阅读 402

带文本的进度条实现方案

前言

在日常开发中,我们经常需要遇到这么一个场景下载,而下载的动态显示很多,比如我上文介绍过的仿魅族应用商店下载进度控件

但是可能更多的是下文这种展示UI:

未开始下载前

下载中

接下来我们看下要怎么去实现它。

思路

画圆角矩形的边 画中间的进度 画String文本以及图标

让我们依次来解决吧!

新建ProgressTextView

我们是基于文本的,所以这里我建议直接继承TextView,通过拓展 TextView的额外属性进行实现。

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; //边框画笔   文字我们只用系统的 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);
    }
复制代码

这样我们一个基本的样子已经搭起来了,因为我们是继承了TextView,所以我们并不需要去实现额外的测量,定位方法,可以看到我们在ondraw(Canvas canvas)里面分别实现了doDrawBorder(canvas)doDrawProgress(canvas),*doDrawLeft(canvas)*方法; 可以发现他们的调用顺序是在super之前的,所以继承自textView的setText(String str)会发生在我们绘制之后,不会发生被我们的后续绘制影响了文字的情况。

画圆角矩形的边

    /**
     * 画边框
     *
     * @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,分别对应的意义是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();
    }
复制代码

因为设计师需要在未完成的时候,进度条的颜色为渐变色,所以我们加了一个 mProgress < 100的判断。 这里进行了切canvas的操作clipRect,用于显示不同的进度颜色。

画String文本以及图标

因为我们是继承TextView的,所以不需要去管什么,但是实际上会发现,我们把canvas进行一下位移,使用效果会更佳

 /**
     * 把drawableLeft换到屏幕中间 缺点:gravity要设置为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);
        }
    }
复制代码

总结

收获

  1. 通过上面的操作(虽然并不是很稳),了解了一下基本的使用,也是得以应用于实际开发中。
  2. 没了

另外的实现方法

  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; //边框画笔   文字我们只用系统的 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要设置为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);
        }
    }

    /**
     * 设置进度
     *
     * @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();
    }

    /**
     * 设置满进度
     */
    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>
复制代码