今天一起来做一个简单实用的自定义Loading控件,效果如图所示:
说一下思路:
1.绘制两个圆环
2.圆环中绘制弧形,填充进度。
3.根据中间控件计算文字合适大小,绘制文字。
第一步,绘制两个圆环。
采用绘制弧形方法绘制,因为考虑到可能是其他形状的进度,比如椭圆等。如这种:
绘制弧形的方法为:
其中,第一个参数oval是一个矩形,它的作用是用来定位咱们要绘制的圆的具体大小和位置,其实说白了就是我们要绘制的圆(或者弧)的外切矩形。当然啦,这个矩形的形状可能是长方形也可能是正方形,如果是正方形咱们绘制出来的就是一个规范的圆的一段,如果是长方形,绘制出来的就是椭圆的一段。
第二个参数是说绘制的这个弧线开始的角度,一般是-90,代表从圆的顶点开始,如果设置为0则从三点钟方向。
第四个参数就是代表要绘制的这段弧线从开始到结束要划过的角度,如果是360,那画出来就是一个完整的圆形或者椭圆咯,注意,这个和startAngle参数没有太大的关系,startAngle只是看你弧形开始的位置而已。
第五个参数是是否需要绘制出圆心,我们这里不需要,设置成false即可。
接下来定义矩形的大小,
外层圆:rectFOut =new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
内层圆:rectFIn =new RectF(ringWidth, ringWidth, getMeasuredWidth() -ringWidth, getMeasuredHeight() -ringWidth);
getMeasuredWidth和getMeasuredHeight就是取出自身控件在测量完毕后的宽和高,这里我们就让圆环紧贴控件本身大小即可。ringWidth为定义的环的宽度,这里按照比例来取,例子中取的是getMeasuredWidth() /20。
第二步,绘制进度
绘制进度一样是采用绘制弧形的方法来做,只不过这里是一段一段的绘制。
和上面不同的是需要计算当前需要绘制的弧形的宽度和开始的位置,这里我将画笔的宽度直接设置成圆环的宽度,即上面的ringWidth变量的值。进度圆弧的外切矩形如何定义呢?想象一下,笔的宽度和圆环的宽度一样,那简单,我们绘制进度肯定是从圆环的中间位置绘制了。所以,定义进度圆弧矩形如下:
paint.setStrokeWidth(ringWidth);//先将画笔宽度设置为圆环的宽度。
ringRec =new RectF(ringWidth /2, ringWidth /2, getMeasuredWidth() -ringWidth /2, getMeasuredHeight() -ringWidth /2);
第三步,绘制文字
绘制文字,肯定是绘制在控件的中央了。那么如何确定文字字体大小呢?(因为要考虑到文字是否会越过圆环)
首先,咱们先固定文字只能占用的大小比例,这里我规定,文字只能占用内圆直径的3/5,
final float textMaxCanUseWidth =3 * (getMeasuredWidth() -ringWidth *2) /5;
然后,先定义一个文字的初始大小值,float textSize = (getMeasuredWidth() -ringWidth) /textSizeScale;//第一次文字大小。textSizeScale为一个文字所占内圆直径的大小,这里我取6。
接下来,就将画笔字体设置为当前值,测量要绘制的文字的宽度是否超过了规定值,如果是则不断的减1,循环判断。
paint.setTextSize(textSize);
float nowWidth =paint.measureText(text);
while (nowWidth > textMaxCanUseWidth) {
textSize--;
paint.setTextSize(textSize);
nowWidth =paint.measureText(text);
}
好了,整个步骤就这么多,最后暴露出进度设置的方法,然后可以将各种属性放到xml中,方便调用。完整代码如下:
public class LoadingView extends View {
private static final String TAG = "LoadingView";
Paint paint;
int textColor;
int circleColor;
int fillColor;
int startProgress = -90;
private float goProgress = 0;
float ringWidth = 0;
int textSizeScale = 6;
int defaultColor = 0;
//////
RectF ringRec, rectFOut, rectFIn;
public LoadingView(Context context) {
super(context);
}
public LoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
Log.e(TAG, "LoadingView: ");
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//获取自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
circleColor = array.getColor(R.styleable.LoadingView_circleColor, defaultColor);
fillColor = array.getColor(R.styleable.LoadingView_fillColor, defaultColor);
textColor = array.getColor(R.styleable.LoadingView_textColor, defaultColor);
startProgress = array.getColor(R.styleable.LoadingView_startDegre, startProgress);
paint = new Paint();
paint.setColor(circleColor);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
}
/**
* 设置当前进度
* @param progress
*/
public void setProgress(int progress) {
goProgress = (float) (360 * progress * 0.01);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(circleColor);
paint.setStyle(Paint.Style.STROKE);
//外圆环
canvas.drawArc(rectFOut, -90, 360, false, paint);
//内圆环
canvas.drawArc(rectFIn, -90, 360, false, paint);
//进度
paint.setStrokeWidth(ringWidth);
paint.setColor(fillColor);
canvas.drawArc(ringRec, startProgress, goProgress, false, paint);
//文字
String text = (int) (goProgress / 3.6) + "%";
float size = getTextSize(text);
paint.setTextSize(size);
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
float textHeight = rect.bottom - rect.top;
float textWidth = rect.right - rect.left;
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
float y = ringWidth + ((getMeasuredHeight() - 2 * ringWidth - textHeight) / 2) + textHeight;
float x = ringWidth + ((getMeasuredWidth() - (2 * ringWidth) - textWidth) / 2);
paint.setColor(textColor);
canvas.drawText(text, x, y, paint);
}
/**
* 计算出合适的文字字体大小
*
* @param text
* @return
*/
private float getTextSize(String text) {
final float textMaxCanUseWidth = 3 * (getMeasuredWidth() - ringWidth * 2) / 5;
float textSize = (getMeasuredWidth() - ringWidth) / textSizeScale;//第一次文字大小
paint.setTextSize(textSize);
float nowWidth = paint.measureText(text);
while (nowWidth > textMaxCanUseWidth) {
textSize--;
paint.setTextSize(textSize);
nowWidth = paint.measureText(text);
}
return textSize;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ringWidth = getMeasuredWidth() / 20;
rectFOut = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
rectFIn = new RectF(ringWidth, ringWidth, getMeasuredWidth() - ringWidth, getMeasuredHeight() - ringWidth);
ringRec = new RectF(ringWidth / 2, ringWidth / 2, getMeasuredWidth() - ringWidth / 2, getMeasuredHeight() - ringWidth / 2);
}
}