本文章只写了个类似微信的录制视频的按钮,效果图如下:
一、主要的功能:
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
长按的事件是通过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) {
}
});
}
设置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
属性文件
源码