1. 程式人生 > >Android-自定義View-自定義進度條

Android-自定義View-自定義進度條

眼看6月到了,由於前段時間域名備案等原因,伺服器關閉了差不多一個月,所以沒更新文章,索性今天補一篇吧,準備寫一個簡單的自定義View,就拿進度條做這個需求吧,雖然簡單,但是也包含了基本自定義View的幾要素,比如自定義屬性、重寫測量、重寫繪製等功能。

需求分析:

  • 1.進度通過繪製線條實現。
  • 2.進度文字跟隨當前進度實時變化,並非一直顯示在固定位置。
  • 3.控制元件未給出寬高屬性時,我們需要給出預設值,具體以文字大小而定。
  • 4.為了滿足開發需求,自定義屬性要多,能夠最大程度的讓開發者控制View的某個屬性。

效果圖:

效果圖

開始擼碼:

新建類繼承View,並實現構造方法:

public class ProgressBarView extends View {

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

    public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProgressBarView(Context context, @Nullable AttributeSet attrs, int
defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } }

新建attrs.xml,並編寫自定義屬性:

    <declare-styleable name="ProgressBarView">
        <attr name="progress_left_color" format="color" />
        <attr name="progress_center_color" format="color" />
<attr name="progress_right_color" format="color" /> <attr name="progress_left_height" format="dimension" /> <attr name="progress_right_height" format="dimension" /> <attr name="progress_text_size" format="dimension" /> <attr name="progress_current_progress" format="integer" /> </declare-styleable>

宣告變數,並接收自定義屬性的值(記得初始化方法在構造中呼叫):

    /**
     * 畫筆
     */
    private Paint mPaint;
    /**
     * 預設進度值
     */
    private int mProgress = 0;
    /**
     * 進度邊距
     */
    private int mProgressPadding = dp2px(2);
    /**
     * 控制元件高度
     */
    private int mHeight = 0;

    /**
     * 字型大小
     */
    float mTextSize = 18f;

    /**
     * 左邊進度條顏色
     */
    int mLeftColor = Color.RED;
    /**
     * 中間文字條顏色
     */
    int mCenterColor = Color.RED;
    /**
     * 右邊進度條顏色
     */
    int mRightColor = Color.RED;
    /**
     * 左邊進度條高度
     */
    int mLeftHeight = dp2px(10);
    /**
     * 右邊進度條高度
     */
    int mRightHeight = dp2px(10);



     /**
     * 初始化
     */
    private void init(AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗鋸齒
        mPaint.setColor(Color.RED);//畫筆顏色
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
        mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
        mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
        mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
        mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
        mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
        mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
        ta.recycle();
        mPaint.setTextSize(mTextSize);//設定文字大小,後續好測量文字高度
    }

重寫 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法測量自身:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);//預設使用者需要給出明確值,所以不判斷模式
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);//設定了測量值後,可以獲取測量值。
    }

    /**
     * 測量高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//得到測量模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {//使用者給了精確值
            result = size;
        } else { //MeasureSpec.UNSPECIFIED  MeasureSpec.AT_MOST 未指定明確引數
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
            result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等於進度條高度和文字高度中最高的為準,並且加上padding值
            if (mode == MeasureSpec.AT_MOST) {//給定了最大值
                result = Math.min(result, size);
            }
        }
        return result;
    }

重寫 protected void onDraw(Canvas canvas)並實現自定義檢視繪製:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mHeight = getMeasuredHeight();
        String str = mProgress + "%";
        float textWidth = mPaint.measureText(str);//文字寬度
        float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控制元件寬度-文字寬度-padding=進度條總寬度
        float currentProgress = getPaddingLeft() + width * mProgress / 100;//當前進度應該繪製的位置
        mPaint.setColor(mLeftColor);
        mPaint.setStrokeWidth(mLeftHeight);//畫筆寬度
        canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
        int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
        mPaint.setColor(mCenterColor);
        canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
        mPaint.setColor(mRightColor);
        mPaint.setStrokeWidth(mRightHeight);
        canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
    }

在佈局中引用編寫的控制元件,並設定自定義屬性:

    <view.peakchao.view.view.ProgressBarView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        app:progress_center_color="#ff00ff"
        app:progress_current_progress="80"
        app:progress_left_color="#f00"
        app:progress_left_height="1dp"
        app:progress_right_color="#0f0"
        app:progress_right_height="2dp"
        app:progress_text_size="30dp" />

至此控制元件已經能夠正常顯示了,但是由於目前只是寫個Demo,還有沒考慮的東西,比如各個屬性的get和set方法等,需要自己根據需求完善。

附上ProgressBarView完整程式碼:

/**
 * Created by Chao  2018/6/1 on 17:49
 * description
 */

public class ProgressBarView extends View {
    /**
     * 畫筆
     */
    private Paint mPaint;
    /**
     * 預設進度值
     */
    private int mProgress = 0;
    /**
     * 進度邊距
     */
    private int mProgressPadding = dp2px(2);
    /**
     * 控制元件高度
     */
    private int mHeight = 0;

    /**
     * 字型大小
     */
    float mTextSize = 18f;

    /**
     * 左邊進度條顏色
     */
    int mLeftColor = Color.RED;
    /**
     * 中間文字條顏色
     */
    int mCenterColor = Color.RED;
    /**
     * 右邊進度條顏色
     */
    int mRightColor = Color.RED;
    /**
     * 左邊進度條高度
     */
    int mLeftHeight = dp2px(10);
    /**
     * 右邊進度條高度
     */
    int mRightHeight = dp2px(10);


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

    public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * 初始化
     */
    private void init(AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗鋸齒
        mPaint.setColor(Color.RED);//畫筆顏色
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
        mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
        mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
        mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
        mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
        mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
        mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
        ta.recycle();
        mPaint.setTextSize(mTextSize);//設定文字大小,後續好測量文字高度
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);//得到測量模式
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);//預設使用者需要給出明確值,所以不判斷模式
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);//設定了測量值後,可以獲取測量值。
        //mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

    /**
     * 測量高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//得到測量模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {//使用者給了精確值
            result = size;
        } else { //MeasureSpec.UNSPECIFIED  MeasureSpec.AT_MOST 未指定明確引數
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
            result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等於進度條高度和文字高度中最高的為準,並且加上padding值
            if (mode == MeasureSpec.AT_MOST) {//給定了最大值
                result = Math.min(result, size);
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mHeight = getMeasuredHeight();
        String str = mProgress + "%";
        float textWidth = mPaint.measureText(str);//文字寬度
        float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控制元件寬度-文字寬度-padding=進度條總寬度
        float currentProgress = getPaddingLeft() + width * mProgress / 100;//當前進度應該繪製的位置
        mPaint.setColor(mLeftColor);
        mPaint.setStrokeWidth(mLeftHeight);//畫筆寬度
        canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
        int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
        mPaint.setColor(mCenterColor);
        canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
        mPaint.setColor(mRightColor);
        mPaint.setStrokeWidth(mRightHeight);
        canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
    }

    private int dp2px(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, val, getResources().getDisplayMetrics());
    }

    private int sp2px(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, val, getResources().getDisplayMetrics());
    }
}