Skip to content

Instantly share code, notes, and snippets.

@cesco89
Last active February 9, 2023 05:09
Show Gist options
  • Save cesco89/9144189 to your computer and use it in GitHub Desktop.
Save cesco89/9144189 to your computer and use it in GitHub Desktop.
A custom animated circle checkbox based on markushi's CircleButton (https://github.com/markushi/android-circlebutton)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleAnimatedCheckBox">
<attr name="cb_color" format="color" />
<attr name="cb_pressed_ring_width" format="dimension" />
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/btn_radio_on_pressed_holo_light" android:state_checked="true"/>
<item android:drawable="@drawable/btn_radio_off_pressed_holo_light" android:state_checked="false"/>
</selector>
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.CheckBox;
import android.widget.ImageView;
public class CircleAnimatedCheckBox extends CheckBox {
private static final int PRESSED_COLOR_LIGHTUP = 255 / 25;
private static final int PRESSED_RING_ALPHA = 75;
private static final int DEFAULT_PRESSED_RING_WIDTH_DIP = 4;
private static final int ANIMATION_TIME_ID = android.R.integer.config_shortAnimTime;
private int centerY;
private int centerX;
private int outerRadius;
private int pressedRingRadius;
private Paint circlePaint;
private Paint focusPaint;
private float animationProgress;
private int pressedRingWidth;
private int defaultColor = Color.BLACK;
private int pressedColor;
private ObjectAnimator pressedAnimator;
public CircleAnimatedCheckBox(Context context) {
super(context);
init(context, null);
}
public CircleAnimatedCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleAnimatedCheckBox(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
@Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
if (circlePaint != null) {
circlePaint.setColor(pressed ? pressedColor : defaultColor);
}
if (pressed) {
showPressedRing();
} else {
hidePressedRing();
}
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(centerX, centerY, pressedRingRadius + animationProgress, focusPaint);
//canvas.drawCircle(centerX, centerY, outerRadius - pressedRingWidth, circlePaint);
super.onDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w/2;
centerY = h/2;
outerRadius = Math.min(w, h) / 2;
pressedRingRadius = outerRadius - pressedRingWidth - pressedRingWidth / 2;
}
public float getAnimationProgress() {
return animationProgress;
}
public void setAnimationProgress(float animationProgress) {
this.animationProgress = animationProgress;
this.invalidate();
}
public void setColor(int color) {
this.defaultColor = color;
this.pressedColor = getHighlightColor(color, PRESSED_COLOR_LIGHTUP);
circlePaint.setColor(defaultColor);
circlePaint.setAlpha(PRESSED_RING_ALPHA);
focusPaint.setColor(defaultColor);
//focusPaint.setAlpha(PRESSED_RING_ALPHA);
this.invalidate();
}
private void hidePressedRing() {
pressedAnimator.setFloatValues(pressedRingWidth, 0f);
pressedAnimator.start();
}
private void showPressedRing() {
pressedAnimator.setFloatValues(animationProgress, pressedRingWidth);
pressedAnimator.start();
}
private void init(Context context, AttributeSet attrs) {
//this.setFocusable(true);
//this.setScaleType(ScaleType.CENTER_INSIDE);
//setClickable(true);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setStyle(Paint.Style.FILL);
focusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
focusPaint.setStyle(Paint.Style.STROKE);
pressedRingWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_PRESSED_RING_WIDTH_DIP, getResources()
.getDisplayMetrics());
int color = Color.BLACK;
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleAnimatedCheckBox);
color = a.getColor(R.styleable.CircleAnimatedCheckBox_cb_color, color);
pressedRingWidth = (int) a.getDimension(R.styleable.CircleAnimatedCheckBox_cb_pressed_ring_width, pressedRingWidth);
a.recycle();
}
setColor(color);
focusPaint.setStrokeWidth(pressedRingWidth);
final int pressedAnimationTime = getResources().getInteger(ANIMATION_TIME_ID);
pressedAnimator = ObjectAnimator.ofFloat(this, "animationProgress", 0f, 0f);
pressedAnimator.setDuration(pressedAnimationTime);
}
private int getHighlightColor(int color, int amount) {
return Color.argb(Math.min(255, Color.alpha(color)), Math.min(255, Color.red(color) + amount),
Math.min(255, Color.green(color) + amount), Math.min(255, Color.blue(color) + amount));
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingRight="?android:attr/scrollbarSize" >
<com.example.package.CircleAnimatedCheckBox
android:id="@+id/cb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:button="@drawable/checkbox_selector_light"
android:focusable="false"
android:scaleX="1.20"
android:scaleY="1.20"
app:cb_color="#CC33b5e5"
app:cb_pressed_ring_width="2dp"/>
</LinearLayout>
@iamnaran
Copy link

iamnaran commented May 9, 2018

Great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment