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/,mHeight/,mBigRadius,mBigCirclePaint);
//绘制内圆
canvas.drawCircle(mWidth/,mHeight/,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()/<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();
bigObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBigRadius= (float) animation.getAnimatedValue();
invalidate();
}
}); ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);
smallObjAni.setDuration();
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/-(mBigRadius-mProgressW/), mHeight/-(mBigRadius-mProgressW/), mWidth/+(mBigRadius-mProgressW/),mHeight/+(mBigRadius-mProgressW/));
//根据进度画圆弧
canvas.drawArc(oval, -, 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 = ;
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=;//长按最短时间(毫秒),
private int mTime=;//录制最大时间s
private int mMinTime=;//录制最短时间
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,);
mTime=a.getInt(R.styleable.CircleButtonView_maxTime,);
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(, 360f);
mProgressAni.setDuration(mTime*);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight=MeasureSpec.getSize(heightMeasureSpec);
mInitBitRadius=mBigRadius= mWidth/*0.75f;
mInitSmallRadius=mSmallRadius= mBigRadius*0.75f;
} @Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
//绘制外圆
canvas.drawCircle(mWidth/,mHeight/,mBigRadius,mBigCirclePaint);
//绘制内圆
canvas.drawCircle(mWidth/,mHeight/,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/-(mBigRadius-mProgressW/), mHeight/-(mBigRadius-mProgressW/), mWidth/+(mBigRadius-mProgressW/),mHeight/+(mBigRadius-mProgressW/));
//根据进度画圆弧
canvas.drawArc(oval, -, 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()/<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();
bigObjAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBigRadius= (float) animation.getAnimatedValue();
invalidate();
}
}); ValueAnimator smallObjAni=ValueAnimator.ofFloat(smallStart,smallEnd);
smallObjAni.setDuration();
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=;
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的,上面也只能贴图片,哎,心塞
Android自定义view之仿微信录制视频按钮的更多相关文章
- Android 自定义View,仿微信视频播放按钮
闲着,尝试实现了新版微信视频播放按钮,使用的是自定义View,先来个简单的效果图...真的很简单哈. 由于暂时用不到,加上时间原因,加上实在是没意思,加上……,本控件就没有实现自定义属性,有兴趣的朋友 ...
- android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...
- Android自定义View实现仿QQ实现运动步数效果
效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...
- Android 自定义View修炼-仿360手机卫士波浪球进度的实现
像360卫士的波浪球进度的效果,一般最常用的方法就是 画线的方式,先绘sin线或贝塞尔曲线,然后从左到右绘制竖线,然后再裁剪圆区域. 今天我这用图片bitmap的方式,大概的方法原理是: (1)首先用 ...
- Android 自定义View修炼-仿QQ5.0 的侧滑菜单效果的实现
有一段时间没有写博客了,最近比较忙,没什么时间写,刚好今天有点时间, 我就分享下,侧滑菜单的实现原理,一般android侧滑的实现原理和步骤如下:(源码下载在下面最后给出哈) 1.使用ViewGrou ...
- Android自定义View之ProgressBar出场记
关于自定义View,我们前面已经有三篇文章在介绍了,如果筒子们还没阅读,建议先看一下,分别是android自定义View之钟表诞生记.android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检 ...
- android自定义View之NotePad出鞘记
现在我们的手机上基本都会有一个记事本,用起来倒也还算方便,记事本这种东东,如果我想要自己实现,该怎么做呢?今天我们就通过自定义View的方式来自定义一个记事本.OK,废话不多说,先来看看效果图. 整个 ...
- Android 仿微信小视频录制
Android 仿微信小视频录制 WechatShortVideo和WechatShortVideo文章
- Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...
随机推荐
- DTP模型之二:(XA协议之二)JTA集成JOTM或Atomikos配置分布式事务(Tomcat应用服务器)
jotm只能用的xapool数据源,而且很少更新. 一.以下介绍Spring中直接集成JOTM提供JTA事务管理.将JOTM集成到Tomcat中. (经过测试JOTM在批量持久化时有BUG需要修改源码 ...
- Coloring Brackets
题意: 给一匹配的括号序列,要求每对括号恰有一个被染成蓝或红色,要求相邻的括号不同色,求方案数. 解法: 类比树的hash将括号序列转化为一棵树,树上子节点之间不得出现冲突, 子节点和父节点不得出现冲 ...
- 3-1Java程序的执行流程
3-1Java程序的执行流程 用记事本写一个简单的程序 存到:E:\java路径下 class HelloImooc{ public static void main(String[] agrg ...
- PXE与cobbler实现系统自动安装
安装操作系统的流程事实上并不复杂,如果你要给三五台服务器安装系统那么我们手工去安装即可.但是当我们要部署的是大型机房的操作系统的时候显然手动安装去一台一台的点,是不现实的.尤其现在互联网行业高速发展, ...
- L2-023 图着色问题 (25 分)vector
图着色问题是一个著名的NP完全问题.给定无向图,,问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色? 但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请 ...
- 移动平台unity3d优化
目录(?)[-] Focus on GPUs 着眼于GPU Good practice 优秀的实践 Sharer optimizations 着色器优化 Focus on CPUs 着眼于CPUs G ...
- [Xcode 实际操作]八、网络与多线程-(23)多线程的同步与异步的区别
目录:[Swift]Xcode实际操作 本文将演示线程的同步与异步的区别. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] 异步线程的运行,是没有按照顺序执行的. ...
- suse 11.4添加阿里源
感谢SilenMark 作者,让我找到了一个可用的suse 国内源. 大家直接使用root 用户执行以下命令,添加阿里源 zypper addrepo -f http://mirrors.aliyun ...
- MySQL--表操作1
这是对自己学习燕十八老师mysql教程的总结,非常感谢燕十八老师. 依赖软件:mysql 系统环境:win 注:本次所有命令都是在命令行上执行 数据库的四大天王操作:增删改查 增删改查都是在对表进行操 ...
- MQ简介1
站在巨人的肩膀上 关于消息队列的使用 一.消息队列概述消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息 ...