Android - 錄制進步式View

分類:編程 時間:2017-02-02

功能需求:


項目有個直播功能,需要顯示進度條類似錄制進度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

文章來源:


ads
ads

相關文章
ads

相關文章

ad