android自定义进度条控件,Android利用Paint自定义View实现进度条控件方法示例

本文详细介绍了如何在Android中自定义一个进度条控件,通过重写View的onMeasure和onDraw方法,结合Paint类,实现进度条的颜色、圆边框颜色、宽度等属性的定制。并展示了在XML和Activity中如何使用该自定义控件,以及通过动态改变进度实现进度条的变化。
摘要由CSDN通过智能技术生成

前言

View的三大流程:测量,布局,绘制,自定义View学的是啥?无非就两种:绘制文字和绘制图像。

我们在上一篇文章《Android绘图之Paint的使用》中学习了Paint的基本用法,但是具体的应用我们还没有实践过。从标题中可知,本文是带领读者使用Paint,自定义一个进度条控件。

效果图

4fdde221d8e5c316e29feac978b697a9.png

上图就是本文要实现的效果图。

实现过程

既然是自定义控件,本文的该控件是直接继承View,然后重写View的onMeasure和onDraw方法来实现。其中onMeasure主要作用是测量控件的宽/高。而onDraw则是将界面绘制到屏幕上。

从效果的效果上看,我们需要自定义一些属性,如:进度度条的颜色、圆边框的颜色、圆边框的宽度和文本的大小等等。

具体的自定义属性请看下面attrs.xml的代码:

接下来看本文的最重要部分,也就是自定义View

public class CustomProgressBar extends View {

private int max = 100;//总进度

private int roundColor = Color.RED;//进度圆弧的颜色

private float roundWidth = 10;//圆边框宽度

private int roundProgressColor = Color.BLUE;//默认的大圆环边框颜色

private float textSize = 55;//文本大小

private int textColor = Color.GREEN;//文本默认颜色

private boolean textShow = true;//是否展示文本

public static final int STROKE = 0;//描边

public static final int FILL = 1;//填充

private int style = STROKE;//默认描边

private int progress;//进度

private Paint mPaint;

private int mWidth = 200;//默认控件宽度,wrap_content时候使用

private int mHeight = 200;//默认控件高度,wrap_content时候使用

public CustomProgressBar(Context context) {

this(context, null);

}

public CustomProgressBar(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init(context, attrs);

}

private void init(Context context, AttributeSet attrs) {

mPaint = new Paint();

if (attrs != null) {

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomProgressBar);

max = typedArray.getInteger(R.styleable.CustomProgressBar_max, 100);

roundColor = typedArray.getColor(R.styleable.CustomProgressBar_roundColor, Color.BLUE);

roundProgressColor = typedArray.getColor(R.styleable.CustomProgressBar_roundProgressColor, Color.BLUE);

textColor = typedArray.getColor(R.styleable.CustomProgressBar_textColor, Color.GREEN);

textSize = typedArray.getDimension(R.styleable.CustomProgressBar_textSize, 55);

roundWidth = typedArray.getDimension(R.styleable.CustomProgressBar_roundWidth, 10);

textShow = typedArray.getBoolean(R.styleable.CustomProgressBar_textShow, true);

style = typedArray.getInt(R.styleable.CustomProgressBar_style, 0);

typedArray.recycle();

}

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){

setMeasuredDimension(mWidth,mHeight);

}else if (widthSpecMode == MeasureSpec.AT_MOST){

setMeasuredDimension(mWidth,heightSpecSize);

}else if (heightSpecMode == MeasureSpec.AT_MOST){

setMeasuredDimension(widthSpecSize,mHeight);

}

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

final int paddingLeft = getPaddingLeft();

final int paddingRight = getPaddingRight();

final int paddingTop = getPaddingTop();

final int paddingBottom = getPaddingBottom();

int width = getWidth() - paddingLeft - paddingRight;

int height = getHeight() - paddingBottom - paddingTop;

//画默认的大圆环

float radius = (float)Math.min(width,height)/2.0f;//中心坐标点

mPaint.setColor(roundColor);

mPaint.setStyle(Paint.Style.STROKE);//描边

mPaint.setStrokeWidth(roundWidth);//圆环边的宽度

// if (style == STROKE){

// mPaint.setStrokeWidth(roundWidth);//圆环边的宽度

// }

mPaint.setAntiAlias(true);

//(float cx, float cy, float radius, @NonNull Paint paint)

canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2,radius,mPaint);

//画进度百分比

mPaint.setColor(textColor);

mPaint.setStrokeWidth(0);//圆环的宽度

mPaint.setTextSize(textSize);

mPaint.setTypeface(Typeface.DEFAULT_BOLD);

int percent = (int)(progress/(float)max * 100);

if(textShow && percent!=0 && style == STROKE){

//(@NonNull String text, float x, float y, @NonNull Paint paint)

canvas.drawText(percent+"%", (getWidth()-mPaint.measureText(percent+"%"))/2f,

//y公式: float baselineY = centerY + (fontMetrics.bottom-fontMetrics.top)/2 - fontMetrics.bottom

getWidth()/2f-(mPaint.descent()+mPaint.ascent())/2f,

mPaint);

}

//画圆弧

//矩形区域,定义圆弧的形状大小

//(float left, float top, float right, float bottom)

RectF oval = new RectF(paddingLeft, paddingTop, width+paddingLeft, height+paddingTop);

mPaint.setColor(roundProgressColor);

mPaint.setStrokeWidth(roundWidth);//圆环边的宽度

switch (style){

case STROKE:

mPaint.setStyle(Paint.Style.STROKE);

//(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint)

//useCenter:设置圆弧在绘画的时候,是否经过圆形

canvas.drawArc(oval , 0, 360*progress/max, false, mPaint);

break;

case FILL:

mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

if(progress!=0)

canvas.drawArc(oval , 0, 360*progress/max, true, mPaint);

break;

default:

break;

}

}

public void setProgressWidth(int width) {

mWidth = width;

}

public void setProgressHeight(int height) {

mHeight = height;

}

public synchronized void setMax(int max) {

if (max < 0) {

throw new IllegalArgumentException("max不能小于0");

}

this.max = max;

}

public void setRoundColor(int roundColor) {

this.roundColor = roundColor;

}

public void setRoundWidth(float roundWidth) {

this.roundWidth = roundWidth;

}

public void setRoundProgressColor(int roundProgressColor) {

this.roundProgressColor = roundProgressColor;

}

public void setTextSize(float textSize) {

this.textSize = textSize;

}

public void setTextColor(int textColor) {

this.textColor = textColor;

}

public void setTextShow(boolean textShow) {

this.textShow = textShow;

}

public synchronized void setProgress(int progress) {

if (progress < 0) {

throw new IllegalArgumentException("progress不能小于0");

}

if (progress > max) {

progress = max;

}

if (progress <= max) {

this.progress = progress;

postInvalidate();

}

}

public synchronized int getMax() {

return max;

}

public int getRoundColor() {

return roundColor;

}

public float getRoundWidth() {

return roundWidth;

}

public int getRoundProgressColor() {

return roundProgressColor;

}

public int getTextColor() {

return textColor;

}

public boolean isTextShow() {

return textShow;

}

public synchronized int getProgress() {

return progress;

}

}

流程:初始化的时候会拿到自定义属性,然后onMeasure方法中测量控件的宽和高,该方法主要处理了LayoutParams的wrap_content,当wrap_content时,默认设置默认宽/高,而不是让控件占据整个屏幕,需要调用setMeasuredDimension方法测量。最后测量得到了控件的宽/高,调用onDraw方法将界面绘制到屏幕上,在onDraw方法绘制的时需要考虑padding的情况,如果不做padding处理,则padding将不起作用。

onDraw绘制流程:先绘制一个默认的大圆环,然后在圆中心绘制百分比的文本,最后再绘制一个进度圆环,进度圆环会覆盖底部的默认大圆环,这样就达到显示进度的情况。

设置好画笔之后,使用canvas.drawCircle绘制默认的大圆环,再次设置画笔,使用canvas.drawText方法绘制文本;画圆弧时需要定义一个矩形区域RectF,通过canvas.drawArc方法绘制。

绘制好之后,如何让用户看到进度条在变化呢?其实就是通过setProgress方法里面的postInvalidate()方法,该方法会刷新界面,刷新界面时会调用onDraw,这样就可以将进度画到屏幕上,进度条不停的在变化。

使用

XML中使用

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

tools:context="com.main.paint.PaintActivity">

android:id="@+id/progressbar"

android:layout_width="200dp"

android:layout_height="200dp"

app:roundProgressColor="#FF0000"

app:roundWidth="2dp"

app:textColor="#FF0000"

app:style="STROKE"

android:padding="30dp"

app:textSize="20dp"/>

android:id="@+id/progressbar01"

android:layout_width="200dp"

android:layout_height="200dp"

app:roundProgressColor="#FF0000"

app:roundWidth="2dp"

app:textColor="#FF0000"

app:style="FILL"

android:padding="30dp"

app:textSize="20dp"/>

Activity代码如下:

public class PaintActivity extends AppCompatActivity {

private CustomProgressBar mCustomProgressBar;

private CustomProgressBar mCustomProgressBar01;

private int progress;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_paint);

mCustomProgressBar = (CustomProgressBar)this.findViewById(R.id.progressbar);

mCustomProgressBar.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

new Thread(new Runnable() {

@Override

public void run() {

progress = 0;

while (progress <= 100) {

progress += 2;

mCustomProgressBar.setProgress(progress);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}).start();

}

});

mCustomProgressBar01 = (CustomProgressBar)this.findViewById(R.id.progressbar01);

mCustomProgressBar01.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

new Thread(new Runnable() {

@Override

public void run() {

progress = 0;

while (progress <= 100) {

progress += 2;

mCustomProgressBar01.setProgress(progress);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}).start();

}

});

}

}

这样就完成了一个自定义的进度条控件,并且在onDraw方法中使用Paint将界面绘制出来。读者可以自行实践一把,加深对Paint的理解。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值