Android_自定義控件之喜馬拉雅6.6.21.3播放進度條

1、前言

補一篇文章,github早已提交,文章遲遲未寫,今天就奉上。
前段時間 Android開發正規羣(324345614)有個羣友在問怎麼實現喜馬拉雅6.6.21.3版本的播放進度條,網上搜了一圈並沒有人寫過類似的,羣裏其他人也沒有人想幫他寫一個,自告奮勇,開始擼一個。

2、編寫代碼

java爲例子,kotlin代碼在github 項目中有,文章後面會給出github地址

2.1、編寫思路:

  • 繪製前測量
  • 背景條繪製
  • 緩衝條繪製
  • 播放進度框背景繪製
  • 播放進度文字繪製
  • 進度框計算移動過程的中心點
  • 觸摸調整進度

2.2、開始寫代碼了

在這裏插入圖片描述
這裏涉及到一點,進度框的寬度是比較長的。不能像那些小圓點進度那樣可以忽略不計,
所以在播放過程中,這個進度框的中心點會一直往右移,起點的計算就尤其重要了。
起點計算:應該是要根據當前的進度和總進度還有總寬度計算出距左邊的距離,這個距離得出來的還不是最終的,因爲進度框有很大寬度的,不然會在播放結束後跑出右邊。所以還要根據當前進度佔比總進度得出的比例算出進度框佔比大小,然後再用前面計算得到的距離減去這個佔比
終點計算:起點+進度框寬度

float textBgStartX = (float) progress * (progressBarRealWidth) / maxProgress - (float) textBgWidth / maxProgress * progress + dia;
        float textBgEndX = textBgStartX + textBgWidth;
        Log.e(TAG, "width " + width + ", textBgWidth " + textBgWidth + ", textBgStartX " + textBgStartX);
        canvas.drawRoundRect(textBgStartX
                , (height >> 1) - (textBgHeight >> 1)
                , textBgEndX
                , (height >> 1) + (textBgHeight >> 1)
                , textBgWidth >> 1 //x半徑
                , textBgWidth >> 1 //y半徑
                , textBgPaint);

CustomSeekBar.java完整代碼

package com.kincai.customseekbar;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.ColorRes;
import androidx.core.content.ContextCompat;

import java.util.Locale;

/**
 * Created by KINCAI
 * <p>
 * Desc 自定義SeekBar 資源配置屬性暫未實現
 * <p>
 * Date 2019-10-28 16:38
 */
public class CustomSeekBar extends View {
    private final String TAG = this.getClass().getSimpleName();
    private int seekBarDefaultWidth;
    private int textBgHeight;
    private int textBgWidth;
    private int cacheProgressBarHeight;
    private int progressBarHeight;
    private int cacheProgressBarColor;
    private int progressBarColor;
    private int textColor;
    private int textBgColor;
    private int maxProgress;
    private int progress;
    private int cacheProgress;
    private int minCircleRadius;
    private int textSize;

    private Paint cacheProgressBarPaint = new Paint();
    private Paint progressBarPaint = new Paint();
    private Paint textPaint = new Paint();
    private Paint textBgPaint = new Paint();
    /**小圓點直徑*/
    private float dia;
    private int textHeight;

    public CustomSeekBar(Context context) {
        this(context, null);
    }

    public CustomSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 默認寬度爲屏幕寬
        Resources resources = this.getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        seekBarDefaultWidth = dm.widthPixels;
        textBgWidth = dp2px(200);
        textBgHeight = dp2px(16);
        cacheProgressBarHeight = dp2px(1.5f);
        progressBarHeight = dp2px(1f);
        setCircleParam();
        textSize = sp2px(10);

        progressBarColor = Color.parseColor("#d9ead3");
        cacheProgressBarColor = Color.parseColor("#ffffff");
        textColor = Color.parseColor("#6e6e6e");
        textBgColor = Color.parseColor("#ffffff");

        progressBarPaint.setColor(progressBarColor);
        progressBarPaint.setAntiAlias(false);
        progressBarPaint.setStyle(Paint.Style.FILL);
        progressBarPaint.setStrokeCap(Paint.Cap.ROUND);

        cacheProgressBarPaint.setColor(cacheProgressBarColor);
        cacheProgressBarPaint.setAntiAlias(false);
        cacheProgressBarPaint.setStyle(Paint.Style.FILL);

        textBgPaint.setColor(textBgColor);
        textBgPaint.setAntiAlias(true);
        textBgPaint.setStyle(Paint.Style.FILL);

        textPaint.setColor(textColor);
        textPaint.setAntiAlias(true);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setTypeface(Typeface.DEFAULT);
        textPaint.setTextSize(textSize);
        textHeight = getTextHeight(textPaint, "00:00/00:00");
        textBgHeight = textHeight + dp2px(8);
    }

    private void setCircleParam() {
        minCircleRadius = progressBarHeight;
        dia = minCircleRadius * 2;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //測量寬高
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = seekBarDefaultWidth;
            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(width, widthSize);
            }
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            //當wrap_content的時候 高度爲textBgHeight
            // 或者是所有部分的最高那個高度
            height = textBgHeight;
            if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(height, heightSize);
            }
        }
        setMeasuredDimension(width, height);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int height = getHeight();
        int width = getWidth();
        float progressBarRealWidth = width - dia * 2;
        Log.e(TAG, "width:" + width + " realWidth:" + progressBarRealWidth);
        // 雞巴 兩頭還有兩個圓點
        canvas.drawCircle(minCircleRadius, height >> 1, minCircleRadius, cacheProgressBarPaint);
        canvas.drawCircle(width - minCircleRadius, height >> 1, minCircleRadius, progressBarPaint);
        // 畫背景條
        canvas.drawRect(dia
                , (height >> 1) - (progressBarHeight >> 1)
                , width - dia
                , (height >> 1) + (progressBarHeight >> 1)
                , progressBarPaint);
        // 畫緩存條
        float currentCache = (float) cacheProgress * progressBarRealWidth / maxProgress + dia;
        canvas.drawRect(dia
                , (height >> 1) - (cacheProgressBarHeight >> 1)
                , currentCache, (height >> 1) + (cacheProgressBarHeight >> 1)
                , cacheProgressBarPaint);

        // 判斷界限
        if (progress < 0) {
            progress = 0;
        }
        if (progress > maxProgress) {
            progress = maxProgress;
        }
        // 計算文字,文字長度可能會變 會不斷的測量
        String progressText = getProgressText();
        int textWidth = getTextWidth(textPaint, progressText);
        // 畫文字背景 背景寬高隨文字變化而變化
        textBgWidth = textWidth + dp2px(12);
        float textBgStartX = (float) progress * (progressBarRealWidth) / maxProgress - (float) textBgWidth / maxProgress * progress + dia;
        float textBgEndX = textBgStartX + textBgWidth;
        Log.e(TAG, "width " + width + ", textBgWidth " + textBgWidth + ", textBgStartX " + textBgStartX);
        canvas.drawRoundRect(textBgStartX
                , (height >> 1) - (textBgHeight >> 1)
                , textBgEndX
                , (height >> 1) + (textBgHeight >> 1)
                , textBgWidth >> 1 //x半徑
                , textBgWidth >> 1 //y半徑
                , textBgPaint);
        //畫文字
        //計算起點
        float textStartY = (height >> 1) + (textHeight >> 1);
        float textStartX = textBgStartX + ((textBgWidth >> 1) - (textWidth >> 1));
        canvas.drawText(progressText, textStartX, textStartY, textPaint);

    }

    private int getTextWidth(Paint paint, String str) {
        Rect rect = new Rect();
        paint.getTextBounds(str, 0, str.length(), rect);
        return rect.width();
    }

    private int getTextHeight(Paint paint, String str) {
        Rect rect = new Rect();
        paint.getTextBounds(str, 0, str.length(), rect);
        return rect.height();
    }

    private String getProgressText() {
        String progressText = formatProgress(this.progress);
        String maxProgressText = formatProgress(this.maxProgress);
        return progressText + "/" + maxProgressText;
    }

    /**
     * @param seconds 進度(秒)
     * @return
     */
    private String formatProgress(int seconds) {
        String standardTime;
        if (seconds <= 0) {
            standardTime = "00:00";
        } else if (seconds < 60) {
            standardTime = String.format(Locale.getDefault(), "00:%02d", seconds % 60);
        } else if (seconds < 3600) {
            standardTime = String.format(Locale.getDefault(), "%02d:%02d", seconds / 60, seconds % 60);
        } else {
            standardTime = String.format(Locale.getDefault(), "%02d:%02d:%02d", seconds / 3600, seconds % 3600 / 60, seconds % 60);
        }
        return standardTime;
    }


    public void setMaxProgress(int maxProgress) {
        this.maxProgress = maxProgress;
    }

    /**
     * @param progress 進度(秒)
     */
    public void cacheProgress(int progress) {
        this.cacheProgress = progress;
        postInvalidate();
    }

    /**
     * @param progress 進度(秒)
     */
    public void progress(int progress) {
        if (mDrag) {
            return;
        }
        this.progress = progress;
        postInvalidate();
    }

    /**
     * @param cacheProgressBarHeight 緩存條高度 單位dp
     */
    public void setCacheProgressBarHeight(float cacheProgressBarHeight) {
        this.cacheProgressBarHeight = dp2px(cacheProgressBarHeight);
    }

    /**
     * @param progressBarHeight 進度條高度 單位dp
     */
    public void setProgressBarHeight(float progressBarHeight) {
        this.progressBarHeight = dp2px(progressBarHeight);
        setCircleParam();
    }

    public void setCacheProgressBarColor(@ColorRes int cacheProgressBarColor) {
        this.cacheProgressBarColor = ContextCompat.getColor(getContext(), cacheProgressBarColor);
        cacheProgressBarPaint.setColor(this.cacheProgressBarColor);
    }

    public void setProgressBarColor(@ColorRes int progressBarColor) {
        this.progressBarColor = ContextCompat.getColor(getContext(), progressBarColor);
        progressBarPaint.setColor(this.progressBarColor);
    }

    public void setTextColor(@ColorRes int textColor) {
        this.textColor = ContextCompat.getColor(getContext(), textColor);
        textPaint.setColor(this.textColor);
    }

    public void setTextBgColor(@ColorRes int textBgColor) {
        this.textBgColor = ContextCompat.getColor(getContext(), textBgColor);
        textBgPaint.setColor(this.textBgColor);
    }

    /**
     * @param textSize 文字大小 單位sp
     */
    public void setTextSize(int textSize) {
        this.textSize = sp2px(textSize);
    }

    private boolean mDrag;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDrag = true;
                break;
            case MotionEvent.ACTION_MOVE:
                touchUpdate(event.getX());
                break;
            case MotionEvent.ACTION_UP:
                mDrag = false;
                touchUpdate(event.getX());
                if (iProgressListener != null) {
                    iProgressListener.progress(progress);
                }
                break;
        }
        return true;
    }

    private void touchUpdate(float x) {
        progress = (int) (x * maxProgress / getWidth());
        postInvalidate();
    }

    IProgressListener iProgressListener;

    public void setProgressListener(IProgressListener iProgressListener) {
        this.iProgressListener = iProgressListener;
    }

    public interface IProgressListener {
        void progress(int progress);
    }

    private int dp2px(float var1) {
        float var2 = getContext().getResources().getDisplayMetrics().density;
        return (int) (var1 * var2 + 0.5F);
    }

    private int sp2px(float var1) {
        float var2 = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (var1 * var2 + 0.5F);
    }
}


模擬播放
佈局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#8B5A2B"
    tools:context=".MainActivity">


    <com.kincai.customseekbar.CustomSeekBar
        android:id="@+id/seekbar"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_centerInParent="true"
        android:layout_height="wrap_content" />
  

</RelativeLayout>

MainActivity.java

package com.kincai.customseekbar;

import android.os.Bundle;
import android.util.Log;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    int cacheProgress = 0;
    int progress;
    int maxProgress = 60;
    private CustomSeekBar customSeekBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.seekbar);
        customSeekBar.setMaxProgress(maxProgress);//最大進度s
        customSeekBar.setProgressBarHeight(1.0f);//進度條高度dp 默認1.0f
        customSeekBar.setCacheProgressBarHeight(1.5f);//緩存條高度dp 默認1.5f
        customSeekBar.setProgressBarColor(android.R.color.darker_gray);//進度條顏色colorId
        customSeekBar.setCacheProgressBarColor(android.R.color.white);//緩存條顏色colorId
        customSeekBar.setTextBgColor(android.R.color.white);//文字背景顏色colorId
        customSeekBar.setTextColor(android.R.color.black);//字體顏色colorId
        customSeekBar.setTextSize(10);//文字大小sp 默認10sp
        // 設置進度拖動監聽
        // 手動拖動進度條會返回當前進度
        customSeekBar.setProgressListener(new CustomSeekBar.IProgressListener() {
            @Override
            public void progress(int progress) {
                MainActivity.this.progress = progress;
            }
        });
        start();

    }

    private void start() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        if (cacheProgress < maxProgress) {
                            cacheProgress += 4;
                            customSeekBar.cacheProgress(cacheProgress);
                        }
                        customSeekBar.progress(progress);
                        if (progress >= maxProgress) {
                            break;
                        }
                        progress += 1;
                        Log.e("ss", "ss" + progress);

                        Thread.sleep(1000);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }
}

3、總結

demo地址 :https://github.com/hiongyend/CustomSeekBar

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章