Android自定义View(短视频录制按钮)
2020-09-14 本文已影响0人
火星移民局局长
1,分析效果
image.png1,控件由一个圆和一个逐步完善的圆环构成
2,圆环启示角度为270
3,按下按钮,圆环开始逐步完善,且录制事件开始
4,手指抬起,圆环停止完善;且录制事件停止
5,圆环满足360°录制事件停止
2,资源属性定义
<declare-styleable name="VideoTakeButton">
<attr name="cBackColor" format="color"/>//按钮的背景色
<attr name="oInColor" format="color"/>//圆环的背景色
<attr name="durTime" format="integer"/>//圆环完成一圈需要的事件
<attr name="rangWidth" format="dimension"/>//圆环的宽度
</declare-styleable>
3,开始编码
1.获取定义的属性参数
private int cBackColor;//中间按钮的背景
private int oInColor;//外环转动圆环的填充色
private long durTime;//圆环转动一圈持续的时间
private float rangWidth;//走动的圆环宽度
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoTakeButton);
cBackColor = typedArray.getColor(R.styleable.VideoTakeButton_cBackColor, R.color.colorPrimary);
oInColor = typedArray.getColor(R.styleable.VideoTakeButton_oInColor, R.color.colorPrimary);
durTime = typedArray.getInt(R.styleable.VideoTakeButton_durTime, 10000);
rangWidth = typedArray.getDimension(R.styleable.VideoTakeButton_rangWidth, 10);
2.初始化画笔
cBtnPaint = new Paint();
cBtnPaint.setColor(cBackColor);
cBtnPaint.setStyle(Paint.Style.FILL);
cBtnPaint.setAntiAlias(true);
oCPaint = new Paint();
oCPaint.setColor(oInColor);
oCPaint.setStrokeWidth(rangWidth);
oCPaint.setStyle(Paint.Style.STROKE);
oCPaint.setAntiAlias(true);
3.测量控件大小
int measureDimension(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.AT_MOST:
result = 100;
break;
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
4.绘制控件
- 圆
canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getWidth(), getHeight()) / 2 - rangWidth - rangAndCirPadding, cBtnPaint);
//圆的半径=控件布局-圆环的宽度-与圆环的边距
- 圆环:因为是有动画效果,所以sweepAngle参数需要动态修改
float sweepAngle;
//绘制
canvas.drawArc(rangWidth / 2, rangWidth / 2, getWidth() - rangWidth / 2, getHeight() - rangWidth / 2, 270, sweepAngle, false, oCPaint);
//需要逐步修改sweepAngle值重新绘制完成动画效果使用ValueAnimator
private void start() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(360f);
valueAnimator.setDuration(durTime);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
sweepAngle = (float) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
5.触摸事件
- 按下:开始绘制圆环
- 抬手:停止绘制圆环
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//手指按下
if (isOnClickCnnter(event.getX(), event.getY()) && viewTakeButtonState == ViewTakeButtonState.STATE_READY) {
start();
}
break;
case MotionEvent.ACTION_UP:
//手指移出
if (viewTakeButtonState == ViewTakeButtonState.STATE_TAKEING) {
stop();
}
break;
}
return true;
}
最后,将事件回调给使用方去执行录制等动作(结合代码)
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class VideoTakeButton extends View {
// private String TAG = VideoTakeButton.class.getSimpleName();
public VideoTakeButton(Context context) {
this(context, null);
}
public VideoTakeButton(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public VideoTakeButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取配置的资源属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoTakeButton);
cBackColor = typedArray.getColor(R.styleable.VideoTakeButton_cBackColor, R.color.colorPrimary);
oInColor = typedArray.getColor(R.styleable.VideoTakeButton_oInColor, R.color.colorPrimary);
durTime = typedArray.getInt(R.styleable.VideoTakeButton_durTime, 10000);
rangWidth = typedArray.getDimension(R.styleable.VideoTakeButton_rangWidth, 10);
init();
}
private Paint cBtnPaint;//中间按钮的画笔
private Paint oCPaint;//外环转动圆环的画笔
private int cBackColor;//中间按钮的背景
private int oInColor;//外环转动圆环的填充色
private long durTime;//圆环转动一圈持续的时间
private float rangWidth;//走动的圆环宽度
private int rangAndCirPadding = 20;//默认按钮和走动圆环的 的距离
ViewTakeButtonState viewTakeButtonState;
OnVideoTakeButtonListener onVideoTakeButtonListener;
float sweepAngle = 0f;
private void init() {
viewTakeButtonState = ViewTakeButtonState.STATE_READY;
cBtnPaint = new Paint();
cBtnPaint.setColor(cBackColor);
cBtnPaint.setStyle(Paint.Style.FILL);
cBtnPaint.setAntiAlias(true);
oCPaint = new Paint();
oCPaint.setColor(oInColor);
oCPaint.setStrokeWidth(rangWidth);
oCPaint.setStyle(Paint.Style.STROKE);
oCPaint.setAntiAlias(true);
}
/**
* 添加监听
*
* @param onVideoTakeButtonListener
*/
public void addVideoTakeButtonListener(OnVideoTakeButtonListener onVideoTakeButtonListener) {
this.onVideoTakeButtonListener = onVideoTakeButtonListener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureDimension(widthMeasureSpec), measureDimension(heightMeasureSpec));
}
ValueAnimator valueAnimator;
/**
* 开始
*/
private void start() {
valueAnimator = ValueAnimator.ofFloat(360f);
valueAnimator.setDuration(durTime);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
sweepAngle = (float) animation.getAnimatedValue();
if (onVideoTakeButtonListener!=null&&sweepAngle==360) {
viewTakeButtonState = ViewTakeButtonState.STATE_READY;
onVideoTakeButtonListener.stop();
}
invalidate();
}
});
valueAnimator.start();
}
private void stop() {
if (valueAnimator!=null){
valueAnimator.cancel();
}
}
//重置
public void reset(){
sweepAngle=0;
invalidate();
}
int measureDimension(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.AT_MOST:
result = 100;
break;
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getWidth(), getHeight()) / 2 - rangWidth - rangAndCirPadding, cBtnPaint);
canvas.drawArc(rangWidth / 2, rangWidth / 2, getWidth() - rangWidth / 2, getHeight() - rangWidth / 2, 270, sweepAngle, false, oCPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//手指按下
if (isOnClickCnnter(event.getX(), event.getY()) && viewTakeButtonState == ViewTakeButtonState.STATE_READY) {
start();
viewTakeButtonState = ViewTakeButtonState.STATE_TAKEING;
if (onVideoTakeButtonListener!=null) {
onVideoTakeButtonListener.start();
}
}
break;
case MotionEvent.ACTION_UP:
//手指移除
if (viewTakeButtonState == ViewTakeButtonState.STATE_TAKEING) {
stop();
viewTakeButtonState = ViewTakeButtonState.STATE_READY;
if (onVideoTakeButtonListener!=null) {
onVideoTakeButtonListener.stop();
}
}
break;
}
return true;
}
//是不是按在中心圆的 位置
private boolean isOnClickCnnter(float x, float y) {
return x > (rangWidth + rangAndCirPadding) && x < (getWidth() - rangWidth - rangAndCirPadding) && y > (rangWidth + rangAndCirPadding) && y < (getHeight() - rangWidth - rangAndCirPadding);
}
enum ViewTakeButtonState {
STATE_READY,
STATE_TAKEING
}
public interface OnVideoTakeButtonListener {
void start();
void stop();
}
}