Back to project page Material.
The source code is released under:
Apache License
If you think the Android project Material listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.rey.material.drawable; /* ww w . ja v a2 s .com*/ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.SystemClock; import android.util.AttributeSet; import com.rey.material.R; import com.rey.material.util.ColorUtil; import com.rey.material.util.ThemeUtil; import com.rey.material.util.ViewUtil; public class RadioButtonDrawable extends Drawable implements Animatable { private boolean mRunning = false; private Paint mPaint; private long mStartTime; private float mAnimProgress; private int mAnimDuration; private int mStrokeSize; private int mWidth; private int mHeight; private int mRadius; private int mInnerRadius; private int mPrevColor; private int mCurColor; private ColorStateList mStrokeColor; private boolean mChecked = false; private boolean mInEditMode = false; private boolean mAnimEnable = true; private RadioButtonDrawable(int width, int height, int strokeSize, ColorStateList strokeColor, int radius, int innerRadius, int animDuration){ mAnimDuration = animDuration; mStrokeSize = strokeSize; mWidth = width; mHeight = height; mRadius = radius; mInnerRadius = innerRadius; mStrokeColor = strokeColor; mPaint = new Paint(); mPaint.setAntiAlias(true); } public void setInEditMode(boolean b){ mInEditMode = b; } public void setAnimEnable(boolean b){ mAnimEnable = b; } public boolean isAnimEnable(){ return mAnimEnable; } @Override public int getIntrinsicWidth() { return mWidth; } @Override public int getIntrinsicHeight() { return mHeight; } @Override public int getMinimumWidth() { return mWidth; } @Override public int getMinimumHeight() { return mHeight; } @Override public boolean isStateful() { return true; } @Override public void draw(Canvas canvas) { if(mChecked) drawChecked(canvas); else drawUnchecked(canvas); } private void drawChecked(Canvas canvas){ float cx = getBounds().exactCenterX(); float cy = getBounds().exactCenterY(); if(isRunning()){ float halfStrokeSize = mStrokeSize / 2f; float inTime = (mRadius - halfStrokeSize) / (mRadius - halfStrokeSize + mRadius - mStrokeSize - mInnerRadius); if(mAnimProgress < inTime){ float inProgress = mAnimProgress / inTime; float outerRadius = mRadius + halfStrokeSize * (1f - inProgress); float innerRadius = (mRadius - halfStrokeSize) * (1f - inProgress); mPaint.setColor(ColorUtil.getMiddleColor(mPrevColor, mCurColor, inProgress)); mPaint.setStrokeWidth(outerRadius - innerRadius); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, (outerRadius + innerRadius) / 2, mPaint); } else{ float outProgress = (mAnimProgress - inTime) / (1f - inTime); float innerRadius = (mRadius - mStrokeSize) * (1 - outProgress) + mInnerRadius * outProgress; mPaint.setColor(mCurColor); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(cx, cy, innerRadius, mPaint); float outerRadius = mRadius + halfStrokeSize * outProgress; mPaint.setStrokeWidth(mStrokeSize); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, outerRadius - halfStrokeSize, mPaint); } } else{ mPaint.setColor(mCurColor); mPaint.setStrokeWidth(mStrokeSize); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, mRadius, mPaint); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(cx, cy, mInnerRadius, mPaint); } } private void drawUnchecked(Canvas canvas){ float cx = getBounds().exactCenterX(); float cy = getBounds().exactCenterY(); if(isRunning()){ float halfStrokeSize = mStrokeSize / 2f; float inTime = (mRadius - mStrokeSize - mInnerRadius) / (mRadius - halfStrokeSize + mRadius - mStrokeSize - mInnerRadius); if(mAnimProgress < inTime){ float inProgress = mAnimProgress / inTime; float innerRadius = (mRadius - mStrokeSize) * inProgress + mInnerRadius * (1f - inProgress); mPaint.setColor(ColorUtil.getMiddleColor(mPrevColor, mCurColor, inProgress)); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(cx, cy, innerRadius, mPaint); float outerRadius = mRadius + halfStrokeSize * (1f - inProgress); mPaint.setStrokeWidth(mStrokeSize); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, outerRadius - halfStrokeSize, mPaint); } else{ float outProgress = (mAnimProgress - inTime) / (1f - inTime); float outerRadius = mRadius + halfStrokeSize * outProgress; float innerRadius = (mRadius - halfStrokeSize) * outProgress; mPaint.setColor(mCurColor); mPaint.setStrokeWidth(outerRadius - innerRadius); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, (outerRadius + innerRadius) / 2, mPaint); } } else{ mPaint.setColor(mCurColor); mPaint.setStrokeWidth(mStrokeSize); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx, cy, mRadius, mPaint); } } @Override protected boolean onStateChange(int[] state) { boolean checked = ViewUtil.hasState(state, android.R.attr.state_checked); int color = mStrokeColor.getColorForState(state, mCurColor); boolean needRedraw = false; if(mChecked != checked){ mChecked = checked; needRedraw = true; if(!mInEditMode && mAnimEnable) start(); } if(mCurColor != color){ mPrevColor = isRunning() ? mCurColor : color; mCurColor = color; needRedraw = true; } else if(!isRunning()) mPrevColor = color; return needRedraw; } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } //Animation: based on http://cyrilmottier.com/2012/11/27/actionbar-on-the-move/ private void resetAnimation(){ mStartTime = SystemClock.uptimeMillis(); mAnimProgress = 0f; } @Override public void start() { resetAnimation(); scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } @Override public void stop() { mRunning = false; unscheduleSelf(mUpdater); invalidateSelf(); } @Override public boolean isRunning() { return mRunning; } @Override public void scheduleSelf(Runnable what, long when) { mRunning = true; super.scheduleSelf(what, when); } private final Runnable mUpdater = new Runnable() { @Override public void run() { update(); } }; private void update(){ long curTime = SystemClock.uptimeMillis(); mAnimProgress = Math.min(1f, (float)(curTime - mStartTime) / mAnimDuration); if(mAnimProgress == 1f) mRunning = false; if(isRunning()) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } public static class Builder{ private int mAnimDuration = 400; private int mStrokeSize = 4; private int mWidth = 64; private int mHeight = 64; private int mRadius = 18; private int mInnerRadius = 10; private ColorStateList mStrokeColor; public Builder(){} public Builder(Context context, AttributeSet attrs, int defStyle){ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RadioButtonDrawable, 0, defStyle); width(a.getDimensionPixelSize(R.styleable.RadioButtonDrawable_rbd_width, ThemeUtil.dpToPx(context, 32))); height(a.getDimensionPixelSize(R.styleable.RadioButtonDrawable_rbd_height, ThemeUtil.dpToPx(context, 32))); strokeSize(a.getDimensionPixelSize(R.styleable.RadioButtonDrawable_rbd_strokeSize, ThemeUtil.dpToPx(context, 2))); radius(a.getDimensionPixelSize(R.styleable.RadioButtonDrawable_rbd_radius, ThemeUtil.dpToPx(context, 10))); innerRadius(a.getDimensionPixelSize(R.styleable.RadioButtonDrawable_rbd_innerRadius, ThemeUtil.dpToPx(context, 5))); strokeColor(a.getColorStateList(R.styleable.RadioButtonDrawable_rbd_strokeColor)); animDuration(a.getInt(R.styleable.RadioButtonDrawable_rbd_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime))); a.recycle(); if(mStrokeColor == null){ int[][] states = new int[][]{ new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_checked}, }; int[] colors = new int[]{ ThemeUtil.colorControlNormal(context, 0xFF000000), ThemeUtil.colorControlActivated(context, 0xFF000000), }; strokeColor(new ColorStateList(states, colors)); } } public RadioButtonDrawable build(){ if(mStrokeColor == null) mStrokeColor = ColorStateList.valueOf(0xFF000000); return new RadioButtonDrawable(mWidth, mHeight, mStrokeSize, mStrokeColor, mRadius, mInnerRadius, mAnimDuration); } public Builder width(int width){ mWidth = width; return this; } public Builder height(int height){ mHeight = height; return this; } public Builder strokeSize(int size){ mStrokeSize = size; return this; } public Builder strokeColor(int color){ mStrokeColor = ColorStateList.valueOf(color); return this; } public Builder strokeColor(ColorStateList color){ mStrokeColor = color; return this; } public Builder radius(int radius){ mRadius = radius; return this; } public Builder innerRadius(int radius){ mInnerRadius = radius; return this; } public Builder animDuration(int duration){ mAnimDuration = duration; return this; } } }