自定义view之一个简单的包含开始停止两种状态的动画(仿uber取消订单动画)

还是老规矩先看看动画效果

car.gif

分析状态值及逻辑事件:0、初始状态1、点击实现旋转动画 2、长按倒计时动画(类似于uber取消订单的效果)

实现步骤

1、来分析下我们自定义view需要的属性

1、内圆的半径round_radius
2、外圆环的宽度ring_width
3、内圆与外圆环之间的间距space_inner_out
4、未点击时刚开始的状态下的颜色start_round_color(我们这个动画其实只用到了两周颜色,所以就定为开始的颜色和过程中的颜色)
5、点击开始动画的颜色centre_round_color
6、文字的大小text_size
7、文字的颜色text_color

2、定义view的属性,创建一个名字叫MyProgressView的类继承自view,这里提前创建它,只是为了我们在创建attrs资源文件自定义view属性的时方便命名

<resources>
    <declare-styleable name="MyProgressView">
        <attr name="space_inner_out" format="integer"/>
        <attr name="round_radius" format="integer"/>
        <attr name="ring_width" format="integer"/>
        <attr name="text_size" format="integer"/>
        <attr name="text_color" format="color"/>
        <attr name="start_round_color" format="color"/>
        <attr name="centre_round_color" format="color"/>
    </declare-styleable>
</resources>

3、我们就该来实现自定义view的功能了

首先获取自定义属性


    private void init(Context context, AttributeSet attrs) {
        //获取自定义属性值
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);
        screenWidth = ScreenUtils.getScreenWidth(context);
        round_radius = a.getInt(R.styleable.MyProgressView_round_radius, screenWidth / 14);//默认大小为屏幕宽度的6分之一
        ring_width = a.getInt(R.styleable.MyProgressView_ring_width, 16);//默认外圆的宽度为20
        space_inner_out = a.getInt(R.styleable.MyProgressView_space_inner_out, 30);
        //获取文字的大小,默认为30
        text_size = a.getInt(R.styleable.MyProgressView_text_size, 30);
        text_color = a.getColor(R.styleable.MyProgressView_text_color, Color.WHITE);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        start_round_color = a.getColor(R.styleable.MyProgressView_start_round_color,Color.BLUE);
        centre_round_color = a.getColor(R.styleable.MyProgressView_centre_round_color,Color.RED);
        calculate();
    }

    /**
     * 计算
     */
    public void calculate() {
        //计算view的大小
        viewSizw = round_radius * 2 + ring_width * 2 + space_inner_out * 2;
        //获取bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.yuanhuan);
        progress_bitmap = Bitmap.createScaledBitmap(bitmap, viewSizw, viewSizw, true);
        //绘制旋转动画用到的matrix
        matrix = new Matrix();
    }

添加一个设置stute的方法

/**
     * 设置状态值
     * @param stute
     */
    public void setStute(int stute) {
        this.stute = stute;
        if (stute == 1) {
            if (animator_stute2 != null) {//当前是stute=2向stute=1转时,结束上一个动画
                animator_stute2.cancel();
            }
            startAnimator();
        } else if (stute == 2) {
            if (animator_stute1 != null)//当前是stute=1向stute=2转时,结束上一个动画
                animator_stute1.cancel();
            startCountDown();
        }
    }

重写on Measure()

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(viewSizw, viewSizw);//设置view的大小是上面计算的
        //圆环的位置
        ring_rect = new RectF(0+ring_width/2, 0+ring_width/2,getWidth()-ring_width/2,getHeight()-ring_width/2);
        width = getWidth();
        height = getHeight();
        //圆心
        cx = width / 2;
        cy = height / 2;
    }

绘制

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (stute) {
            case 0://开始
                //绘制内圆
                mPaint.setColor(start_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
                //绘制外圆环
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(ring_width);
                canvas.drawCircle(cx, cy, width / 2 - ring_width / 2, mPaint);
                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v = mPaint.measureText(START_TEXT);
                canvas.drawText(START_TEXT, width / 2 - v / 2, height / 2 + text_size / 2, mPaint);
                break;
            case 1:
                //绘制内圆
                mPaint.setColor(centre_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆

                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v1 = mPaint.measureText(CENTRE_TEXT);
                canvas.drawText(CENTRE_TEXT, width / 2 - v1 / 2, height / 2 + text_size / 2, mPaint);
                //绘制外圆环
                canvas.drawBitmap(progress_bitmap, matrix, mPaint);
                break;
            case 2:
                //绘制内圆
                mPaint.setColor(centre_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
                //绘制外圆环
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(ring_width);
                canvas.drawArc(ring_rect, 270, index, false, mPaint);
                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v2 = mPaint.measureText(CENTRE_TEXT);
                canvas.drawText(CENTRE_TEXT, width / 2 - v2 / 2, height / 2 + text_size / 2, mPaint);
                break;
        }
    }

还有两个动画的实现

 /**
     * 开启动画
     */
    private void startAnimator() {
        animator_stute1 = ValueAnimator.ofInt(360);
        animator_stute1.setDuration(2000);
        animator_stute1.setInterpolator(new LinearInterpolator());
        animator_stute1.setRepeatCount(ValueAnimator.INFINITE);
        animator_stute1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //这里使用来实现旋转动画
                matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);
                postInvalidate();
            }
        });
        animator_stute1.start();
    }

    /**
     * 停止的动画
     */
    private void startCountDown() {
        index=360;//将index设置为初始值
        animator_stute2 = ValueAnimator.ofInt(360);
        animator_stute2.setDuration(TIME_FOR_SOTP);
        animator_stute2.setInterpolator(new LinearInterpolator());
        animator_stute2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                index -= S;
                if (index <= 0) {
                    stute = 0;//设置为初始状态
                    index = 360;//将index设置为初始状态
                    is_stop = true;//停止动画结束
                }
                postInvalidate();
            }
        });
        animator_stute2.start();
    }
旋转动画实现的主要方法

matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);//这里这是旋转的角度DEGREES,旋转中心就是图片的中心
这里就是绘制旋转后的图片
//绘制外圆环 canvas.drawBitmap(progress_bitmap, matrix, mPaint);

在mainactivity中的使用

@Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       final MyProgressView m = (MyProgressView) findViewById(R.id.mp);
       m.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               if (m.getStute()==1){//如果当前已经是开始状态,则return
                   return;
               }
               m.setStute(1);
           }
       });
       m.setOnLongClickListener(new View.OnLongClickListener() {
           @Override
           public boolean onLongClick(View view) {
               if (m.getStute()==1){//如果当前是开始状态才触发停止动画
                   m.setStute(2);
               }
               return false;
           }
       });
   }

当然还要重写view的onTouchEvent()方法

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP && !is_stop && stute == 2) {
            setStute(1);//没有结束停止动画,就返回出车动画
            index=360;
        } else if (event.getAction() == MotionEvent.ACTION_UP && is_stop && stute == 0) {
            is_stop = false;//当前停止动画已经结束,则将is_stop设置为初始值
            return true;
        }
        return super.onTouchEvent(event);
    }

xml中的布局

  <com.dituwuyou.myapplication.MyProgressView
        android:id="@+id/mp"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:text_color="@android:color/white"
        app:text_size="30"
        app:space_inner_out="30"
        app:round_radius="150"
        app:ring_width="20"
        app:centre_round_color="@color/color_ff4631"
        app:start_round_color="@color/color_3F51B5"/>

接下来我贴出整个自定义view的代码


/**
 * Created by Laer on 2016/10/24.
 */
public class MyProgressView extends View {
    private int space_inner_out;//外圆环和内圆的间距(不提供大小设置,代码计算)
    private int round_radius;//内圆的半径
    private int ring_width;//外圆环的宽度
    private int text_size;//文字的大小
    private int text_color;//文字的颜色
    private int centre_round_color;//开始动画的内圆颜色值
    private int start_round_color;//没开始的内圆颜色值

    //常量
    private static final String START_TEXT = "开始";
    private static final String CENTRE_TEXT = "停止";
    private static final int S = 3;//减少时的速率
    private static  final int DEGREES =2;//旋转动画的速率
    private static final int TIME_FOR_SOTP = 3000;//长按停止出车的时间,这里默认是3秒
    //画笔
    private Paint mPaint;
    private RectF ring_rect;
    //标识
    private boolean is_stop;//是否停止出车
    private boolean is_start_animotor;//是否开启动画
    //
    private int screenWidth;
    private int viewSizw;//当前view的大小
    private Bitmap progress_bitmap;
    private Matrix matrix;
    private int index = 360;//当前倒计时的进度值
    private int stute = 0;//当前的状态值
    private ValueAnimator animator_stute1;
    private ValueAnimator animator_stute2;
    private int width;//当前view的宽度
    private int height;//当前view的高度
    private int cx;//圆心的x坐标
    private int cy;//圆心的y坐标

    public MyProgressView(Context context) {
        super(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    }
    public MyProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * 获取当前状态值
     * @return
     */
    public int getStute() {
        return stute;
    }

    /**
     * 设置状态值
     * @param stute
     */
    public void setStute(int stute) {
        this.stute = stute;
        if (stute == 1) {
            if (animator_stute2 != null) {//当前是stute=2向stute=1转时,结束上一个动画
                animator_stute2.cancel();
            }
            startAnimator();
        } else if (stute == 2) {
            if (animator_stute1 != null)//当前是stute=1向stute=2转时,结束上一个动画
                animator_stute1.cancel();
            startCountDown();
        }
    }

    /**
     * 开启动画
     */
    private void startAnimator() {
        animator_stute1 = ValueAnimator.ofInt(360);
        animator_stute1.setDuration(2000);
        animator_stute1.setInterpolator(new LinearInterpolator());
        animator_stute1.setRepeatCount(ValueAnimator.INFINITE);
        animator_stute1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                matrix.postRotate(DEGREES, progress_bitmap.getWidth() / 2, progress_bitmap.getHeight() / 2);
                postInvalidate();
            }
        });
        animator_stute1.start();
    }

    /**
     * 停止的动画
     */
    private void startCountDown() {
        index=360;//将index设置为初始值
        animator_stute2 = ValueAnimator.ofInt(360);
        animator_stute2.setDuration(TIME_FOR_SOTP);
        animator_stute2.setInterpolator(new LinearInterpolator());
        animator_stute2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                index -= S;
                if (index <= 0) {
                    stute = 0;//设置为初始状态
                    index = 360;//将index设置为初始状态
                    is_stop = true;//停止动画结束
                }
                postInvalidate();
            }
        });
        animator_stute2.start();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP && !is_stop && stute == 2) {
            setStute(1);//没有结束停止动画,就返回出车动画
            index=360;
        } else if (event.getAction() == MotionEvent.ACTION_UP && is_stop && stute == 0) {
            is_stop = false;//当前停止动画已经结束,则将is_stop设置为初始值
            return true;
        }
        return super.onTouchEvent(event);
    }

    private void init(Context context, AttributeSet attrs) {
        //获取自定义属性值
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);
        screenWidth = ScreenUtils.getScreenWidth(context);
        round_radius = a.getInt(R.styleable.MyProgressView_round_radius, screenWidth / 14);//默认大小为屏幕宽度的6分之一
        ring_width = a.getInt(R.styleable.MyProgressView_ring_width, 16);//默认外圆的宽度为20
        space_inner_out = a.getInt(R.styleable.MyProgressView_space_inner_out, 30);
        //获取文字的大小,默认为30
        text_size = a.getInt(R.styleable.MyProgressView_text_size, 30);
        text_color = a.getColor(R.styleable.MyProgressView_text_color, Color.WHITE);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        start_round_color = a.getColor(R.styleable.MyProgressView_start_round_color,Color.BLUE);
        centre_round_color = a.getColor(R.styleable.MyProgressView_centre_round_color,Color.RED);
        calculate();
    }

    /**
     * 计算
     */
    public void calculate() {
        //计算view的大小
        viewSizw = round_radius * 2 + ring_width * 2 + space_inner_out * 2;
        //获取bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.yuanhuan);
        progress_bitmap = Bitmap.createScaledBitmap(bitmap, viewSizw, viewSizw, true);
        //绘制旋转动画用到的matrix
        matrix = new Matrix();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(viewSizw, viewSizw);//设置view的大小是上面计算的
        //圆环的位置
        ring_rect = new RectF(0+ring_width/2, 0+ring_width/2,getWidth()-ring_width/2,getHeight()-ring_width/2);
        width = getWidth();
        height = getHeight();
        //圆心
        cx = width / 2;
        cy = height / 2;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (stute) {
            case 0://开始
                //绘制内圆
                mPaint.setColor(start_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
                //绘制外圆环
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(ring_width);
                canvas.drawCircle(cx, cy, width / 2 - ring_width / 2, mPaint);
                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v = mPaint.measureText(START_TEXT);
                canvas.drawText(START_TEXT, width / 2 - v / 2, height / 2 + text_size / 2, mPaint);
                break;
            case 1:
                //绘制内圆
                mPaint.setColor(centre_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆

                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v1 = mPaint.measureText(CENTRE_TEXT);
                canvas.drawText(CENTRE_TEXT, width / 2 - v1 / 2, height / 2 + text_size / 2, mPaint);
                //绘制外圆环
                canvas.drawBitmap(progress_bitmap, matrix, mPaint);
                break;
            case 2:
                //绘制内圆
                mPaint.setColor(centre_round_color);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(cx, cy, round_radius, mPaint);//绘制内圆
                //绘制外圆环
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(ring_width);
                canvas.drawArc(ring_rect, 270, index, false, mPaint);
                //绘制文字
                mPaint.setStrokeWidth(0);
                mPaint.setColor(text_color);
                mPaint.setTextSize(text_size);
                float v2 = mPaint.measureText(CENTRE_TEXT);
                canvas.drawText(CENTRE_TEXT, width / 2 - v2 / 2, height / 2 + text_size / 2, mPaint);
                break;
        }
    }
}

由于写的比较仓促所以可能没有解释的很详细,正好读者就可以仔细推敲了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,737评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,103评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,710评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,909评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,794评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,557评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,939评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,572评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,852评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,871评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,692评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,490评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,939评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,114评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,409评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,971评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,176评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,189评论 25 707
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,677评论 22 664
  • 红点:善良,热情,重情重义,有执行力 黑点:不专注,不坚持,不会演讲,不喜欢喝酒应酬。口才不好,又没有什么销售技巧。
    尘埃里的一粒沙阅读 157评论 0 0
  • 感赏自己一次次在气急败坏的时候,相信只要我坚持,感赏儿子儿子的好,他一定会好的 感赏儿子上午不愿意写作业的时候,在...
    果然妈阅读 149评论 4 6
  • 我带相机,你带上钱,我们远行。 你在沙漠里见过我吗 我好像在那片山间见过你 你坐在大石头上面 细数年月 远方与你 ...
    __旧隐阅读 307评论 0 0