帶文本的進度條實現(xiàn)方案

前言

在日常開發(fā)中,我們經(jīng)常需要遇到這么一個場景下載,而下載的動態(tài)顯示很多,比如我上文介紹過的仿魅族應(yīng)用商店下載進度控件

但是可能更多的是下文這種展示UI:


未開始下載前
下載中

接下來我們看下要怎么去實現(xiàn)它。

思路

畫圓角矩形的邊
畫中間的進度
畫String文本以及圖標

讓我們依次來解決吧!

新建ProgressTextView

我們是基于文本的,所以這里我建議直接繼承TextView,通過拓展 TextView的額外屬性進行實現(xiàn)。

public class ProgressTextView extends android.support.v7.widget.AppCompatTextView {

    private int mBorderWidth = 4;
    private int mBorderWidthColor = Color.parseColor("#FFe12f");
    private int mCorners;//我們默認以px為單位
    private Paint mCornerPaint, mProgressPaint; //邊框畫筆   文字我們只用系統(tǒng)的 TextView
    private int mProgress = 0;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCorners = h  / 4;
    }


    public ProgressTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

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

    public void init(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressTextView);
        mProgress = a.getInteger(R.styleable.ProgressTextView_progress, 0);
        a.recycle();

        mCornerPaint = new Paint();
        mProgressPaint = new Paint();
        mCornerPaint.setAntiAlias(true);
        mCornerPaint.setDither(true);
        mCornerPaint.setStrokeWidth(mBorderWidth);
        mCornerPaint.setStyle(Paint.Style.STROKE); //實心 只畫邊框也畫心
        mCornerPaint.setColor(mBorderWidthColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        doDrawBorder(canvas);
        doDrawProgress(canvas);
        doDrawLeft(canvas);
        super.onDraw(canvas);
    }

這樣我們一個基本的樣子已經(jīng)搭起來了,因為我們是繼承了TextView,所以我們并不需要去實現(xiàn)額外的測量,定位方法,可以看到我們在ondraw(Canvas canvas)里面分別實現(xiàn)了doDrawBorder(canvas),doDrawProgress(canvas)doDrawLeft(canvas)方法;
可以發(fā)現(xiàn)他們的調(diào)用順序是在super之前的,所以繼承自textView的setText(String str)會發(fā)生在我們繪制之后,不會發(fā)生被我們的后續(xù)繪制影響了文字的情況。

畫圓角矩形的邊

    /**
     * 畫邊框
     *
     * @param canvas
     */
    private void doDrawBorder(Canvas canvas) {
        RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, getMeasuredWidth() - mBorderWidth, getMeasuredHeight() - mBorderWidth);
        canvas.drawRoundRect(rectF,  Kits.Dimens.dpToPxInt(getContext(), mCorners),  Kits.Dimens.dpToPxInt(getContext(), mCorners), mCornerPaint);
    }

這里簡單畫了一個圓角矩形,需要注意的是

public void drawRoundRect (RectF rect, float rx, float ry, Paint paint)

其中的rx,ry,分別對應(yīng)的意義是x,y上的切角。

這里我們在一開始定義為了高度的1/4

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCorners = h  / 4;
    }

畫中間的進度

 /**
     * 畫進度條
     *
     * @param canvas
     */
    private void doDrawProgress(Canvas canvas) {
        canvas.save();
        if (mProgress < 100) {
            LinearGradient linearGradient = new LinearGradient(0, 0, (getMeasuredWidth() - mBorderWidth) * (mProgress / 100f), 0,
                    colors,
                    null, Shader.TileMode.REPEAT);
            mProgressPaint.setShader(linearGradient);
        } else {
            mProgressPaint.setColor(Color.parseColor("#FFe12f"));
        }
        RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, (getMeasuredWidth() - mBorderWidth), getMeasuredHeight() - mBorderWidth);
        RectF rectFClipRect = new RectF(mBorderWidth / 2, mBorderWidth / 2, (getMeasuredWidth() - mBorderWidth) * (mProgress / 100f), getMeasuredHeight() - mBorderWidth);
        canvas.clipRect(rectFClipRect);
        canvas.drawRoundRect(rectF, Kits.Dimens.dpToPxInt(getContext(), mCorners), Kits.Dimens.dpToPxInt(getContext(), mCorners), mProgressPaint);
        canvas.restore();
    }

因為設(shè)計師需要在未完成的時候,進度條的顏色為漸變色,所以我們加了一個
mProgress < 100的判斷。
這里進行了切canvas的操作clipRect,用于顯示不同的進度顏色。

畫String文本以及圖標

因為我們是繼承TextView的,所以不需要去管什么,但是實際上會發(fā)現(xiàn),我們把canvas進行一下位移,使用效果會更佳

 /**
     * 把drawableLeft換到屏幕中間 缺點:gravity要設(shè)置為center_vertical
     *
     * @param canvas
     */
    private void doDrawLeft(Canvas canvas) {
        Drawable[] drawables = getCompoundDrawables();
        Drawable drawableLeft = drawables[0];
        if (drawableLeft != null) {
            float textWidth = getPaint().measureText(getText().toString());
            int drawablePadding = getCompoundDrawablePadding();
            int drawableWidth;
            drawableWidth = drawableLeft.getIntrinsicWidth();
            float bodyWidth = textWidth + drawableWidth + drawablePadding;
            canvas.translate((getWidth() - bodyWidth) / 2, 0);
        }
    }

總結(jié)

收獲

  1. 通過上面的操作(雖然并不是很穩(wěn)),了解了一下基本的使用,也是得以應(yīng)用于實際開發(fā)中。
  2. 沒了

另外的實現(xiàn)方法

  1. 組合View
  2. 同理,xml代碼布局組合

代碼如下

package com.playingjoy.fanrabbit.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;

import com.playingjoy.fanrabbit.R;

import cn.droidlover.xdroidmvp.kit.Kits;
import cn.droidlover.xdroidmvp.log.XLog;

/**
 * Author: Ly
 * Data:2018/3/29-16:25
 * Description:
 */
public class ProgressTextView extends android.support.v7.widget.AppCompatTextView {

    private int mBorderWidth = 4;
    private int mBorderWidthColor = Color.parseColor("#FFe12f");
    private int mCorners;//我們默認以px為單位
    private Paint mCornerPaint, mProgressPaint; //邊框畫筆   文字我們只用系統(tǒng)的 TextView
    private int mProgress = 0;


    private int[] colors = new int[]{0xffFFe12f, 0x59FFe12f};

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCorners = h / 4;
    }


    public ProgressTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

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

    public void init(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressTextView);
        mProgress = a.getInteger(R.styleable.ProgressTextView_progress, 0);
        a.recycle();

        mCornerPaint = new Paint();
        mProgressPaint = new Paint();
        mCornerPaint.setAntiAlias(true);
        mCornerPaint.setDither(true);
        mCornerPaint.setStrokeWidth(mBorderWidth);
        mCornerPaint.setStyle(Paint.Style.STROKE); //實心 只畫邊框也畫心
        mCornerPaint.setColor(mBorderWidthColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        doDrawBorder(canvas);
        doDrawProgress(canvas);
        doDrawLeft(canvas);
        super.onDraw(canvas);
    }

    /**
     * 畫邊框
     *
     * @param canvas
     */
    private void doDrawBorder(Canvas canvas) {
        RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, getMeasuredWidth() - mBorderWidth, getMeasuredHeight() - mBorderWidth);
        canvas.drawRoundRect(rectF, Kits.Dimens.dpToPxInt(getContext(), mCorners), Kits.Dimens.dpToPxInt(getContext(), mCorners), mCornerPaint);
    }

    /**
     * 畫進度條
     *
     * @param canvas
     */
    private void doDrawProgress(Canvas canvas) {
        canvas.save();
        if (mProgress < 100) {
            LinearGradient linearGradient = new LinearGradient(0, 0, (getMeasuredWidth() - mBorderWidth) * (mProgress / 100f), 0,
                    colors,
                    null, Shader.TileMode.REPEAT);
            mProgressPaint.setShader(linearGradient);
        } else {
            mProgressPaint.setColor(Color.parseColor("#FFe12f"));
        }
        RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, (getMeasuredWidth() - mBorderWidth), getMeasuredHeight() - mBorderWidth);
        RectF rectFClipRect = new RectF(mBorderWidth / 2, mBorderWidth / 2, (getMeasuredWidth() - mBorderWidth) * (mProgress / 100f), getMeasuredHeight() - mBorderWidth);
        canvas.clipRect(rectFClipRect);
        canvas.drawRoundRect(rectF, Kits.Dimens.dpToPxInt(getContext(), mCorners), Kits.Dimens.dpToPxInt(getContext(), mCorners), mProgressPaint);
        canvas.restore();
    }

    /**
     * 把drawableLeft換到屏幕中間 缺點:gravity要設(shè)置為center_vertical
     *
     * @param canvas
     */
    private void doDrawLeft(Canvas canvas) {
        Drawable[] drawables = getCompoundDrawables();
        Drawable drawableLeft = drawables[0];
        if (drawableLeft != null) {
            float textWidth = getPaint().measureText(getText().toString());
            int drawablePadding = getCompoundDrawablePadding();
            int drawableWidth;
            drawableWidth = drawableLeft.getIntrinsicWidth();
            float bodyWidth = textWidth + drawableWidth + drawablePadding;
            canvas.translate((getWidth() - bodyWidth) / 2, 0);
        }
    }

    /**
     * 設(shè)置進度
     *
     * @param progress
     */
    public void setProgress(int progress) {
        XLog.e("the progress is" + progress);
        mProgress = progress;
        invalidate();
    }

    public void setProgress(int soFarBytes, int totalBytes) {
        mProgress = (int) ((soFarBytes / (float) totalBytes) * 100);
        invalidate();
    }

    /**
     * 設(shè)置滿進度
     */
    public void setFillProgress() {
        setProgress(100, 100);
    }

    public void setProgress(long soFarBytes, long totalBytes) {
        mProgress = (int) ((soFarBytes / (float) totalBytes) * 100);
        invalidate();
    }
}


 <declare-styleable name="ProgressTextView">
        <attr name="progress" format="integer" />
    </declare-styleable>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容