前言
在日常开发中,我们经常需要遇到这么一个场景下载,而下载的动态显示很多,比如我上文介绍过的仿魅族应用商店下载进度控件
但是可能更多的是下文这种展示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);
}
}
总结
收获
- 通过上面的操作(虽然并不是很稳),了解了一下基本的使用,也是得以应用于实际开发中。
- 没了
另外的实现方法
- 组合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; //边框画笔 文字我们只用系统的 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();
}
}