Android自定义view之仿微信录制视频按钮
本文章只写了个类似微信的录制视频的按钮,效果图如下:
一、主要的功能:
1.长按显示进度条,单击事件,录制完成回调
2.最大时间和最小时间控制
3.进度条宽度,颜色设置
二、实现思路
该自定义View主要有三块组成,白色内圆,浅色大圆,圆形进度条;长按一段时间,内圆缩小0.75倍,外圆放大1.33倍,进度条显示更新,松开手内圆,外圆统一恢复到原来大小;长按时间达到最大,影藏进度条,,同样内圆外圆恢复到原来大小;动画主要用到属性动画中的ValueAnimator,在一定时间内匀速改变内圆,外圆半径,和圆形进度条的绘制角度,最后调用invalidate()重新绘制,起到动画的作用。
三、代码分析
@Overrideprotected 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表示录制的情况下才参与绘制,相当于显示
@Overridepublic 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(){@Overridepublic 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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mBigRadius= (float) animation.getAnimatedValue();invalidate();}});ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);smallObjAni.setDuration(150);smallObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mSmallRadius= (float) animation.getAnimatedValue();invalidate();}});bigObjAni.start();smallObjAni.start();smallObjAni.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {isRecording=false;}@Overridepublic void onAnimationEnd(Animator animation) {//开始绘制圆形进度if(isPressed){isRecording=true;isMaxTime=false;startProgressAnimation();}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic 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;//录制最大时间sprivate 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);}@Overrideprotected 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;}@Overrideprotected 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(){@Overridepublic 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;}}} ;@Overridepublic 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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mBigRadius= (float) animation.getAnimatedValue();invalidate();}});ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);smallObjAni.setDuration(150);smallObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mSmallRadius= (float) animation.getAnimatedValue();invalidate();}});bigObjAni.start();smallObjAni.start();smallObjAni.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {isRecording=false;}@Overridepublic void onAnimationEnd(Animator animation) {//开始绘制圆形进度if(isPressed){isRecording=true;isMaxTime=false;startProgressAnimation();}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});}/*** 圆形进度变化动画*/private void startProgressAnimation() {mProgressAni.start();mProgressAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentProgress= (float) animation.getAnimatedValue();invalidate();}});mProgressAni.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {//录制动画结束时,即为录制全部完成if(onLongClickListener!=null&&isPressed){isPressed=false;isMaxTime=true;onLongClickListener.onRecordFinishedListener();startAnimation(mBigRadius,mInitBitRadius,mSmallRadius,mInitSmallRadius);//影藏进度进度条mCurrentProgress=0;invalidate();}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic 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的,上面也只能贴图片,哎,心塞
源码
Android自定义view之仿微信录制视频按钮相关推荐
- Android自定义View分享——仿微信朋友圈图片合并效果
写在前面 笔者近来在学习Android自定义View,收集了一些不算复杂但又"长得"还可以的自定义View效果实现,之前分享过两个效果:一个水平的进度条,一个圆形温度显示器,如果你 ...
- android 自定义取色器,【Android自定义View】仿Photoshop取色器ColorPicker(二)
ColorPicker 一款仿Photoshop取色器的Android版取色器. 前言 上一篇已经简单介绍了ColorPicker的项目结构以及两种颜色空间,接下来我们详细解析一下ColorPicke ...
- 仿微信录制视频和拍照并发送留言
仿微信小视频录制功能,打开相机后,点击是拍照,长按是录制,录制小于1秒,要提示"录制时间太短",最大可以录制1分钟的视频,拍完照或录制完视频后,要自动跳转到相片或视频展示页面,点击 ...
- Android自定义View之仿QQ运动步数进度效果
文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...
- Android自定义view之仿支付宝芝麻信用仪表盘 ---by ccy
自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 <?xml version="1.0" encoding=&qu ...
- 自定义xy组 android,Android自定义view之仿支付宝芝麻信用仪表盘示例
自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 接着在构造方法里初始化自定义属性和画笔: private void initAttr(At ...
- Android自定义View分享——仿网易云音乐留声机效果
写在前面 这是笔者自学习自定义View以来,分享的第五篇效果,之前分享过一篇动态时钟效果的自定义View,如果有兴趣的可以看看: Android自定义View分享--一个时钟 之前的博客笔者一般都会说 ...
- 仿微信录制视频之自定义View
最近公司一个项目需要实现仿微信拍照,然后我去看了看微信的界面: 然后我自己最后实现的界面是这样: 当然,这个界面不是重点,重点是这个自定义View需要实现单击实现拍照,长按实现录制视频.然后这个自定义 ...
- Android自定义ListView实现仿微信侧滑删除
经常在遇到问题第一时间都会在网上搜索解决的方法,因此看到很多前辈们的比较精辟的技术文章,学习了很多东西,现在将自己平时工作中开发的一些小功能坐下总结,也写出来,既方便自己理清思路记忆功能块实现思路,又 ...
最新文章
- 《塞洛特傳說》道具系统
- python使用符号 表示单行注释-Pyhton 单行、多行注释符号使用方法及规范
- Jmeter接口测试进阶
- Union-Find 并查集算法详解
- 应行家算法_一些行家技巧和窍门
- prometheus命令_Prometheus入门教程(一):Prometheus 快速入门
- 618电商大促 到底谁家赢了?大家都这么有钱的吗?
- GPS导航电文编码与校验
- Arduino 超声波避障循迹小车,四轮智能小车
- 质量管理体系五大核心工具
- python音频 降噪_一种基于深度神经网络的音频降噪方法技术
- 计算机提示策略阻止安装,win7安装软件提示此程序被组策略阻止怎么办
- 网线/双绞线相关知识
- Exp7 网络欺诈防范 20164302 王一帆
- 高漫数位板1060PRO 8192级的驱动下载与安装
- 企业邮箱如何发送国外邮件?2021知名企业邮箱网站排名
- 为什么装完计算机系统后进不去,电脑系统装完后为啥进不去?
- 文件共享服务器onedrive,如何共享OneDrive文件和文件夹
- MD5算法的编程实现
- ubuntu16.04的HDMI没有输出不能外接显示器