欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-11-24 18:54:46
前言 view的三大流程:测量,布局,绘制,自定义view学的是啥?无非就两种:绘制文字和绘制图像。 我们在上一篇文章《android绘图之paint的使用》中学习了p...

前言

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

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

效果图

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

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

实现过程

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

从效果的效果上看,我们需要自定义一些属性,如:进度度条的颜色、圆边框的颜色、圆边框的宽度和文本的大小等等。
具体的自定义属性请看下面attrs.xml的代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="customprogressbar">
 <attr name="roundprogresscolor" format="color"></attr>
 <attr name="roundcolor" format="color"></attr>
 <attr name="roundwidth" format="dimension"></attr>
 <attr name="textsize" format="dimension"></attr>
 <attr name="textcolor" format="color"></attr>
 <attr name="max" format="integer"></attr>
 <attr name="textshow" format="boolean"></attr>
 <attr name="style">
  <enum name="stroke" value="0"></enum>
  <enum name="fill" value="1"></enum>
 </attr>
 </declare-styleable>
</resources>

接下来看本文的最重要部分,也就是自定义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中使用

<?xml version="1.0" encoding="utf-8"?>
<linearlayout
 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">

 <com.main.paint.customprogressbar
 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"/>

 <com.main.paint.customprogressbar
 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"/>

</linearlayout>

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的理解。

总结

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