仿微信录制视频之自定义View

最近公司一个项目需要实现仿微信拍照,然后我去看了看微信的界面:
这里写图片描述
然后我自己最后实现的界面是这样:
这里写图片描述
当然,这个界面不是重点,重点是这个自定义View需要实现单击实现拍照,长按实现录制视频。然后这个自定义View可以通过自定义触摸事件来完成区别。其实一开始我也不明白,为什么按下去的时候就开始调用了长按监听事件,最后还能将二者区分。最后留意到这一句代码:

myHandler.sendEmptyMessageDelayed(0, animTime);//animTime默认值是200;

其中表达的意思是该信息将在200毫秒后发送出去,我们看看手指抬起后的处理:

case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                startTime = 0;
                float upX = event.getX();
                float upY = event.getY();
                if(myHandler.hasMessages(0)){
                    myHandler.removeMessages(0);
                    if (Math.abs(upX - downX) < dp5 && Math.abs(upY - downY) < dp5) {
                        if(onGestureListener != null) onGestureListener.onClick();
                    }
                }else if(onGestureListener != null && closeMode){
                    onGestureListener.onLift();
                    closeButton();
                }
                break;

如果myHandler还有这条消息没有处理(拍照的情况)就移除,并调用点击的监听事件;如果没有这条消息了(录制视频的情况)就调用手指离开的监听,并且通过closeButton()方法来初始化控件,整个手势处理就这么简单,代码如下:

package cn.com.vicent.mymap;

import android.animation.ValueAnimator;
import android.content.Context;
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.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by asus on 2017/4/22.
 */

public class RecordedButton extends View {
    private static final String TAG = "RecordedButton";
    private int measuredWidth = -1;
    private Paint paint;
    private int colorGray;
    private float radius1;
    private float radius2;
    private float zoom = 0.7f;//初始化缩放比例
    private int dp5;
    private Paint paintProgress;
    private int colorBlue;
    private float girth;//周长
    private RectF oval;
    private int max;
    private OnGestureListener onGestureListener;
    private int animTime = 200;
    private float downX;
    private float downY;
    private boolean closeMode = true;

    public RecordedButton(Context context) {
        super(context);
        init();
    }

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

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

    private void init() {

        dp5 = (int) getResources().getDimension(R.dimen.dp5);
        colorGray = getResources().getColor(R.color.gray);
        colorBlue = getResources().getColor(R.color.blue);

        paint = new Paint();
        paint.setAntiAlias(true);

        paintProgress = new Paint();
        paintProgress.setAntiAlias(true);
        paintProgress.setColor(colorBlue);
        paintProgress.setStrokeWidth(dp5);
        paintProgress.setStyle(Paint.Style.STROKE);
    }

    public interface OnGestureListener {
        void onLongClick();
        void onClick();
        void onLift();
        void onOver();
    }

    public void setOnGestureListener(OnGestureListener onGestureListener){
        this.onGestureListener = onGestureListener;
    }

    private Handler myHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(onGestureListener != null) {
                startAnim(0, 1-zoom);
                onGestureListener.onLongClick();
                closeMode = true;
            }
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                myHandler.sendEmptyMessageDelayed(0, animTime);
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                float upX = event.getX();
                float upY = event.getY();
                if(myHandler.hasMessages(0)){
                    myHandler.removeMessages(0);
                    if (Math.abs(upX - downX) < dp5 && Math.abs(upY - downY) < dp5) {
                        if(onGestureListener != null) onGestureListener.onClick();
                    }
                }else if(onGestureListener != null && closeMode){
                    onGestureListener.onLift();
                    closeButton();
                }
                break;
        }
        return true;
    }

    public void closeButton(){

        if(closeMode) {
            closeMode = false;
            startAnim(1 - zoom, 0);
            girth = 0;
            invalidate();
        }
    }

    private void startAnim(float start, float end){

        ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(animTime);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                radius1 = measuredWidth * (zoom + value) / 2;
                radius2 = measuredWidth * (zoom - value) / 2.5f;
                invalidate();
            }
        });
        va.start();
    }

    public void setMax(int max){
        this.max = max;
    }

    public void setProgress(float progress){

        float ratio = progress/max;
        girth = 370*ratio;
        invalidate();

        if(ratio >= 1){
            if(onGestureListener != null) onGestureListener.onOver();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if(measuredWidth == -1) {
            measuredWidth = getMeasuredWidth();
            // radius1:radius2 = 5:4
            radius1 = measuredWidth* zoom /2;
            radius2 = measuredWidth* zoom /2.5f;

            //设置绘制大小 边距2.5dp
            oval = new RectF();
            oval.left = dp5/2;
            oval.top = dp5/2;
            oval.right = measuredWidth-dp5/2;
            oval.bottom = measuredWidth-dp5/2;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        //绘制外圈圆 radius1代表绘制半径
        paint.setColor(colorGray);
        canvas.drawCircle(measuredWidth/2, measuredWidth/2, radius1, paint);

        //绘制内圈圆 radius2代表绘制半径
        paint.setColor(Color.WHITE);
        canvas.drawCircle(measuredWidth/2, measuredWidth/2, radius2, paint);
        Log.d(TAG, "onDraw: "+radius1+"   "+radius2);
        //绘制进度 270表示以圆的270度为起点, 绘制girth长度的弧线
        canvas.drawArc(oval, 270, girth, false, paintProgress);
    }
}

使用的也比较简单,没有一个自定义属性,所以实例化控件之后简单的设定一下录制视频的最长时间和具体的监听事件实现就好!

  final RecordedButton recordedButton = (RecordedButton) findViewById(R.id.btn);
        recordedButton.setMax(30*1000);//最长录制时间30秒
        Timer timer = new Timer();//通过timer来模拟拍摄的进度
        recordedButton.setOnGestureListener(new RecordedButton.OnGestureListener() {
            @Override
            public void onLongClick() {
                //长按监听
                final long startTime = System.currentTimeMillis();

                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                long progress = System.currentTimeMillis()-startTime;
                                recordedButton.setProgress(progress);//默认的进度值是0,所以这个需要自定义
                            }
                        });
                    }
                },1000,1000);
            }

            @Override
            public void onClick() {
timer.cancel();
            }

            @Override
            public void onLift() {
timer.cancel();
            }

            @Override
            public void onOver() {
timer.cancel();
            }
        });

如果这里的进度值只是时间的话,其实我们还可以直接在控件里面来实现,将控件的功能封装得更好,于是修改代码如下:

private boolean closeMode = false;
private long startTime;
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                startTime = System.currentTimeMillis();
                myHandler.sendEmptyMessageDelayed(0, animTime);
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                startTime = 0;
                float upX = event.getX();
                float upY = event.getY();
                if(myHandler.hasMessages(0)){
                    myHandler.removeMessages(0);
                    if (Math.abs(upX - downX) < dp5 && Math.abs(upY - downY) < dp5) {
                        if(onGestureListener != null) onGestureListener.onClick();
                    }
                }else if(onGestureListener != null && closeMode){
                    onGestureListener.onLift();
                    closeButton();
                }
                break;
        }
        return true;
    }
     private Handler myHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {


            if(onGestureListener != null) {
                startAnim(0, 1-zoom);
                onGestureListener.onLongClick();
                closeMode = true;

                if(!closeMode){
                    setProgress(System.currentTimeMillis()-startTime);
                    myHandler.sendEmptyMessageDelayed(0, 50);
                }
            }
        }
    };
    public void setProgress(float progress){

        float ratio = progress/max;
        girth = 370*ratio;
        invalidate();

        if(ratio >= 1){
            if(onGestureListener != null) onGestureListener.onOver();
        }
    }

OK,这个功能就实现了!

学习的源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值