使用PathMeasure实现 动画CheckBox
来源:互联网 发布:红蜘蛛软件 路由器 编辑:程序博客网 时间:2024/06/05 03:05
先看效果:(最上面那个颜色录像有点问题了)
看见支付宝的支付结果 那个效果还不错,想到那实现一个类似的checkBox吧。
这个view有点击事件,选中监听,快速点击动画流畅,支持wrapcontent,数据持久。
主角是PathMeasure中的getSegment(...):
public boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
Given a start and stop distance, return in dst the intervening segment(s). If the segment is zero-length, return false, else return true. startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD then return false (and leave dst untouched). Begin the segment with a moveTo if startWithMoveTo is true.
On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas. A simple workaround is to add a single operation to this path, such as dst.rLineTo(0, 0).
简单理解就是,从原有的path中截取出来其中一段path。
一.使用到的知识点:
1.属性动画,AnimatorSet
2.path,pathMeasure,pathMeasure.getSegment(...);
3.paint ,shader
二.聊聊知识点中注意的地方:
1.属性动画,AnimatorSet:
2.path,pathMeasure:
3.paint:
4.事件监听:
5.要数据持久处理
6.动画即时结束
三.思路
四:源码
package com.dup.bitmapdemo.view;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PathMeasure;import android.graphics.SweepGradient;import android.os.Bundle;import android.os.Parcelable;import android.util.AttributeSet;import android.view.View;import android.view.animation.AccelerateDecelerateInterpolator;/** * Created by dup on 16-8-2. */public class PathCheckBoxView extends View implements View.OnClickListener { private static final String INSTANCE_STATUE = "status"; private static final String INSTANCE_CHECK = "check"; private static final String INSTANCE_ENABLE = "enable"; private Context context; private static int duration = 300; //wrapcontent时默认大小:dp private int defaultSize = 40; private int viewSize = defaultSize; //背景灰色线条paint private Paint mBackPathPaint; //前线条paint private Paint mDstPaint; //背景 圆 private Path mCirclePath; //前景 圆 private Path mCircleDst; //背景 对号 private Path mCheckPath; //前景 对号 private Path mCheckDst; //圆圈 measure private PathMeasure measureCircle; //对号 measure private PathMeasure measureCheck; //圆圈长度 private float lengthCircle; //对号长度 private float lengthCheck; //圆圈动画进度 private float mPercentCircle = 0; //对号进度 private float mPercentCheck = 0; //选中动画 private AnimatorSet asCheck; //取消选中动画 private AnimatorSet asUnCheck; //选中动画---圆环动画 private ValueAnimator vaCheckCheck; // ---对号动画 private ValueAnimator vaCircleCheck; private ValueAnimator vaCircleUnCheck; private ValueAnimator vaCheckUnCheck; private boolean isEnabled = true; public boolean isEnabled() { return isEnabled; } public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } /** * 标志位 */ private boolean isChecked = false; public boolean isChecked() { return isChecked; } public void setChecked(boolean is) { if (!isEnabled) { return; } isChecked = is; if (mCheckChangedListener != null) { mCheckChangedListener.onCheckedChanged(this, isChecked); } if (isChecked) { startCheckAnim(); } else { startUnCheckAnim(); } } public void toggle() { if(isEnabled()){ isChecked = !isChecked; setChecked(isChecked); } } public PathCheckBoxView(Context context) { this(context, null); } public PathCheckBoxView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PathCheckBoxView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; this.setOnClickListener(this); setClickable(true); } @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(INSTANCE_STATUE, super.onSaveInstanceState()); bundle.putBoolean(INSTANCE_CHECK, isChecked); bundle.putBoolean(INSTANCE_ENABLE, isEnabled); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; setEnabled(bundle.getBoolean(INSTANCE_ENABLE, true)); setChecked(bundle.getBoolean(INSTANCE_CHECK, false)); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUE)); } else { super.onRestoreInstanceState(state); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(asCheck!=null){ asCheck.cancel(); } if(asUnCheck!=null){ asUnCheck.cancel(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //当为wrapcontent时,将大小设置为默认大小。 if(MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.AT_MOST||MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST){ float scale = context.getResources().getDisplayMetrics().density; int sizePx = (int) (defaultSize * scale + 0.5f); setMeasuredDimension( MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.AT_MOST?sizePx:MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.AT_MOST?sizePx:MeasureSpec.getSize(heightMeasureSpec) ); }else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { initData(left, top, right, bottom); } } /** * 初始化具体数据 * * @param left * @param top * @param right * @param bottom */ private void initData(int left, int top, int right, int bottom) { //初始化画笔宽度 int paintWidth = right - left - getPaddingLeft() - getPaddingRight(); int paintHeight = bottom - top - getPaddingTop() - getPaddingBottom(); int paintStroke = Math.min(paintWidth, paintHeight) / 10;//环宽度设为10分之一 //重设padding,加上圆环的宽度,因为不设置,圆环一半会在view外 setPadding(getPaddingLeft() + paintStroke / 2, getPaddingTop() + paintStroke / 2, getPaddingRight() + paintStroke / 2, getPaddingRight() + paintStroke / 2); //获取到圆环直径 viewSize = Math.min(right - left - getPaddingLeft() - getPaddingRight(), bottom - top - getPaddingTop() - getPaddingBottom()); //初始化背景 线 画笔 mBackPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBackPathPaint.setColor(Color.LTGRAY); mBackPathPaint.setStyle(Paint.Style.STROKE); mBackPathPaint.setStrokeWidth(paintStroke); mBackPathPaint.setStrokeCap(Paint.Cap.ROUND); mBackPathPaint.setStrokeJoin(Paint.Join.ROUND); //初始化前景 线 画笔 mDstPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDstPaint.setStyle(Paint.Style.STROKE); mDstPaint.setStrokeWidth(paintStroke); mDstPaint.setColor(Color.BLACK); mDstPaint.setStrokeCap(Paint.Cap.ROUND); mDstPaint.setStrokeJoin(Paint.Join.ROUND); //圆环路径 mCirclePath = new Path(); mCircleDst = new Path(); //对号路径 mCheckPath = new Path(); mCheckDst = new Path(); //pathmeasure measureCheck = new PathMeasure(); measureCircle = new PathMeasure(); //初始化背景线 路径.这里Direction可以决定动画时圆环的旋转方向 mCirclePath.addCircle(viewSize / 2 + getPaddingLeft(), viewSize / 2 + getPaddingTop(), viewSize / 2, Path.Direction.CCW); //初始化对号背景线 路径(根据百分比画对号) float[] floats = new float[2]; floats[0] = getPaddingLeft() + viewSize / 6; floats[1] = viewSize / 2 + getPaddingTop(); mCheckPath.moveTo(floats[0], floats[1]); mCheckPath.lineTo(floats[0] + viewSize / 4, floats[1] + viewSize / 4); mCheckPath.lineTo(floats[0] + 4 * viewSize / 6, floats[1] - viewSize / 7); //初始化Circle的measure measureCircle.setPath(mCirclePath, false); lengthCircle = measureCircle.getLength(); //初始化对号的measure measureCheck.setPath(mCheckPath, false); measureCheck.nextContour() lengthCheck = measureCheck.getLength(); //设置线条渐变 mDstPaint.setShader( mDstPaint.setShader(new SweepGradient(viewSize / 2 + getPaddingLeft(), viewSize / 2 + getPaddingTop(), new int[]{Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.RED} , new float[]{0.1f, 0.3f, 0.5f, 0.7f, 0.9f} ))); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //画背景圆环 canvas.drawPath(mCirclePath, mBackPathPaint); //画背景对号 canvas.drawPath(mCheckPath, mBackPathPaint); mCircleDst.reset(); mCircleDst.lineTo(0, 0); mCheckDst.reset(); mCheckDst.lineTo(0, 0); //主角:根据动画进度和pathmeasure测量出的总长度,获取到当前应有长度,从0开始到应有长度. measureCircle.getSegment(0, lengthCircle * mPercentCircle, mCircleDst, true); measureCheck.getSegment(0, lengthCheck * mPercentCheck, mCheckDst, true); canvas.drawPath(mCheckDst, mDstPaint); canvas.drawPath(mCircleDst, mDstPaint); } /** * 开始未选中动画 */ private void startUnCheckAnim() { //圆圈不选中动画,这里选用mPercentCircle作为动画起始值,是为了快速勾选时动画不闪 vaCircleUnCheck = ObjectAnimator.ofFloat(mPercentCircle, 0); vaCircleUnCheck.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPercentCircle = (float) animation.getAnimatedValue(); invalidate(); } }); vaCircleUnCheck.setDuration(duration); //对号不选中动画 vaCheckUnCheck = ObjectAnimator.ofFloat(mPercentCheck, 0); vaCheckUnCheck.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPercentCheck = (float) animation.getAnimatedValue(); invalidate(); } }); vaCheckUnCheck.setDuration(duration); asUnCheck = new AnimatorSet(); //这里顺序执行anim,保证先执行对号动画。 asUnCheck.playSequentially(vaCheckUnCheck, vaCircleUnCheck); asUnCheck.setInterpolator(new AccelerateDecelerateInterpolator()); if (asCheck != null && asCheck.isStarted()) { asCheck.cancel(); } asUnCheck.start(); } /** * 开始选中动画 */ private void startCheckAnim() { //圆圈选中动画,这里选用mPercentCircle作为动画起始值,是为了快速勾选时动画不闪. vaCircleCheck = ObjectAnimator.ofFloat(mPercentCircle, 1); vaCircleCheck.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPercentCircle = (float) animation.getAnimatedValue(); invalidate(); } }); vaCircleCheck.setDuration(duration); //对号选中动画 vaCheckCheck = ObjectAnimator.ofFloat(mPercentCheck, 1); vaCheckCheck.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPercentCheck = (float) animation.getAnimatedValue(); invalidate(); } }); vaCheckCheck.setDuration(duration); asCheck = new AnimatorSet(); asCheck.setInterpolator(new AccelerateDecelerateInterpolator()); asCheck.playSequentially(vaCircleCheck, vaCheckCheck); if (asUnCheck != null && asUnCheck.isStarted()) { asUnCheck.cancel(); } asCheck.start(); } @Override public void onClick(View v) { if (mClickListener != null) { mClickListener.onClick(v); } toggle(); } private OnClickListenerEx mClickListener; /** * 点击事件监听 * * @param mListener */ public void setOnClickListenerEx(OnClickListenerEx mListener) { this.mClickListener = mListener; } public interface OnClickListenerEx { void onClick(View v); } private OnCheckedChangeListener mCheckChangedListener; public void setOnCheckedChangeListener(OnCheckedChangeListener mListener) { this.mCheckChangedListener = mListener; } public interface OnCheckedChangeListener { void onCheckedChanged(View view, boolean isChecked); }}注意的地方就是使用此view的点击事件需要使用OnClickListenerEx。至于彩虹色,可以修改initData()最后的shader。使用和CheckBox基本没有什么区别(貌似这个view中监听事件不能跟ButterKnife好好相处。。。哈)
- 使用PathMeasure实现 动画CheckBox
- Android使用PathMeasure实现加载动画
- 使用PathMeasure制作Loading动画
- 安卓开发之使用PathMeasure自定义加载动画控件
- Android:使用PathMeasure绘制动画效果的搜索按钮
- PathMeasure 轨迹动画神器
- Android PathMeasure使用
- PathMeasure的基本使用
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure 仿支付宝支付动画
- Android PathMeasure的使用详解
- Android 中PathMeasure的使用
- PathMeasure + 贝塞尔曲线实现过山车效果
- 使用PathMeasure简单模仿系统ProgressBar
- hdu1875 畅通工程再续--prim
- iOS面试系列2
- SmartFoxServer 2X 简介
- RSA加密算法浅谈
- SSH+activity之Activity软件环境搭建
- 使用PathMeasure实现 动画CheckBox
- 多线程自动执行类中的所有函数
- 解决libc.so.6: version `GLIBC_2.14' not found问题
- 一条线显示两种颜色的方法汇总
- eclipse使用JDBC连接mysql数据库
- mysql事务和锁InnoDB
- iOS面试系列3
- DNS预解析(dns-prefetch)
- 关于spring+hibernate的FlushMode的记录