一個帶縮放動畫的 RadioButton
效果如圖
How do I use it?
Step 1.Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.qingyc:FixedAnimatedRadioButton:0.1'
}
一.處理低版本RadioButton問題
RadioButton在布局中的使用
<RadioGroup
android:id="@+id/rg_main"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_main_bottom"
android:gravity="bottom"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/main_viewpager">
<com.qingyc.fixedanimatedradiobutton.FixedAnimatedRadioButton
android:id="@+id/rb_capricorn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:checked="true"
android:drawableTop="@drawable/main_rb_01"
android:gravity="center"
android:text="@string/capricorn"
android:textColor="@color/radio_btn_text_color"
android:textSize="12sp"/>
<com.qingyc.fixedanimatedradiobutton.FixedAnimatedRadioButton
android:id="@+id/rb_compatibility"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:checked="false"
android:drawableTop="@drawable/main_rb_02"
android:gravity="center"
android:text="@string/compatibility"
android:textColor="@color/radio_btn_text_color"
android:textSize="12sp"/>
<com.qingyc.fixedanimatedradiobutton.FixedAnimatedRadioButton
android:id="@+id/rb_personality"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:checked="false"
android:drawableTop="@drawable/main_rb_03"
android:gravity="center"
android:text="@string/personality"
android:textColor="@color/radio_btn_text_color"
android:textSize="12sp"/>
<com.qingyc.fixedanimatedradiobutton.FixedAnimatedRadioButton
android:id="@+id/rb_discover"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:checked="false"
android:drawableTop="@drawable/main_rb_04"
android:gravity="center"
android:text="@string/discover"
android:textColor="@color/radio_btn_text_color"
android:textSize="12sp"/>
</RadioGroup>
RadioButton在低版本上的問題(android 4.4)
可以看出在低版本的模擬器中 radioButton左邊顯示了一個默認button圖標,xml中 android:button="@null" 無效
而在高版本的模擬器和手機中 顯示正常
RadioButton的button圖標的設(shè)置是在CompoundButton類中實現(xiàn)的
android Api 28源碼
public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);
final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
if (d != null) {
setButtonDrawable(d);
}
if (a.hasValue(R.styleable.CompoundButton_buttonTintMode)) {
mButtonTintMode = Drawable.parseTintMode(a.getInt(
R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
mHasButtonTintMode = true;
}
if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
mButtonTintList = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
mHasButtonTint = true;
}
final boolean checked = a.getBoolean(
com.android.internal.R.styleable.CompoundButton_checked, false);
setChecked(checked);
mCheckedFromResource = true;
a.recycle();
applyButtonTint();
}
a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); 獲取xml中設(shè)置的button drawable,CompoundButton內(nèi)部私有成員變量 mButtonDrawable 即是
可以看出最終會調(diào)用下面兩個方法
/**
* Sets a drawable as the compound button image given its resource
* identifier.
*
* @param resId the resource identifier of the drawable
* @attr ref android.R.styleable#CompoundButton_button
*/
public void setButtonDrawable(@DrawableRes int resId) {
final Drawable d;
if (resId != 0) {
d = getContext().getDrawable(resId);
} else {
d = null;
}
setButtonDrawable(d);
}
/**
* Sets a drawable as the compound button image.
*
* @param drawable the drawable to set
* @attr ref android.R.styleable#CompoundButton_button
*/
public void setButtonDrawable(@Nullable Drawable drawable) {
if (mButtonDrawable != drawable) {
if (mButtonDrawable != null) {
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable);
}
mButtonDrawable = drawable
if (drawable != null) {
drawable.setCallback(this);
drawable.setLayoutDirection(getLayoutDirection());
if (drawable.isStateful()) {
drawable.setState(getDrawableState());
}
drawable.setVisible(getVisibility() == VISIBLE, false);
setMinHeight(drawable.getIntrinsicHeight());
applyButtonTint();
}
}
}
可以看出不管是在xml 或者代碼中設(shè)置RadioButton的button 最終都調(diào)用 setButtonDrawable(@Nullable Drawable drawable)
對比Android api 19源碼
public void setButtonDrawable(Drawable d) {
if (d != null) {
if (mButtonDrawable != null) {
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable);
}
d.setCallback(this);
d.setVisible(getVisibility() == VISIBLE, false);
mButtonDrawable = d;
setMinHeight(mButtonDrawable.getIntrinsicHeight());
}
refreshDrawableState();
}
可以看出 輸入drawable為null時根本沒有調(diào)用 mButtonDrawable = drawable 把mButtonDrawable置空
問題處理
override fun setButtonDrawable(buttonDrawable: Drawable?) {
// QTIP: 2019-04-28 修復(fù)低版本(android4.4)設(shè)置按鈕為null時顯示默認按鈕
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
val clazz = CompoundButton::class.java
val field = clazz.getDeclaredField("mButtonDrawable")
field.isAccessible = true
field.set(this, null)
} catch (e: Exception) {
e.printStackTrace()
}
} else {
super.setButtonDrawable(buttonDrawable)
}
}
二 實現(xiàn)點擊RadioButton縮放動畫
調(diào)用時機 check狀態(tài)變化時
override fun setChecked(checked: Boolean) {
super.setChecked(checked)
if (width == 0 || height == 0) {
return
}
if (checked) {
val animator = ValueAnimator.ofFloat(minScaleRate, maxScaleRate)
animator.addUpdateListener(this)
animator.duration = 300
animator.start()
} else {
if (animatedScaleRate != 1f) {
val animator = ValueAnimator.ofFloat(animatedScaleRate, 1f)
animator.addUpdateListener(this)
animator.duration = 0
animator.start()
}
}
}
RadioButton選中動畫實現(xiàn)
override fun onAnimationUpdate(animation: ValueAnimator?) {
animatedScaleRate = animation?.animatedValue as Float
try {
//1.保存圖標默認位置
if (mDefaultDrawableBounds == null) {
mDefaultDrawableBounds = arrayOfNulls(4)
compoundDrawables.forEachIndexed { index, drawable ->
drawable?.let {
val rect = Rect(drawable.bounds)
mDefaultDrawableBounds?.set(index, rect)
}
}
}
var leftDrawable: Drawable? = null
var rightDrawable: Drawable? = null
var topDrawable: Drawable? = null
var bottomDrawable: Drawable? = null
//2.獲取radioButton中設(shè)置的圖標的drawable
compoundDrawables.forEachIndexed { index, drawable ->
drawable?.let {
mDefaultDrawableBounds?.get(index)?.let { mDefaultDrawableBounds ->
val copyBounds = Rect(mDefaultDrawableBounds)
//3.動態(tài)縮放RadioButton的圖標
copyBounds.let {
copyBounds.left = mDefaultDrawableBounds.left
copyBounds.right =
mDefaultDrawableBounds.right - (mDefaultDrawableBounds.width() * (1 - animatedScaleRate)).toInt()
copyBounds.top =
mDefaultDrawableBounds.top + (mDefaultDrawableBounds.height() * (1 - animatedScaleRate)).toInt() / 2
copyBounds.bottom =
mDefaultDrawableBounds.bottom - (mDefaultDrawableBounds.height() * (1 - animatedScaleRate)).toInt() / 2
when (index) {
0 -> {
leftDrawable = drawable
leftDrawable?.bounds = copyBounds
}
1 -> {
topDrawable = drawable
topDrawable?.bounds = copyBounds
}
2 -> {
rightDrawable = drawable
rightDrawable?.bounds = copyBounds
}
3 -> {
bottomDrawable = drawable
bottomDrawable?.bounds = copyBounds
}
}
}
}
}
}
//4.更新圖標大小和位置
setCompoundDrawables(leftDrawable, topDrawable, rightDrawable, bottomDrawable)
} catch (e: Exception) {
}
}