功能需求:
項目有個直播功能,需要顯示進度條類似錄制進度View具體如下
1. 可以控制頂部時間間隔,以及是否繪制,是否預留位置
2. 能夠重頭開始,從指定時間開始,恢復,暫停,停止等
實現代碼:
package com.pro.record.widgets;
import Java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.os.Handler;
import android.os.message;
import android.util.AttributeSet;
import android.view.View;
import com.pro.record.R;
import com.pro.record.utils.ProUtils;
import com.pro.record.utils.ScreenUtils;
/**
* 時間進度 自定義View
*/
public class RecordTimeView extends View{
/** 日誌Tag */
private final String TAG = "RecordTimeView";
/** View 寬度 */
private int vWidth = 0;
/** View 高度 */
private int vHeight = 0;
/** 進度圖片 寬度 */
private int bWidth = 0;
/** 進度圖片 高度 */
private int bHeight = 0;
/** 字體高度 */
private int tHeight = 0;
/** 進度圖片 */
private Bitmap sBitmap;
/** dip轉換px */
private int dip = 0;
/** 密度 */
private float mDensity;
// ====================
/** 時間畫筆 */
private Paint mTextPaint;
/** 線條畫筆 */
private Paint mLinePaint;
/** 背景進度畫筆 */
private Paint mStepPaint;
// ================== 構造函數 ===================
public RecordTimeView(Context mContext) {
super(mContext);
init();
}
public RecordTimeView(Context mContext, AttributeSet attrs) {
super(mContext, attrs);
init();
}
/**
* 初始化操作
*/
private void init(){
// 防止不進行繪畫 觸發onDraw
setWillNotDraw(false);
// 獲取進度圖片
sBitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_location);
// 屏幕密度
mDensity = ScreenUtils.getDensity(getContext());
// 1 dip 對應的px
dip = ScreenUtils.dipConvertPx(getContext(), 1f);
// 圖片寬高
bWidth = sBitmap.getWidth();
bHeight = sBitmap.getHeight();
// 初始化畫筆
initPaint();
}
/**
* 初始化畫筆
*/
private void initPaint(){
// 初始化畫筆
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 字體(時間)
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 頂部、底部 直線
mStepPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 半透明(畫布遮擋層)
// 畫筆顏色
mTextPaint.setColor(Color.rgb(148, 148, 148)); // 字體顏色
mLinePaint.setColor(Color.rgb(84, 84, 84)); // 線條顏色 #545454
mStepPaint.setColor(Color.rgb(148, 148, 148)); // 背景進度顏色(畫布遮擋層) #949494
// 設置透明度
mStepPaint.setAlpha(50); // 畫布遮擋層
mLinePaint.setStrokeWidth(dip * 2f); // 線條高度
// 設置畫筆樣式
mLinePaint.setStyle(Paint.Style.STROKE); // 設置粗線 - 線條
// 設置字體大小
setTextSize(14f, false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 防止獲取寬度高度失敗
if (vWidth == 0 || vHeight == 0){
vWidth = getWidth();
vHeight = getHeight();
} else {
// 獲取字體間隔高度 + 6dip
int tSpaceHeight = tHeight + dip * 6;
// 需要繪制文本
if (isDrawText){
// 畫頂部字體
for (int i = axisStep - 1; i < vWidth + axisStep - 1; i++) {
if (i % (timeInterval) == 0) { // 顯示時間軸的寬度和文字的距離
if (i != 0){ // 防止從指定時間開始,當前時間會在最前面(x = 負數的位置, 如果有需求可以把if註釋) - 該if 確保前面有 timeIntervalS 的間隔
float time = (i * refTime) + cTime; // 顯示的時間
// 繪制的文本(正常循環5s,10s,15s....,55s,1m,5s..2m,5s...)
String sText = ProUtils.secToTimeSecondRetain((int)(time / 1000));
// 完整的時間(1m:50s, ....非秒循環)
//sText = ProUtils.secToTimeRetain((int)(time / 1000));
// 繪制時間
canvas.drawText(sText, (i) - axisStep - 10, tHeight, mTextPaint);
}
}
}
} else if (!isKeepText){ // 不需要繪制文本 => 不保留文本位置
tSpaceHeight = 0;
}
// 設置頂部線條位置(繪制的高度,開始位置)
int tLineY = tSpaceHeight;
// 計算底部線位置 - View高度 - 圖片高度 - 繪制線條的高度
int bLineY = vHeight - bHeight - (dip * 2);
// 畫頂部線
canvas.drawLine(0, tLineY, vWidth, tLineY, mLinePaint);
// 畫底部一條線
canvas.drawLine(0, bLineY, vWidth, bLineY, mLinePaint);
// 繪制中間進度
canvas.drawRect(0, tSpaceHeight, moveStep, bLineY, mStepPaint);
// 設置進度圖片(坐標進度圖片)
canvas.drawBitmap(sBitmap, moveStep - (bWidth / 2), vHeight - bHeight, mLinePaint);
}
}
// ==
/** 是否繪制時間文本 */
private boolean isDrawText = true;
/** 是否保留繪制時間文本的位置(不需要繪制時間文本時,可以控制是否保留位置) */
private boolean isKeepText = true;
/** 進度圖片移動位置 */
private float moveStep;
/** 時間軸步數 */
private int axisStep;
/** 字體大小 */
private float textSize = 13f;
/**
* 設置字體大小
* @param textSize
*/
public void setTextSize(float textSize){
setTextSize(textSize, true);
}
/**
* 設置字體大小
* @param textSize
* @param isRef 是否刷新View
*/
private void setTextSize(float textSize, boolean isRef){
this.textSize = textSize;
// --
// 設置畫筆大小
mTextPaint.setTextSize(textSize * mDensity); // 字體大小
// 獲取字體度量(用於計算字體高度)
FontMetrics fMetrics = mTextPaint.getFontMetrics();
// 獲取字體高度(取整)
//tHeight = (int) Math.ceil((double)(fMetrics.bottom - fMetrics.top));
// 獲取字體高度(取整) -- 無邊距
tHeight = (int) Math.ceil((double)(fMetrics.descent - fMetrics.ascent));
// 判斷是否刷新
if (isRef){
postInvalidate();
}
}
/**
* 是否保留繪制文本的位置
* @param keepText
*/
public void setKeepText(boolean keepText) {
isKeepText = keepText;
// --
if (!isKeepText){
isDrawText = false;
}
}
/**
* 是否繪制文本
* @param drawText
*/
public void setDrawText(boolean drawText) {
isDrawText = drawText;
}
/** 時間進度還沒到中間位置, moveToMidStep進行疊加, 如果到達屏幕中間位置axisStep進行疊加 */
private void changeMoveAxisParams() {
if (moveStep <= vWidth / 2) {
moveStep = moveStep + 1; //移動的速度
} else {
axisStep = axisStep + 1;
}
}
// =============== 回調事件 =================
/** 錄制時間回調 */
private RecordTimeCallBack rtCallBack;
/** 錄制時間回調 */
public interface RecordTimeCallBack {
/**
* 每秒觸發
* @param dTime 當前時間
*/
public void preSecond(float dTime);
/**
* 開始時觸發 - (恢復、開始等)
* @param dTime 當前時間
*/
public void start(float dTime);
}
/**
* 設置錄制時間回調
* @param rtCallBack
*/
public void setRecordTimeCallBack(RecordTimeCallBack rtCallBack) {
this.rtCallBack = rtCallBack;
}
// ============== 內部控制代碼 ===============
/** 專門刷新View */
private Handler vhandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case 0: // 正常進行繪制觸發
postInvalidate();
break;
case 1: // 滿一秒進行觸發
displayTime = displayTime + 1000; // 累積時間
if(rtCallBack != null){
rtCallBack.preSecond(displayTime);
}
break;
}
}
};
/** 設備連接定時器 */
private Timer refTimer;
/** 設備連接定時器任務棧 */
private TimerTask refTask;
/** 當前時間 */
private float cTime = 0l;
/** 整秒統計 */
private int iTime = 0;
/** 刷新時間(毫秒) */
private int refTime = 20;
/** 刷新頻率 1000 / 刷新時間 */
private int refRate = 1000 / refTime;
/** 時間間隔(秒) */
private int timeIntervalS = 5000; // (5s,10s,15s) - 可以動態修改
/** 時間間隔 */
private int timeInterval = timeIntervalS / refTime;
/** 對外獲取時間 */
private float displayTime = 0l;
/**
* 設置定時器,刷新View
* @param isOpen 是否打開
*/
private void setTimer(boolean isOpen) {
if (isOpen) {
try {
if (refTimer != null) {
refTimer.cancel();
refTimer = null;
}
if (refTask != null) {
refTask.cancel();
refTask = null;
}
} catch (Exception e) {
}
// 開啟定時器
refTimer = new Timer(); // 每次重新new 防止被取消
// 重新生成定時器 防止出現TimerTask is scheduled already 所以同一個定時器任務只能被放置一次
refTask = new TimerTask() {
@Override
public void run() {
changeMoveAxisParams();
vhandler.sendEmptyMessage(0);
++ iTime;
if(iTime >= refRate){
vhandler.sendEmptyMessage(1);
iTime = 0; // 滿1秒
}
}
};
// xx秒後執行,每隔xx秒再執行一次
refTimer.schedule(refTask, 0, refTime); // 開啟定時器
} else {
try {
if (refTimer != null) {
refTimer.cancel();
refTimer = null;
}
if (refTask != null) {
refTask.cancel();
refTask = null;
}
} catch (Exception e) {
}
vhandler.sendEmptyMessage(0);
}
}
// =================== 對外公開方法 =====================
/** 開始 */
public void start(){
cTime = 0;
displayTime = 0;
iTime = 0;
moveStep = 0f;
axisStep = 1;
setTimer(true);
// --
if(rtCallBack != null){
rtCallBack.start(displayTime);
}
}
/**
* 開始
* @param time 開始時間(單位毫秒)
*/
public void start(float time){
cTime = time;
displayTime = time;
iTime = 0;
moveStep = 0f;
axisStep = 1;
setTimer(true);
// --
if(rtCallBack != null){
rtCallBack.start(displayTime);
}
}
/** 停止 */
public void stop(){
cTime = 0;
displayTime = 0;
iTime = 0;
moveStep = 0f;
axisStep = 1;
// --
setTimer(false);
}
/** 暫停 */
public void pause(){
setTimer(false);
}
/** 恢復 */
public void recover(){
setTimer(true);
// --
if(rtCallBack != null){
rtCallBack.start(displayTime);
}
}
// ======================================
/**
* 獲取時間,單位毫秒
* @return
*/
public float getTime(){
return displayTime;
}
/**
* 獲取時間,單位秒數
* @return
*/
public int getTimes(){
return (int)(displayTime / 1000);
}
}
使用方法:
/** 時間進度View */
private RecordTimeView am_rtview;
am_rtview.setKeepText(false); // 是否保留繪制文本的位置 - 如果進行屬於進行繪制,則無視該參數
am_rtview.setDrawText(true); // 是否繪制文本
am_rtview.setTextSize(14.0f); // 設置文本大小 - 默認13f
am_rtview.start(); // 開始
//am_rtview.start(20 * 1000f); // 從指定時間開始
am_rtview.stop();// 停止錄制
am_rtview.pause(); // 暫停
am_rtview.recover(); // 恢復
am_rtview.getTime(); // 獲取時間,單位毫秒
am_rtview.getTimes(); // 獲取時間,單位秒數
/** 設置時間觸發回調 */
am_rtview.setRecordTimeCallBack(new RecordTimeView.RecordTimeCallBack() {
@Override
public void preSecond(float dTime) { // 滿一秒觸發
}
@Override
public void start(float dTime) { // 開始、恢復時觸發
}
});
代碼下載
Android - 錄制進步式View
Tags: Android private package public import
文章來源: