自定义控件--带动画的CheckBox

效果图

CheckBox状态:
Checked , UnChecked

动画分析:

1.UnChecked --> Checked , 圆由小变大(简单易实现),然后是对号的动画(后面分析怎么画对号)

2.Checked --> UnChecked , 显示对号消失动画,然后圆由大变小(简单易实现)

---------------------------------------------------------------------------------------------------------

画的对号的位置分析

然后是画对号的动画分析

关键分析在图里,上代码

/*
 * Created by Hanks
 * Copyright (c) 2015 . All rights reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package rxandroid.zyh.com.rxandroid.custom;

import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

/**
 * Created by Administrator on 2015/5/14.
 */
public class TouchCheckBox extends View {

    private Paint mCirclePaint;
    private Paint mCorrectPaint;
    private int radius;                    //圆的半径
    private int width, height;             //控件宽高
    private int cx, cy;                    //圆心xy坐标
    private float[] points = new float[6]; //对号的3个点的坐标
    private float correctProgress;
    private float downY;
    private boolean isChecked;
    private boolean toggle;
    private boolean isAnim;
    private int animDurtion = 150;

    private OnCheckedChangeListener listener;
    private int unCheckColor = Color.GRAY;
    private int circleColor = Color.RED;

    public TouchCheckBox(Context context) {
        this(context, null);
    }

    public TouchCheckBox(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TouchCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);

    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public TouchCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    /**
     * 初始化
     * @param context
     */
    private void init(Context context) {

        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setColor(Color.RED);
        mCirclePaint.setStyle(Paint.Style.FILL);

        mCorrectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCorrectPaint.setColor(Color.WHITE);
        mCorrectPaint.setStyle(Paint.Style.FILL);
        mCorrectPaint.setStrokeWidth(dip2px(context, 2));
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isChecked) {
                    hideCorrect();
                } else {
                    showCheck();
                }
            }
        });
    }

    /**
     * 设置当前选中状态
     * @param checked
     */
    public void setChecked(boolean checked){
        if (isChecked && !checked) {
            hideCorrect();
        } else if(!isChecked && checked) {
            showCheck();
        }
    }

    /**
     * 返回当前选中状态
     * @return
     */
    public boolean isChecked(){
        return isChecked;
    }

    /**
     * 确定尺寸坐标
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        height = width = Math.min(w - getPaddingLeft() - getPaddingRight(),h - getPaddingBottom() - getPaddingTop());
        cx = w / 2;
        cy = h / 2;

        float r = height / 2f;
        points[0] = r / 2f + getPaddingLeft();
        points[1] = r + getPaddingTop();

        points[2] = r * 5f / 6f + getPaddingLeft();
        points[3] = r + r / 3f + getPaddingTop();

        points[4] = r * 1.5f +getPaddingLeft();
        points[5] = r - r / 3f + getPaddingTop();
        radius = (int) (height * 0.125f);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        float f = (radius -height * 0.125f) / (height * 0.5f); //当前进度
        mCirclePaint.setColor(evaluate(f,unCheckColor,circleColor));
        canvas.drawCircle(cx, cy, radius, mCirclePaint); //画圆

        //画对号
        if(correctProgress>0) {
            if(correctProgress<1/3f) {
                float x = points[0] + (points[2] - points[0]) * correctProgress;
                float y = points[1] + (points[3] - points[1]) * correctProgress;
                canvas.drawLine(points[0], points[1], x, y, mCorrectPaint);
            }else {
                float x = points[2] + (points[4] - points[2]) * correctProgress;
                float y = points[3] + (points[5] - points[3]) * correctProgress;
                canvas.drawLine(points[0], points[1], points[2], points[3], mCorrectPaint);
                canvas.drawLine(points[2], points[3], x,y, mCorrectPaint);
            }
        }
    }

    /**
     * 设置圆的颜色
     * @param color
     */
    public void setCircleColor(int color){
        circleColor = color;
    }

    /**
     * 设置对号的颜色
     * @param color
     */
    public void setCorrectColor(int color){
        mCorrectPaint.setColor(color);
    }

    /**
     * 设置未选中时的颜色
     * @param color
     */
    public void setUnCheckColor(int color){
        unCheckColor = color;
    }

    private int evaluate(float fraction, int startValue, int endValue) {
        int startInt = startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return ((startA + (int) (fraction * (endA - startA))) << 24)
                | ((startR + (int) (fraction * (endR - startR))) << 16)
                | ((startG + (int) (fraction * (endG - startG))) << 8)
                | ((startB + (int) (fraction * (endB - startB))));
    }

    /**
     * 处理触摸事件触发动画
     */
    /*private class OnChangeStatusListener implements OnTouchListener {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.i("Touch","Touch");
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float dy = event.getRawY() - downY;
                    if (Math.abs(dy) >= 0) { //滑过一半触发
                        toggle = true;
                    } else {
                        toggle = false;
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    if (toggle) {
                        if (isChecked) {
                            hideCorrect();
                        } else {
                            showCheck();
                        }
                    }
                    break;
            }
            return true;
        }
    }*/

    private void showUnChecked() {
        if (isAnim) {
            return;
        }

        isAnim = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(animDurtion);
        va.setInterpolator(new LinearInterpolator());
        va.start();
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue(); // 0f ~ 1f
                radius = (int) ((1 - value) * height * 0.375f + height * 0.125f);
                if (value >= 1) {
                    isChecked = false;
                    isAnim = false;
                    if(listener!=null){
                        listener.onCheckedChanged(TouchCheckBox.this,false);
                    }
                }
                invalidate();
            }
        });
    }

    private void showCheck() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(animDurtion);
        va.setInterpolator(new LinearInterpolator());
        va.start();
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue(); // 0f ~ 1f
                radius = (int) (value * height * 0.37f + height * 0.125f);
                if (value >= 1) {
                    isChecked = true;
                    isAnim = false;
                    if(listener!=null){
                        listener.onCheckedChanged(TouchCheckBox.this,true);
                    }
                    showCorrect();
                }
                invalidate();
            }
        });
    }

    private void showCorrect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(animDurtion);
        va.setInterpolator(new LinearInterpolator());
        va.start();
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue(); // 0f ~ 1f
                correctProgress = value;
                invalidate();
                if(value>=1){
                    isAnim = false;
                }
            }
        });
    }
    private void hideCorrect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(animDurtion);
        va.setInterpolator(new LinearInterpolator());
        va.start();
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue(); // 0f ~ 1f
                correctProgress = 1-value;
                invalidate();
                if(value>=1){
                    isAnim = false;
                    showUnChecked();
                }
            }
        });
    }
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener){
        this.listener = listener;
    }
    public interface OnCheckedChangeListener{
        void onCheckedChanged(View buttonView, boolean isChecked);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
}

源码:https://github.com/hanks-zyh/AnimateCheckBox

时间: 2024-10-11 15:20:49

自定义控件--带动画的CheckBox的相关文章

Android实现GridView的item长按拖动删除实现(带动画效果)

领导这几天让做一个项目,就是可以实现像支付宝首页一样的可以长按拖动,删除的界面,以前没做过,领导让我做的时候觉得简直是老虎吃天,无从下手啊,可是领导的任务还是要实现的,没办法,就自己网上找咯,但是网上的demo五花八门无法满足我的需求,而且bug还很多,所以最后就自己实现了,说实话,这个效果困扰了我好几个星期,因为其中牵扯的知识点太多了,什么事件分发机制,动画效果,互换位置的算法,还有拖动,这些我都没有接触过,所以只有一点一点来做咯,如果大家还没有了解过这些知识点,我建议搭建先去了解一下,毕竟这

IOS的一个带动画的多项选择的控件(二)

然后我们来写:TypeSelectView 这个比较简单,我们只要只要每个TypeView的位置,然后作为自己的subview就好了 @interface TypeSelectView : UIView @property(nonatomic) BOOL bShown; @property(nonatomic, strong) TypeView* curSelectedView; -(id)initWithFrame:(CGRect)frame searchType:(int)type; @en

matlab中使用正弦波合成方波(带动画)

x=0:0.1:6*pi; for step=1:2:100 s=0; for i=1:2:step s = s+1/i*sin(i*x); end plot(s);set(figure(1),'visible','off'); filename=[num2str(step, '%04d'),'.png']; print(1, '-dpng',filename); end matlab中使用正弦波合成方波(带动画),布布扣,bubuko.com

/*带动画效果的hover*/

1 <!DOCTYPE html> 2 /*带动画效果的hover*/ 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 <style> 8 .ele{ 9 background-color: #dddddd;/*带动画效果的hover*/ 10 } 11 .ele:hover{

07---关于动态创建和销毁带动画的UIVew

在我们做开发的过程中经常会遇到在你触发了某个动作之后,需要动态添加一个带动画的UIView,执行完以后就销毁这个UIView #pragma mark 展示最新微博的数目 - (void)showNewStatusCount:(int)count { // 1.创建按钮 UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.enabled = NO; // 设置按钮禁用 btn.adjustsImageWhenDisa

IOS的一个带动画的多项选择的控件(一)

先上效果图: 这个程序分2个层次,一个是顶部的带UITextField的bar,一个是下拉选择的view,下拉选择的view带有4个自定义的UIView 我们先定义一个UIViewController叫MyViewController,然后顶部的bar叫TopBarView,下拉选择的view叫TypeSelectView,像UIButton的自定义的view叫做TypeView TypeView有两种状态,如果手指触摸到的item就是选中状态,所以TypeSelectView应该有个属性表示当

UITableVIew与UICollectionView带动画删除cell时崩溃的处理

-会崩溃的原因是因为没有处理好数据源与cell之间的协调关系- 效果: tableView的源码: ModelCell.h + ModelCell.m // // ModelCell.h // Set // // Created by YouXianMing on 14/11/24. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import <UIKit/UIKit.h> @class ModelCell; @pro

Android带动画效果的弹窗

在网络加载数据的时候通常需要很多时间,这个时候程序里面经常需要写一个提示正在加载数据的弹窗,这篇文章用两种方式实现带动画效果的Dialog:帧动画实现和GIF动态图实现,它们都能达到动画的效果 第一种.帧动画实现 自定义一个Dialog,先看一下布局文件dialog_animation.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width=

Android开发:带动画的分享效果

这几天做了个带动画的分享页面,现在把它分享出来,如果你觉得有用,请直接使用,避免重复造轮子 先看下效果图 觉得只是看效果图不明显,那么用手机扫描下面的二维码下载安装包: 这个效果本身没有什么特别的难度,都是用Animator实现的,只是动画效果的代码量有点多,因为分享模块一般都会做,把这个分享出来,想要用的话,就不要重复写这么多行代码了 对于熟悉Animator的人,下面的代码可以略过了 我来看下主要的实现代码,完整的代码看底部的项目Github地址 1. 我用了一个方法,实现进入的效果,加上判