美文网首页Android开发Android技术知识程序员
自定义view之一个简单的包含开始停止两种状态的动画(仿uber

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

作者: laer_L | 来源:发表于2016-10-29 15:31 被阅读380次

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

    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;
            }
        }
    }
    

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

    相关文章

      网友评论

        本文标题:自定义view之一个简单的包含开始停止两种状态的动画(仿uber

        本文链接:https://www.haomeiwen.com/subject/gqfjuttx.html