前言
在日常開發(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é)
收獲
- 通過上面的操作(雖然并不是很穩(wěn)),了解了一下基本的使用,也是得以應(yīng)用于實際開發(fā)中。
- 沒了
另外的實現(xiàn)方法
- 組合View
- 同理,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>