Android自定义控件系列案例【四】

案例效果:

模拟器上运行有些锯齿,真机上和预期一样好

案例分析:

看效果,第一直觉肯定是Android原生态控件中没有这样的控件实现这种效果,自然想到应该需要自定义控件了,没错,这就是通过自定义控件来绘制的一个圆环进度条。仔细分析发现这个效果的进度条应该由几个部分组成,首先是无进度时的浅色圆环,然后是一个随进度变化的深色圆弧,而中间部分是一个深蓝色的实心圆,最后就是显示进度百分比的文字。这几部分大部分都是图形,所以使用图形绘制技术应该可以绘制出分部分效果,然后加上进度控制部分应该心里就有底了。

技术实现:

按照上面的分析我们一部分一部分的来实现。先大概列一个步骤:

(1)自定义View,重写构造方法,重写onDraw()方法。

重写onDraw()方法的目的是为了找到绘制图形的时机与场所。

(2)绘制浅色圆环。

需求准备Paint画笔,并设置画笔宽度,颜色,样式等,然后绘制圆环。

(3)绘制深色进度圆弧。

重置Paint画笔,并设置画笔颜色,绘制进度圆弧。

(4)添加SeekBar,并通过进度变化控制进度圆弧。

测试进度圆弧是否会随进度的变化正常工作。

(5)绘制深蓝色实心圆。

重置Paint画笔,并设置画笔颜色,绘制实心圆。

(6)绘制百分比文本。

计算进度百分比,绘制文本。

7)自定义属性,使进度条样式可配置(扩展部分)。

  • 自定义View,重写构造方法,重写onDraw()方法
package com.kedi.myprogressbar;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

public class MyProgressBar extends View {

	public MyProgressBar(Context context) {
		this(context, null);
	}
	public MyProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
	     super(context, attrs, defStyleAttr);

	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

	}

}

有了自定义MyProgressBar,此时就可以在XML布局中先占位了,下面是XML布局文件。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.kedi.myprogressbar.MyProgressBar
        android:id="@+id/pg"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:padding="5dp"
        android:layout_centerInParent="true" >
    </com.kedi.myprogressbar.MyProgressBar>

    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/pg"
        android:max="100"
        android:layout_margin="20dp" />

</RelativeLayout>

  • 绘制浅色圆环

Paint 画笔:

|- new Paint() 创建画笔

|- setColor() 设置画笔颜色

|- setStyle()设置画笔样式(Paint.Style.STROKE 虚框样式,Paint.Style.FILL 实心样式)

|- setStrokeWidth()设置画笔线宽

|- setAntiAlias()消除锯齿或毛边

|- setTextSize()设置画文本时的文本大小

|- setTypeface()设置画文本时的文本字体

Canvas画布:

|- drawCircle(cx, cy, radius, paint)绘制圆环或圆。cx,cy:圆心坐标,radius:圆半径,paint:画笔。

|- drawText(text, x, y, paint)绘制文本。text:文本,x,y:文本坐标,paint:画笔。

|- drawArc(oval, startAngle, sweepAngle, useCenter, paint)绘制圆弧。

oval:矩形区域,用来确定圆弧形状和大小。new RectF(left, top, right, bottom)确定一个矩形区域。

startAngle:圆弧始端角度。

sweepAngle:圆弧末端角度。

paint:画笔。

userCenter:设置是否显示圆弧的两边线条,false时只画圆弧没有两边,true时带两边。如下图:

有了这些技术准备,绘制工作将一路顺利。为了进行接下来的绘制工作,先在初始化方法中创建一个空画笔。

public class MyProgressBar extends View {
	private Paint paint;// 画笔
	public MyProgressBar(Context context) {
		this(context, null);
	}
	public MyProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
	     super(context, attrs, defStyleAttr);
	     init();
	}
	/**
	 * 初始化方法
	 */
	private void init() {
	    paint = new Paint();
	}
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

	}

}

接下来绘制浅色圆环

核心逻辑:

private int roundW;// 圆环宽
/**
 * 初始化方法
 */
private void init() {
   paint = new Paint();
 //初始化圆环宽,这里考虑了适配把15dp进行了对应平台的像素转换。
 roundW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics());

}
	       // 1.圆心(x,y)坐标值
		int centerX = getWidth() / 2;
		int centerY = centerX;
		// 2.圆环半径
		int radius1 = (centerX - roundW / 2);
		// 3.设置圆环颜色(浅色)
		paint.setColor(Color.parseColor("#11339ED4"));
		// 4.设置画笔的风格
		paint.setStyle(Paint.Style.STROKE);
		// 5.设置画圆环的宽度
		paint.setStrokeWidth(roundW);
		// 6.消除锯齿
		paint.setAntiAlias(true);
		// 7.画圆环
		canvas.drawCircle(centerX, centerY, radius1, paint);


完整代码(如何是直接拷贝这块代码的时候记的修改包名和导包):

package com.kedi.myprogressbar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class MyProgressBar extends View {
	private Paint paint;// 画笔
	private int roundW;// 圆环宽

	public MyProgressBar(Context context) {
		this(context, null);
	}

	public MyProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	/**
	 * 初始化方法
	 */
	private void init() {
		paint = new Paint();
		// 初始化圆环宽,这里考虑了适配把15dp进行了对应平台的像素转换。
		roundW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics());
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 绘制浅色圆环
		// 1.圆心(x,y)坐标值
		int centerX = getWidth() / 2;
		int centerY = centerX;
		// 2.圆环半径
		int radius1 = (centerX - roundW / 2);
		// 3.设置画大圆环颜色
		paint.setColor(Color.parseColor("#11339ED4"));
		// 4.设置画笔的风格
		paint.setStyle(Paint.Style.STROKE);
		// 5.设置画圆环的宽度
		paint.setStrokeWidth(roundW);
		// 6.消除锯齿
		paint.setAntiAlias(true);
		// 7.画圆环
		canvas.drawCircle(centerX, centerY, radius1, paint);
	}
}


效果图:

  • 绘制深色进度圆弧

圆弧的绘制重点在开始角度和末端角度的计算和确定,因为我们的圆弧代表进度,所以开始角度和末端角度都与进度有关,会跟着进度的变化而变化,所以首先我们的确定当前进度和最大进度。然后就可以计算始末角度了。

定义进度值:

private int progress = 0;// 当前进度值
private int maxProgress = 100;// 最大进度值

进度改变接口方法:

/**
	 * 更新进度和界面的方法
	 *
	 * @param progress
	 */
	public void setProgress(int progress) {
		if (progress < 0) {
			progress = 0;
		} else {
			this.progress = progress;
		}
		invalidate();
	}

计算始末角度:

开始角度 0

末端角度 (float)360 * progress / (float)maxProgress

核心逻辑:

		//绘制深色进度圆弧
		// 1.设置圆孤的宽度
		paint.setStrokeWidth(roundW);
		// 2.设置圆孤进度的颜色
		paint.setColor(Color.parseColor("#339ED4"));
		// 3.定义圆弧的形状和大小区域
		RectF oval = new RectF(centerX - radius1, centerY - radius1, centerX + radius1, centerY + radius1);
		// 4.设置空心样式
		paint.setStyle(Paint.Style.STROKE);
		// 5.根据进度画圆弧
		canvas.drawArc(oval, 0, (float)360 * progress / (float)maxProgress, false, paint);

完整代码(为了测试进度效果,我们先把progress=20,测试完再设置为progress=0):

package com.kedi.myprogressbar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class MyProgressBar extends View {
	private Paint paint;// 画笔
	private int roundW;// 圆环宽
	private int progress = 20;// 当前进度值
	private int maxProgress = 100;// 最大进度值

	/**
	 * 更新进度和界面的方法
	 *
	 * @param progress
	 */
	public void setProgress(int progress) {
		if (progress < 0) {
			progress = 0;
		} else {
			this.progress = progress;
		}
		invalidate();
	}

	public MyProgressBar(Context context) {
		this(context, null);
	}

	public MyProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	/**
	 * 初始化方法
	 */
	private void init() {
		paint = new Paint();
		roundW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics());

	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 绘制浅色圆环
		// 1.圆心(x,y)坐标值
		int centerX = getWidth() / 2;
		int centerY = centerX;
		// 2.圆环半径
		int radius1 = (centerX - roundW / 2);
		// 3.设置画大圆环颜色
		paint.setColor(Color.parseColor("#11339ED4"));
		// 4.设置画笔的风格
		paint.setStyle(Paint.Style.STROKE);
		// 5.设置画圆环的宽度
		paint.setStrokeWidth(roundW);
		// 6.消除锯齿
		paint.setAntiAlias(true);
		// 7.画圆环
		canvas.drawCircle(centerX, centerY, radius1, paint);

		// 绘制深色进度圆弧
		// 1.设置圆孤的宽度
		paint.setStrokeWidth(roundW);
		// 2.设置圆孤进度的颜色
		paint.setColor(Color.parseColor("#339ED4"));
		// 3.定义圆弧的形状和大小区域界限
		RectF oval = new RectF(centerX - radius1, centerY - radius1, centerX + radius1, centerY + radius1);
		// 4.设置空心样式
		paint.setStyle(Paint.Style.STROKE);
		// 5.根据进度画圆弧
		canvas.drawArc(oval, 0, (float) 360 * progress / (float) maxProgress, false, paint);
	}
}

效果图:

  • 添加SeekBar,并通过进度变化控制进度圆弧

这块主要是通过SeekBar进度的改变来更新进度圆弧的动态改变,逻辑很简单,监听SeekBar进度改变,然后把改变的进度通过setProgress()方法传给自定义控件,触发自定义控件重新调用onDraw()方法,根据新进度值重绘圆弧。

完整代码:

<span style="font-weight: normal;"><span style="font-size:14px;">package com.kedi.myprogressbar;

import android.app.Activity;
import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

public class MainActivity extends Activity {
	//自定义进度条控件
	private MyProgressBar pg;
	//SeekBar控件
	private SeekBar sb;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initViews();
		initEvents();
	}
	/**
	 * 初始化View
	 */
	private void initViews(){
		pg =(MyProgressBar) findViewById(R.id.pg);
		sb = (SeekBar) findViewById(R.id.sb);

	}
	/**
	 * 初始化事件监听与处理
	 */
	private void initEvents() {
		sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				//改变圆弧的进度,并重新绘制圆弧,主要是通过触发自定义控件的onDraw()方法达到目的
				pg.setProgress(progress);
			}
		});
	}
}</span></span>

效果图:

  • 绘制深蓝色实心圆

实心圆的绘制也是通过drawCircle()方法,只是画笔的样式为FILL实心,所以比较简单。

核心代码:

	// 绘制深蓝色实心圆
		 // 1.实心圆半径
		 int radius2 = centerX - roundW;
		 // 2.实心圆颜色
		 paint.setColor(Color.parseColor("#336799"));
		 // 3.设置画笔风格为实心
		 paint.setStyle(Paint.Style.FILL);
		 // 4.画实心圆
		 canvas.drawCircle(centerX, centerY, radius2, paint);

效果图:

  • 绘制百分比文本

绘制文本需要知道文本大小,文本字体,文本颜色,文本x,y坐标位置以及文本内容。

文本大小我们定义个成员变量,然后在init()方法中初始化,当然可以对外提供接口方法,设置文本大小,

文本颜色使用白色,当然也可以像文本大小那样定义成员变量,然后初始化,也可对外提供接口方法,设置文本颜色

文本x,y坐标需要根据圆心坐标与文本自身的宽高进行计算。

文本内容是进度百分比,需要根据进度进行计算。(float) progress / (float) maxProgress) * 100

核心逻辑:

     // 绘制百分比文本
		// 1.设置无边框
		paint.setStrokeWidth(0);
		// 2.设置字体颜色
		paint.setColor(Color.WHITE);
		paint.setAntiAlias(true);
		// 3.设置字体大小,定义成员变量textSize,然后在初始化方法中赋初始值
		paint.setTextSize(textSize);
		// 4.设置字体
		paint.setTypeface(Typeface.DEFAULT_BOLD);
		// 5.计算进度百分比
		int percent = (int) (((float) progress / (float) maxProgress) * 100);
		// 6.测量字体宽度
		float textWidth = paint.measureText(percent + "%");
		// 7.画出进度百分比文本
		canvas.drawText(percent + "%", centerX - textWidth / 2, centerY + textSize / 2, paint);

完整代码:

package com.kedi.myprogressbar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class MyProgressBar extends View {
	private Paint paint;// 画笔
	private int roundW;// 圆环宽
	private int textSize;// 字体大小
	private int progress = 0;// 当前进度值
	private int maxProgress = 100;// 最大进度值

	/**
	 * 更新进度和界面的方法
	 *
	 * @param progress
	 */
	public void setProgress(int progress) {
		if (progress < 0) {
			progress = 0;
		} else {
			this.progress = progress;
		}
		invalidate();
	}

	/**
	 * @param context
	 */
	public MyProgressBar(Context context) {
		this(context, null);
	}

	/**
	 * @param context
	 * @param attrs
	 */
	public MyProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	/**
	 * @param context
	 * @param attrs
	 * @param defStyleAttr
	 */
	public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	/**
	 * 初始化方法
	 */
	private void init() {
		paint = new Paint();
		roundW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics());
		textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 25, getResources().getDisplayMetrics());
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 绘制浅色圆环
		// 1.圆心(x,y)坐标值
		int centerX = getWidth() / 2;
		int centerY = centerX;
		// 2.圆环半径
		int radius1 = (centerX - roundW / 2);
		// 3.设置画大圆环颜色
		paint.setColor(Color.parseColor("#11339ED4"));
		// 4.设置画笔的风格
		paint.setStyle(Paint.Style.STROKE);
		// 5.设置画圆环的宽度
		paint.setStrokeWidth(roundW);
		// 6.消除锯齿
		paint.setAntiAlias(true);
		// 7.画圆环
		canvas.drawCircle(centerX, centerY, radius1, paint);

		// 绘制深色进度圆弧
		// 1.设置圆孤的宽度
		paint.setStrokeWidth(roundW);
		// 2.设置圆孤进度的颜色
		paint.setColor(Color.parseColor("#339ED4"));
		// 3.定义圆弧的形状和大小区域界限
		RectF oval = new RectF(centerX - radius1, centerY - radius1, centerX + radius1, centerY + radius1);
		// 4.设置空心样式
		paint.setStyle(Paint.Style.STROKE);
		// 5.根据进度画圆弧
		canvas.drawArc(oval, 0, (float) 360 * progress / (float) maxProgress, false, paint);

		// 绘制深蓝色实心圆
		// 1.实心圆半径
		int radius2 = centerX - roundW;
		// 2.实心圆颜色
		paint.setColor(Color.parseColor("#336799"));
		// 3.设置画笔风格为实心
		paint.setStyle(Paint.Style.FILL);
		// 4.画实心圆
		canvas.drawCircle(centerX, centerY, radius2, paint);

		// 绘制百分比文本
		// 1.设置无边框
		paint.setStrokeWidth(0);
		// 2.设置字体颜色
		paint.setColor(Color.WHITE);
		// 3.设置字体大小
		paint.setTextSize(textSize);
		// 4.设置字体
		paint.setTypeface(Typeface.DEFAULT_BOLD);
		// 5.计算进度百分比
		int percent = (int) (((float) progress / (float) maxProgress) * 100);
		// 6.测量字体宽度
		float textWidth = paint.measureText(percent + "%");
		// 7.画出进度百分比文本
		canvas.drawText(percent + "%", centerX - textWidth / 2, centerY + textSize / 2, paint);
	}
}


效果图:

到此实现的效果已经与我们一开始看到的效果分毫不差了。

  • 自定义属性,使进度条样式可配置(扩展部分)

效果已经实现完了,但是如果我的项目中需要一个其它主题风格的进度条,那我们不得不重新Copy一份,然后修改其中的颜色值什么的,这样的自定义控件显然不够灵活和通用,如果进度条提供了对主题风格的定制接口那就灵活不少,所以接下来做为扩展部分,要做的是为进度条提供主题风格定制途径---自定义属性与Setter方法。

经分析,把与样式相关的特征都定义成成员变量,有如下几个:

	//圆环相关成员变量
	 private int roundW;// 圆环宽
	 private int roundColor;//圆环颜色

	//圆弧相关成员变量
	 private int progress;// 当前进度值
	 private int progressColor;//进度圆弧颜色
	 private int maxProgress = 100;// 最大进度值

	//实心圆相关成员变量
   	 private int circleColor;//实心圆颜色

   	 //百分比文本相关成员变量
   	 private int textColor;//字体颜色
   	 private int textSize;// 字体大小

定义自定义属性文件values/attrs.xml,并定义成上面成员变量一一对应的自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <declare-styleable name="MyProgressBarSytle">
        <!-- 圆环相关 -->
        <attr name="roundW" format="dimension" />
        <attr name="roundColor" format="color" />
        <!-- 圆弧相关 -->
        <attr name="progress" format="integer" />
        <attr name="progressColor" format="color" />
        <attr name="maxProgress" format="integer" />
        <!-- 实心圆相关 -->
        <attr name="circleColor" format="color" />

        <!-- 百分比文本相关 -->
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
    </declare-styleable>
</resources>

构造方法中获取自定义属性,然后初始化上面的那几个成员变量(将原来的值做为默认值):

public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(attrs);
	}

	private void init(AttributeSet attrs) {
		paint = new Paint();
	    TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyProgressBarSytle);

	    roundW = (int) typedArray.getDimension(R.styleable.MyProgressBarSytle_roundW, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics()));
	    roundColor = typedArray.getColor(R.styleable.MyProgressBarSytle_roundColor, Color.parseColor("#11339ED4"));
	    progress = typedArray.getInt(R.styleable.MyProgressBarSytle_progress, 0);
	    progressColor = typedArray.getColor(R.styleable.MyProgressBarSytle_progressColor, Color.parseColor("#339ED4"));

	    circleColor = typedArray.getColor(R.styleable.MyProgressBarSytle_circleColor, Color.parseColor("#336799"));

	    textColor = typedArray.getColor(R.styleable.MyProgressBarSytle_textColor, Color.parseColor("#ffffff"));
	    textSize = (int) typedArray.getDimension(R.styleable.MyProgressBarSytle_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 25, getResources().getDisplayMetrics()));

	    typedArray.recycle();
	}

将绘制逻辑各分部中样式原来的值,换成成员变量:

@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		//绘制浅色圆环
		// 1.圆心(x,y)坐标值
		int centerX = getWidth() / 2;
		int centerY = centerX;
		// 2.圆环半径
		int radius1 = (centerX - roundW / 2);
		// 3.设置画大圆环颜色
		paint.setColor(roundColor);
		// 4.设置画笔的风格
		paint.setStyle(Paint.Style.STROKE);
		// 5.设置画圆环的宽度
		paint.setStrokeWidth(roundW);
		// 6.消除锯齿
		paint.setAntiAlias(true);
		// 7.画圆环
		canvas.drawCircle(centerX, centerY, radius1, paint);

		// 绘制深色进度圆弧
		// 1.设置圆孤的宽度
		paint.setStrokeWidth(roundW);
		// 2.设置圆孤进度的颜色
		paint.setColor(progressColor);
		// 3.定义圆弧的形状和大小区域界限
		RectF oval = new RectF(centerX - radius1, centerY - radius1, centerX + radius1, centerY + radius1);
		// 4.设置空心样式
		paint.setStyle(Paint.Style.STROKE);
		// 5.根据进度画圆弧
		canvas.drawArc(oval, 0, (float) 360 * progress / (float) maxProgress, false, paint);

		// 绘制深蓝色实心圆
		// 1.实心圆半径
		int radius2 = centerX - roundW;
		// 2.实心圆颜色
		paint.setColor(circleColor);
		// 3.设置画笔风格为实心
		paint.setStyle(Paint.Style.FILL);
		// 4.画实心圆
		canvas.drawCircle(centerX, centerY, radius2, paint);

		// 绘制百分比文本
		// 1.设置无边框
		paint.setStrokeWidth(0);
		// 2.设置字体颜色
		paint.setColor(textColor);
		paint.setAntiAlias(true);
		// 3.设置字体大小
		paint.setTextSize(textSize);
		// 4.设置字体
		paint.setTypeface(Typeface.DEFAULT_BOLD);
		// 5.计算进度百分比
		int percent = (int) (((float) progress / (float) maxProgress) * 100);
		// 6.测量字体宽度
		float textWidth = paint.measureText(percent + "%");
		// 7.画出进度百分比文本
		canvas.drawText(percent + "%", centerX - textWidth / 2, centerY + textSize / 2, paint);

	}

如果我们希望通过代码也可以修改这些样式值,就对外提供Setter方法:

 /**
     * 设置圆环宽度
     * @param roundW
     */
	public void setRoundW(int roundW) {
		this.roundW = roundW;
	}
	/**
	 * 设置圆环颜色
	 * @param roundColor
	 */
	public void setRoundColor(int roundColor) {
		this.roundColor = roundColor;
	}
	/**
	 * 设置进度圆弧颜色
	 * @param progressColor
	 */
	public void setProgressColor(int progressColor) {
		this.progressColor = progressColor;
	}
	/**
	 * 设置最大进度值
	 * @param maxProgress
	 */
	public void setMaxProgress(int maxProgress) {
		this.maxProgress = maxProgress;
	}
	/**
	 * 设置实心圆颜色
	 * @param circleColor
	 */
	public void setCircleColor(int circleColor) {
		this.circleColor = circleColor;
	}
	/**
	 * 设置百分比进度文本颜色
	 * @param textColor
	 */
	public void setTextColor(int textColor) {
		this.textColor = textColor;
	}
	/**
	 * 设置百分比进度文本字体大小
	 * @param textSize
	 */
	public void setTextSize(int textSize) {
		this.textSize = textSize;
	}

	/**
	 * 更新进度和界面的方法
	 *
	 * @param progress
	 */
	public void setProgress(int progress) {
		if (progress < 0) {
			progress = 0;
		} else {
			this.progress = progress;
		}
		invalidate();
	}

最后演示一下使用自定义属性定制主题的方式:

(1)在根布局中添加命名空间

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

其中my是是命名空间名,可随意指定,而“http://schemas.android.com/apk/res-auto”是固定的命名空间。

(2)使用自定义属性并指定属性值

完整布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:my="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.kedi.myprogressbar.MyProgressBar
        android:id="@+id/pg"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_centerInParent="true"
        android:padding="5dp"
        my:circleColor="#ACD900"
        my:maxProgress="100"
        my:progressColor="#FF6400"
        my:roundColor="#316900"
        my:roundW="8dp"
        my:textColor="#EE3400"
        my:textSize="20sp" >
    </com.kedi.myprogressbar.MyProgressBar>

    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/pg"
        android:layout_margin="20dp"
        android:max="100" />

</RelativeLayout>

效果图:

到此就完成了比较灵活的圆形进度条的制作。

时间: 2024-10-06 06:57:30

Android自定义控件系列案例【四】的相关文章

Android自定义控件系列案例【五】

案例效果: 实例分析: 在开发银行相关客户端的时候或者开发在线支付相关客户端的时候经常要求用户绑定银行卡,其中银行卡号一般需要空格分隔显示,最常见的就是每4位数以空格进行分隔,以方便用户实时比对自己输入的卡号是否正确.当产品经理或UI设计师把这样的需求拿给我们的时候,我们的大脑会马上告诉我们Android中有个EditText控件可以用来输入卡号,但好像没见过可以分隔显示的属性或方法啊.当我们睁大眼睛对着效果图正发呆的时候,突然发现当用户输入内容的时候还出现了清除图标,点击清空图标还可以清空用户

Android自定义控件系列 十:利用添加自定义布局来搞定触摸事件的分发,解决组合界面中特定控件响应特定方向的事件

这个例子是比较有用的,基本上可以说,写完这一次,以后很多情况下,直接拿过来addView一下,然后再addInterceptorView一下,就可以轻轻松松的达到组合界面中特定控件来响应特定方向的触摸事件了. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45198549,非允许请勿用于商业或盈利用途,违者必究. 在写Android应用的过程之中,经常会遇到这样的情况:界面包含了多个控件,我们希望触摸在界面上的不

Android自定义控件系列八:详解onMeasure()(二)--利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

上一篇文章详细讲解了一下onMeasure/measure方法在Android自定义控件时的原理和作用,参看博文:Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一),今天就来真正实践一下,让这两个方法大显神威来帮我们搞定图片的屏幕适配问题. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45038329,非允许请勿用于商业或盈利用途,违者必究. 使用ImageView会遇到

史上最详细的Android Studio系列教程四--Gradle基础

史上最详细的Android Studio系列教程四--Gradle基础

[转]Android自定义控件系列五:自定义绚丽水波纹效果

出处:http://www.2cto.com/kf/201411/353169.html 今天我们来利用Android自定义控件实现一个比较有趣的效果:滑动水波纹.先来看看最终效果图: 图一 效果还是很炫的:饭要一口口吃,路要一步步走,这里我们将整个过程分成几步来实现 一.实现单击出现水波纹单圈效果: 图二 照例来说,还是一个自定义控件,这里我们直接让这个控件撑满整个屏幕(对自定义控件不熟悉的可以参看我之前的一篇文章:Android自定义控件系列二:自定义开关按钮(一)).观察这个效果,发现应该

Android自定义控件系列二:如何自定义属性

上一篇Android自定义控件系列一:如何测量控件尺寸 我们讲了如何确定控件的属性,这篇接着也是讲个必要的知识-如何自定义属性.对于一个完整的或者说真正有实用价值的控件,自定义属性是必不可少的. 如何为控件定义属性 在res/values/attrs.xml(attrs.xml如果不存在,可以创建个)中使用<declare-styleable>标签定义属性,比如我想定义个显示头像的圆形的图片控件(AvatarImageView): 01.<?xml version="1.0&q

Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)

转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45027641 自定义view/viewgroup要重写的几个方法:onMeasure(),onLayout(),onDraw().(不熟悉的话可以查看专栏的前几篇文章:Android自定义控件系列二:自定义开关按钮(一)). 今天的任务就是详细研究一下protected void onMeasure(int widthMeasureSpec, int heightMeasureSpe

android自定义控件系列教程----视图的测量和布局

前面说点什么 当我们的一个视图界面绘制在android屏幕上面的时候其实都必须经过这几步measure. layout.draw这几个阶段,我们可以在view类里面看到这几个函数,然后里面有几个函数是onmeasure.onlayout.ondraw这几个函数是我们重写控件需要注意的这几个函数,下面我们就来讲讲这几个函数的功能和作用. onMeasure 正如这个函数的名子一样就是测量,所有的图示其实系统在绘制之前都不知道它到底有多大的,所以在很多时候我们在初始化界面oncreate的时候直接去

Android自定义控件系列三:自定义开关按钮(三)--- 自定义属性

尊重原创,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/40855377 接之前的:Android自定义控件系列二:自定义开关按钮(一)和Android自定义控件系列三:自定义开关按钮(二)继续,今天要讲的就是如何在自定义控件中使用自定义属性,实际上这里有两种方法,一种是配合XML属性资源文件的方式,另一种是不需要XML资源文件的方式:下面我们分别来看看: 一.配合XML属性资源文件来使用自定义属性: 那么还是针对我们之前写的自定义