A RadioButton with zoom animation
The effect is shown in figure
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'}}}Copy the code
Step 2. Add the dependency
dependencies {
implementation 'com. Making. Qingyc: FixedAnimatedRadioButton: 0.1'
}
Copy the code
1. Dealing with the problem of the lower version of RadioButton
Use of RadioButton in layout
<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>
Copy the code
RadioButton issues on earlier versions (Android 4.4)
You can see that on the lower emulator a default button icon is displayed to the left of radioButton. Android: Button =”@null” is invalid in XML
It works fine on older emulators and phones
The Settings for RadioButton’s Button icon are implemented in the CompoundButton class
Android Api 28 source code
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();
}
Copy the code
a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); Gets the button drawable set in XML, which is the mButtonDrawable private member inside CompoundButton
You can see that the following two methods end up being called
/**
* 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 = drawableif(drawable ! = null) { drawable.setCallback(this); drawable.setLayoutDirection(getLayoutDirection());if (drawable.isStateful()) {
drawable.setState(getDrawableState());
}
drawable.setVisible(getVisibility() == VISIBLE, false);
setMinHeight(drawable.getIntrinsicHeight()); applyButtonTint(); }}}Copy the code
So you can see that whatever RadioButton is set up in XML or code ends up calling setButtonDrawable(@nullable Drawable Drawable)
Compare the Android API 19 source code
Click to view
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();
}
Copy the code
You can see that mButtonDrawable = drawable is not called at all when drawable is null and mButtonDrawable is null
Problem processing
override fun setButtonDrawable(buttonDrawable: Drawable?) {// QTIP: 2019-04-28 fix for lower versions (android4.4) setting button to null to display default buttonif (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)
}
}
Copy the code
Click RadioButton to zoom animation
Call Time When the check status changes
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() } } }Copy the code
RadioButton selected animation implementation
override fun onAnimationUpdate(animation: ValueAnimator?) { animatedScaleRate = animation? .animatedValue as Float try { //1. Save the default icon locationif(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. Get the Settings icon in the radioButton drawable compoundDrawables. ForEachIndexed {index, drawable - > drawable? .let { mDefaultDrawableBounds? .get(index)? .let { mDefaultDrawableBounds -> val copyBounds = Rect(mDefaultDrawableBounds) //3. Dynamic scaling RadioButton icon 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. Update icon size and positionsetCompoundDrawables(leftDrawable, topDrawable, rightDrawable, bottomDrawable)
} catch (e: Exception) {
}
}
Copy the code
Making the source code