本文的实例是别人写的,我又重写了一遍,在代码中加上了详细的注解。
1.首先新建工程,在工程的res/values目录下新建attrs.xml文件,用于添加自定义控件的属性。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomProgressBar">
<attr name="firstColor" format="color" />
<attr name="secondColor" format="color" />
<attr name="circleWidth" format="dimension" />
<attr name="speed" format="integer" />
</declare-styleable>
</resources>
2.接下来新建一个类继承view,开始编写控件类
public class Mycustomview extends View {
private int FirstColor;//第一圈颜色
private int SecondColor;//第二圈颜色
private int circlewidth;//圆弧宽
private int speed;//速度
private Paint mpaint;//画笔
private int mprogress;//进度
private boolean next;//是否开始下一轮
private int center;//圆心的位置(x/y)
private int radius;//半径
public Mycustomview(Context context) {
//注意这里是 this 不是super
this(context,null);
}
public Mycustomview(Context context, AttributeSet attrs) {
//目的为了让下面构造中的代码一定执行
this(context, attrs,0);
}
public Mycustomview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//获取对自定义属性设置的类 typearray
TypedArray a=context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomProgressBar, defStyle,0);
//对自定义的属性值进行获取
FirstColor=a.getColor(R.styleable.CustomProgressBar_firstColor, Color.BLACK);
SecondColor=a.getColor(R.styleable.CustomProgressBar_secondColor, Color.RED);
//第二个参数的方法的作用是转换成标准尺寸。 如本文中此的意思是20“dp”
circlewidth=a.getDimensionPixelSize(R.styleable.CustomProgressBar_circleWidth,(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()));
speed=a.getInt(R.styleable.CustomProgressBar_speed, 10);
//获取typearray结束后一定要调用recycle方法,防止对下次调用的影响。
a.recycle();
//初始化画笔
mpaint=new Paint();
//启动一条线程,使进度条自增,再睡眠speed毫秒,speed值越小,进度条速度越快。
//next用于切换进度条状态(true的时候旋转的是第一个颜色进度条,false时旋转的是第二个颜色进度条)
new Thread(){
@Override
public void run() {
while(true){
mprogress++;
if(mprogress==360){
mprogress=0;
if(next){
next=false;
}else{
next=true;
}
}
//重新构造视图
invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 一个Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。
在这么多线程当中,把主要是负责控制UI界面的显示、更新和控件交互的线程称为UI线程,由于onCreate()方法是由UI线程执行的,所以也可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。
invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。
而postInvalidate()在工作者线程中被调用
invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。
而postInvalidate()在工作者线程中被调用
postInvalidate();
try {
sleep(speed);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
}
//重写绘制视图的方法
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
center=getWidth()/2;//x坐标
radius=center-circlewidth/2;//半径
mpaint.setStrokeWidth(circlewidth);//设置线条宽度
mpaint.setAntiAlias(true);//设置抗锯齿
mpaint.setStyle(Paint.Style.STROKE);
//设置绘制范围
RectF rectf=new RectF(center-radius,center-radius, center+radius, center+radius);
if(!next){//初始状态为false 第一层直接绘制,第二层按照扇形绘制,角度为动态变化的mprogress
mpaint.setColor(FirstColor);
//x坐标 y坐标 半径 画笔
canvas.drawCircle(center, center, radius, mpaint);
mpaint.setColor(SecondColor);
//扇形区域 初始角度 扇形角度 是否填充 画笔
canvas.drawArc(rectf, -90, mprogress, false, mpaint);
}else{//第二层绘制完成后,执行此方法,底部绘制第二层,第一层按照扇形绘制。
mpaint.setColor(SecondColor);
canvas.drawCircle(center, center, radius, mpaint);
mpaint.setColor(FirstColor);
canvas.drawArc(rectf, -90, mprogress, false, mpaint);
}
}
}
3.在布局中调用该控件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
<!--这一句必须加,意为指向自定义控件属性 “app”可自由定义名称(com.example.mycustomview01为项目包名) 该句也可写为 xmlns:app="http://schemas.android.com/apk/res-auto"-->
xmlns:app="http://schemas.android.com/apk/res/com.example.mycustomview01"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.example.mycustomview01.Mycustomview
android:layout_width="200dp"
android:layout_height="200dp"
app:firstColor="#0000aa"
app:secondColor="#00aa00"
app:circleWidth="40dp"
android:background="#33333333"
app:speed="50"/>
</RelativeLayout>
下附源码