zoukankan      html  css  js  c++  java
  • Android自定义view之仿微信录制视频按钮

    本文章只写了个类似微信的录制视频的按钮,效果图如下:

               

    一、主要的功能:

    1.长按显示进度条,单击事件,录制完成回调

    2.最大时间和最小时间控制

    3.进度条宽度,颜色设置

    二、实现思路

    该自定义View主要有三块组成,白色内圆,浅色大圆,圆形进度条;长按一段时间,内圆缩小0.75倍,外圆放大1.33倍,进度条显示更新,松开手内圆,外圆统一恢复到原来大小;长按时间达到最大,影藏进度条,,同样内圆外圆恢复到原来大小;动画主要用到属性动画中的ValueAnimator,在一定时间内匀速改变内圆,外圆半径,和圆形进度条的绘制角度,最后调用invalidate()重新绘制,起到动画的作用。

    三、代码分析

        @Override
        protected void onDraw(final Canvas canvas) {
            super.onDraw(canvas);
            //绘制外圆
            canvas.drawCircle(mWidth/2,mHeight/2,mBigRadius,mBigCirclePaint);
            //绘制内圆
            canvas.drawCircle(mWidth/2,mHeight/2,mSmallRadius,mSmallCirclePaint);
            //录制的过程中绘制进度条
            if(isRecording){
                drawProgress(canvas);
            }
        }

    绘制内外圆,isRecording表示录制的情况下才参与绘制,相当于显示

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    isPressed=true;
                    mStartTime=System.currentTimeMillis();
                    Message mMessage=Message.obtain();
                    mMessage.what=WHAT_LONG_CLICK;
                    mHandler.sendMessageDelayed(mMessage,mLongClickTime);
                    break;
                case MotionEvent.ACTION_UP:
                    isPressed=false;
                    isRecording=false;
                    mEndTime=System.currentTimeMillis();
                    if(mEndTime-mStartTime<mLongClickTime){
                        mHandler.removeMessages(WHAT_LONG_CLICK);
                        if(onClickListener!=null)
                            onClickListener.onClick();
                    }else{
                        startAnimation(mBigRadius,mInitBitRadius,mSmallRadius,mInitSmallRadius);//手指离开时动画复原
                        if(mProgressAni!=null&&mProgressAni.getCurrentPlayTime()/1000<mMinTime&&!isMaxTime){
                            if(onLongClickListener!=null){
                                onLongClickListener.onNoMinRecord(mMinTime);
                            }
                            mProgressAni.cancel();
                        }else{
                            //录制完成
                            if(onLongClickListener!=null&&!isMaxTime){
                                onLongClickListener.onRecordFinishedListener();
                            }
                        }
                    }
                    break;
            }
            return true;
    
        }

    长按的事件是通过handler发送延时消息实现的,按下的时候就发送,当手指离开,记录按下和离开的时间间隔,达到一定时间即为长按,否则直接移除消息,长按事件失效,此时情况就是点击事件;

        private Handler mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case WHAT_LONG_CLICK:
                        //长按事件触发
                        if(onLongClickListener!=null) {
                            onLongClickListener.onLongClick();
                        }
                        //内外圆动画,内圆缩小,外圆放大
                        startAnimation(mBigRadius,mBigRadius*1.33f,mSmallRadius,mSmallRadius*0.7f);
                        break;
                }
            }
        } ;

    handler里面处理的即为长按的触发事件,此时开始startAnimation

    private void startAnimation(float bigStart,float bigEnd, float smallStart,float smallEnd) {
            ValueAnimator bigObjAni=ValueAnimator.ofFloat(bigStart,bigEnd);
            bigObjAni.setDuration(150);
            bigObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mBigRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);
            smallObjAni.setDuration(150);
            smallObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mSmallRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            bigObjAni.start();
            smallObjAni.start();
    
            smallObjAni.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    isRecording=false;
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    //开始绘制圆形进度
                    if(isPressed){
                        isRecording=true;
                        isMaxTime=false;
                        startProgressAnimation();
                    }
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
    
        }

    ValueAnimator.ofFloat(bigStart,bigEnd);让圆的半径从bigStart到bigEnd动态变化,

    设置addUpdateListener监听,获取正在变化的值重新赋值给圆的半径,调用 invalidate重新绘制,从而起到动画的效果,在开始录制的动画结束后,来绘制圆形进度条,

    /**
         * 绘制圆形进度
         * @param canvas
         */
        private void drawProgress(Canvas canvas) {
            mProgressCirclePaint.setStrokeWidth(mProgressW);
            mProgressCirclePaint.setStyle(Paint.Style.STROKE);
            //用于定义的圆弧的形状和大小的界限
            RectF oval = new RectF(mWidth/2-(mBigRadius-mProgressW/2), mHeight/2-(mBigRadius-mProgressW/2), mWidth/2+(mBigRadius-mProgressW/2),mHeight/2+(mBigRadius-mProgressW/2));
            //根据进度画圆弧
            canvas.drawArc(oval, -90, mCurrentProgress, false, mProgressCirclePaint);
        }

    RectF限制圆弧的绘制范围,mCurrentProgress绘制的角度0~360f之间变化,同样可以利用ValueAnimator,来在0~360f之间不断改变,然后不断更新绘制,起到进度条动态更新的效果

    四、全部代码

    package com.yus.ycamera;
    
    import android.animation.Animator;
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.RectF;
    import android.os.Handler;
    import android.os.Message;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * Created by yufs on 2017/7/4.
     */
    
    public class CircleButtonView extends View{
        private static final int WHAT_LONG_CLICK = 1;
        private Paint mBigCirclePaint;
        private Paint mSmallCirclePaint;
        private Paint mProgressCirclePaint;
        private int mHeight;//当前View的高
        private int mWidth;//当前View的宽
        private float mInitBitRadius;
        private float mInitSmallRadius;
        private float mBigRadius;
        private float mSmallRadius;
        private long mStartTime;
        private long mEndTime;
        private Context mContext;
        private boolean isRecording;//录制状态
        private boolean isMaxTime;//达到最大录制时间
        private float mCurrentProgress;//当前进度
    
        private long mLongClickTime=500;//长按最短时间(毫秒),
        private int mTime=5;//录制最大时间s
        private int mMinTime=3;//录制最短时间
        private int mProgressColor;//进度条颜色
        private float mProgressW=18f;//圆环宽度
    
        private boolean isPressed;//当前手指处于按压状态
        private ValueAnimator mProgressAni;//圆弧进度变化
    
    
        public CircleButtonView(Context context ) {
            super(context);
            init(context,null);
        }
    
        public CircleButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context,attrs);
        }
    
        public CircleButtonView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context,attrs);
        }
    
        private void init(Context context,AttributeSet attrs) {
            this.mContext=context;
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleButtonView);
            mMinTime=a.getInt(R.styleable.CircleButtonView_minTime,0);
            mTime=a.getInt(R.styleable.CircleButtonView_maxTime,10);
            mProgressW=a.getDimension(R.styleable.CircleButtonView_progressWidth,12f);
            mProgressColor=a.getColor(R.styleable.CircleButtonView_progressColor,Color.parseColor("#6ABF66"));
            a.recycle();
            //初始画笔抗锯齿、颜色
            mBigCirclePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
            mBigCirclePaint.setColor(Color.parseColor("#DDDDDD"));
    
            mSmallCirclePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
            mSmallCirclePaint.setColor(Color.parseColor("#FFFFFF"));
    
            mProgressCirclePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
            mProgressCirclePaint.setColor(mProgressColor);
    
            mProgressAni= ValueAnimator.ofFloat(0, 360f);
            mProgressAni.setDuration(mTime*1000);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
            mHeight=MeasureSpec.getSize(heightMeasureSpec);
            mInitBitRadius=mBigRadius= mWidth/2*0.75f;
            mInitSmallRadius=mSmallRadius= mBigRadius*0.75f;
        }
    
        @Override
        protected void onDraw(final Canvas canvas) {
            super.onDraw(canvas);
            //绘制外圆
            canvas.drawCircle(mWidth/2,mHeight/2,mBigRadius,mBigCirclePaint);
            //绘制内圆
            canvas.drawCircle(mWidth/2,mHeight/2,mSmallRadius,mSmallCirclePaint);
            //录制的过程中绘制进度条
            if(isRecording){
                drawProgress(canvas);
            }
        }
    
        /**
         * 绘制圆形进度
         * @param canvas
         */
        private void drawProgress(Canvas canvas) {
            mProgressCirclePaint.setStrokeWidth(mProgressW);
            mProgressCirclePaint.setStyle(Paint.Style.STROKE);
            //用于定义的圆弧的形状和大小的界限
            RectF oval = new RectF(mWidth/2-(mBigRadius-mProgressW/2), mHeight/2-(mBigRadius-mProgressW/2), mWidth/2+(mBigRadius-mProgressW/2),mHeight/2+(mBigRadius-mProgressW/2));
            //根据进度画圆弧
            canvas.drawArc(oval, -90, mCurrentProgress, false, mProgressCirclePaint);
        }
    
        private Handler mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case WHAT_LONG_CLICK:
                        //长按事件触发
                        if(onLongClickListener!=null) {
                            onLongClickListener.onLongClick();
                        }
                        //内外圆动画,内圆缩小,外圆放大
                        startAnimation(mBigRadius,mBigRadius*1.33f,mSmallRadius,mSmallRadius*0.7f);
                        break;
                }
            }
        } ;
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    isPressed=true;
                    mStartTime=System.currentTimeMillis();
                    Message mMessage=Message.obtain();
                    mMessage.what=WHAT_LONG_CLICK;
                    mHandler.sendMessageDelayed(mMessage,mLongClickTime);
                    break;
                case MotionEvent.ACTION_UP:
                    isPressed=false;
                    isRecording=false;
                    mEndTime=System.currentTimeMillis();
                    if(mEndTime-mStartTime<mLongClickTime){
                        mHandler.removeMessages(WHAT_LONG_CLICK);
                        if(onClickListener!=null)
                            onClickListener.onClick();
                    }else{
                        startAnimation(mBigRadius,mInitBitRadius,mSmallRadius,mInitSmallRadius);//手指离开时动画复原
                        if(mProgressAni!=null&&mProgressAni.getCurrentPlayTime()/1000<mMinTime&&!isMaxTime){
                            if(onLongClickListener!=null){
                                onLongClickListener.onNoMinRecord(mMinTime);
                            }
                            mProgressAni.cancel();
                        }else{
                            //录制完成
                            if(onLongClickListener!=null&&!isMaxTime){
                                onLongClickListener.onRecordFinishedListener();
                            }
                        }
                    }
                    break;
            }
            return true;
    
        }
    
        private void startAnimation(float bigStart,float bigEnd, float smallStart,float smallEnd) {
            ValueAnimator bigObjAni=ValueAnimator.ofFloat(bigStart,bigEnd);
            bigObjAni.setDuration(150);
            bigObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mBigRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);
            smallObjAni.setDuration(150);
            smallObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mSmallRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            bigObjAni.start();
            smallObjAni.start();
    
            smallObjAni.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    isRecording=false;
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    //开始绘制圆形进度
                    if(isPressed){
                        isRecording=true;
                        isMaxTime=false;
                        startProgressAnimation();
                    }
                }
    
    
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
    
        }
    
        /**
         * 圆形进度变化动画
         */
        private void startProgressAnimation() {
            mProgressAni.start();
            mProgressAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurrentProgress= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            mProgressAni.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    //录制动画结束时,即为录制全部完成
                    if(onLongClickListener!=null&&isPressed){
                        isPressed=false;
                        isMaxTime=true;
                        onLongClickListener.onRecordFinishedListener();
                        startAnimation(mBigRadius,mInitBitRadius,mSmallRadius,mInitSmallRadius);
                        //影藏进度进度条
                        mCurrentProgress=0;
                        invalidate();
                    }
    
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
        }
    
        /**
         * 长按监听器
         */
        public interface OnLongClickListener{
            void onLongClick();
            //未达到最小录制时间
            void onNoMinRecord(int currentTime);
            //录制完成
            void onRecordFinishedListener();
        }
        public OnLongClickListener onLongClickListener;
    
        public void setOnLongClickListener(OnLongClickListener onLongClickListener) {
            this.onLongClickListener = onLongClickListener;
        }
    
        /**
         * 点击监听器
         */
        public interface OnClickListener{
            void onClick();
        }
        public OnClickListener onClickListener;
    
        public void setOnClickListener(OnClickListener onClickListener) {
            this.onClickListener = onClickListener;
        }
    
    }

    属性文件

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="CircleButtonView">
            <attr name="minTime" format="integer"></attr>
            <attr name="maxTime" format="integer"></attr>
            <attr name="progressColor" format="color"></attr>
            <attr name="progressWidth" format="dimension"></attr>
        </declare-styleable>
    </resources>

    全部的大家可以下载源码查看,有什么问题,欢迎提出,后续会将此控件应用到小视频的录制上面,下一遍记录小视频录制,还有就是个人在demo演示的时候都没有找到个将视频转gif的,上面也只能贴图片,哎,心塞

    源码

     

  • 相关阅读:
    我总结的面试题系列:kafka
    RabbitMQ大厂面试题
    [Algorithm] 并查集
    [LintCode] 编辑距离
    [LeetCode] Length of Longest Fibonacci Subsequence
    [LintCode] 交叉字符串
    [LeetCode] Permutation Sequence
    Permutation Sequence
    [LeetCode] Next Permutation
    [LeetCode] Longest Palindromic Substring
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/9035170.html
Copyright © 2011-2022 走看看