Android自定义view之仿微信录制视频按钮

来源:互联网 发布:停车场收费系统数据库 编辑:程序博客网 时间:2024/05/21 15:41

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

          


一、主要的功能:

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的,上面也只能贴图片,哎,心塞


源码



原创粉丝点击