不得不說自定義view繪製各類圖形及它們的動畫是使人有成就感的事情。該篇來分享以前項目中寫的一個視頻錄製按鈕的自定義view,該view有錄製、暫停(結束)等狀態的控制,各類狀態轉變的動畫,以及錄製進度展現。先看效果(圓圈的小白色段是最小錄製時長的標記點): android
代碼的內容其實主要就是繪製進度條、錄製按鈕的切換動畫(圓角正方形—>圓—>圓角正方形)。仍是那句話,計算好每一個組件的座標(半徑)事情就完成了一半,因此關鍵在於計算,這裏的圖形都簡單計算並不複雜,只是要稍微細心點。註釋都寫得比較詳細,就不一一解釋了,具體看代碼:import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.robot.common.frame.BaseApp;
import com.robot.common.utils.PixelUtil;
/**
* 視頻錄製控制、進度
*
* @author ly
* date 2020/3/3 11:02
*/
public class RecordProgressView extends View implements View.OnClickListener {
private static final float changeRatio = 5f;
//最小錄製時長標記的弧長
private static final int minPAngle = 2;
private OnRecordStatusListener onRecordStatusListener;
private Paint paint;
//內圈錄製、暫停按鈕顏色
private int circleInsideColor;
//進度圈顏色
private int circleOutsideColor;
//進度顏色
private int circleProgressColor;
private float progressW;
//進度條圓圈半徑
private float circleProgressR;
//內實心圓圈半徑
private float circleInsideR;
//錄製按鈕寬高
private float pauseRectW;
//動態變量內圈半徑(逐漸變小或變大)
private float curCircleInsideR;
private State state = State.READY;
//暫停按鈕rect
private RectF pauseRect;
private int pauseRadius;
private RectF progressRect;
private float progress;
private float minProgress = 90;
private float minCircleR;
private LinearGradient linearGradientCircle;
private LinearGradient linearGradientRect;
public enum State {
READY, RECORDING, PAUSE, FINISH
}
public RecordProgressView(Context context) {
super(context);
init();
}
public RecordProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public RecordProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
circleInsideColor = 0xffE9B763;
circleOutsideColor = 0x55ffffff;
circleProgressColor = 0xffffffff;
paint = new Paint();
paint.setAntiAlias(true);//抗鋸齒
pauseRadius = PixelUtil.dp2px(5);
setOnClickListener();
}
public float getProgress() {
return progress;
}
/**
* @param progress 已錄製時長/最大時長
* @author ly on 2020/3/3 16:41
*/
public void setProgress(float progress) {
this.progress = progress * 360;
if (this.progress >= 360) {//表示錄製達到最大時長,自動結束
this.progress = 360;
state = State.FINISH;
if (onRecordStatusListener != null)
onRecordStatusListener.onFinish();
}
invalidate();
}
/**
* @param minProgress 最小錄製時長/最大時長
* @author ly on 2020/3/3 16:41
*/
public void setMinProgress(float minProgress) {
this.minProgress = minProgress * 360;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (circleProgressR == 0) {
int w = getWidth();
progressW = w * 0.061f;
circleProgressR = (w - progressW) / 2f;
circleInsideR = (w * 0.83f) / 2;
pauseRectW = w * 0.37f;
curCircleInsideR = circleInsideR;
//矩形對角線的一半
// minCircleR = (float) (Math.sqrt(Math.pow(pauseRectW, 2) * 2) / 2f);
minCircleR = pauseRectW / 2;
pauseRect = new RectF(w / 2f - pauseRectW / 2
, w / 2f - pauseRectW / 2
, w / 2f + pauseRectW / 2
, w / 2f + pauseRectW / 2);
progressRect = new RectF(0 + progressW / 2, 0 + progressW / 2, w - progressW / 2, w - progressW / 2);
float offsetX = (float) Math.sqrt(Math.pow(curCircleInsideR, 2) / 2);
//圓的左上到右下方向漸變
linearGradientCircle = new LinearGradient(
curCircleInsideR - offsetX, curCircleInsideR - offsetX, w - offsetX, w - offsetX,
0xff7360FF, 0xff9663F3,
Shader.TileMode.MIRROR);
linearGradientRect = new LinearGradient(
pauseRect.left, pauseRect.top, pauseRect.right, pauseRect.bottom,
0xff7360FF, 0xff9663F3,
Shader.TileMode.MIRROR);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫外圈進度圈、進度
paint.setShader(null);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(circleOutsideColor);
paint.setStrokeWidth(progressW);//設置畫筆粗細
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, circleProgressR, paint);
if (minProgress - minPAngle > 0 && BaseApp.get().isDebug()) {
paint.setColor(circleProgressColor);
canvas.drawArc(progressRect, minProgress - 90, minPAngle, false, paint);
}
if (state != State.READY) {
//進度就是圓弧
//時鐘3點的方向爲0度,順時鐘方向爲正,-90是圓弧的開始點,即12點位置開始,
//sweepAngle掃過的角度,調整該值便可實現進度順時針加載(0-360)
paint.setColor(circleProgressColor);
canvas.drawArc(progressRect, -90, progress, false, paint);
}
//畫內部圓/暫停按鈕
paint.setColor(circleInsideColor);
paint.setStyle(Paint.Style.FILL);
paint.setShader(linearGradientCircle);
if (state == State.RECORDING) {//錄製中畫矩形和進度
if (curCircleInsideR > minCircleR) {
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, curCircleInsideR, paint);
changeCircleR(false);
} else {
paint.setShader(linearGradientRect);
canvas.drawRoundRect(pauseRect, pauseRadius, pauseRadius, paint);
}
} else {
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, curCircleInsideR, paint);
if (curCircleInsideR < circleInsideR)
changeCircleR(true);
}
}
public void resetState() {
state = State.READY;
progress = 0;
invalidate();
}
public void setState(State state) {
this.state = state;
invalidate();
}
public void setOnClickListener() {
setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (state) {
//由開始狀態變爲錄製狀態,點擊後由圓變爲矩形
case READY:
case PAUSE:
state = State.RECORDING;
if (onRecordStatusListener != null)
onRecordStatusListener.onRecording();
invalidate();
break;
//錄製中,點擊後由矩形變爲圓
case RECORDING:
if (progress < minProgress) {
state = State.PAUSE;
if (onRecordStatusListener != null)
onRecordStatusListener.onPause();
} else {
state = State.FINISH;
if (onRecordStatusListener != null)
onRecordStatusListener.onFinish();
}
invalidate();
break;
default:
break;
}
}
private void changeCircleR(boolean is2Big) {
if (is2Big) {//變大
curCircleInsideR += changeRatio;
if (curCircleInsideR >= circleInsideR)
curCircleInsideR = circleInsideR;
} else {//變小
curCircleInsideR -= changeRatio;
if (curCircleInsideR <= minCircleR)
curCircleInsideR = minCircleR;
}
invalidate();
}
public void setOnRecordStatusListener(OnRecordStatusListener onRecordStatusListener) {
this.onRecordStatusListener = onRecordStatusListener;
}
public interface OnRecordStatusListener {
void onRecording();
void onPause();
void onFinish();
}
}
複製代碼
若是對你們有幫助,請點個贊以鼓勵我前進~canvas