Android自定义view之环形等候控件的实现
本篇博客参考鸿洋_大神的《Android 自定义View (三) 圆环交替 等待效果》
拖了这么久才开始更新****,着实是懒到家了,写这篇博客的目的就是为了帮助更多的android入门开发者更多的了解自定义控件,毕竟自定义控件对新手来说还是比较神秘的,多说无益,直接上图:
以上就是今天我们要实现的效果,乍一看是不是觉得高端大气上档次,完全没有什么头绪怎么去实现这么“高端”的东西。还会不定时的反问自己可以吗?对,你可以的。让我们一起来学习如何写这样的控件吧。
【前言】自定义view 的几个步骤
- 自定义view的属性
- 在view 的构造方法中获取我们自定义的属性的值
- 重写onMeasure方法(有时不需要重写这个方法)
- 重写onDraw方法
【正文】
项目结构图奉上:
我们先来分析一下这个控件。这个控件主要有两种颜色,加载的速度,圆环的宽度,好像也没有其他属性值了。
1.自定义属性:attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="firstColor" format="color"/> <attr name="secondColor" format="color"/> <attr name="circleWidth" format="dimension"/> <attr name="speed" format="integer"/> <declare-styleable name="CustomProgressbar"> <attr name="firstColor"/> <attr name="secondColor"/> <attr name="circleWidth"/> <attr name="speed"/> </declare-styleable> </resources>
这里的format主要常见的有以下几种属性:reference 、color、boolean、dimension、float、integer、string、fraction、enum、flag。更多用法以及如何在初始化时获取相应的值,可以百度,这里不是我们的重点。
2.在构造方法中获取我们自定义的属性:CustomProgressbar.java
// 设置第一圈颜色 private int mFirstColor=Color.GREEN; // 设置第二圈颜色 private int mSecondColor=Color.RED; // 设置圈的宽度 private int mCircleWidth=20; // 设置颜色填充画笔 private Paint mPaint; // 设置当前进度 private int mProgress; // 设置当前进度加载速度 private int speed=20; // 是否开始下一个 private boolean isNext = false; public CustomProgressbar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomProgressbar(Context context) { this(context, null); } /** * 必要的初始化,获取一些自定义的值 * * @param context * @param attrs * @param defStyle */ public CustomProgressbar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 获取自定义的属性集合 TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomProgressbar, defStyle, 0); // 获取自定义属性的个数 int n = array.getIndexCount(); Log.i("test", "自定义属性的个数:" + n); // 遍历属性值 for (int i = 0; i < n; i++) { int attr = array.getIndex(i); Log.i("test", "自定义的属性为:"+attr); switch (attr) { case R.styleable.CustomProgressbar_firstColor: // 获取第一圈颜色值 mFirstColor = array.getColor(attr, Color.GREEN); break; case R.styleable.CustomProgressbar_secondColor: // 获取第一圈颜色值 mSecondColor = array.getColor(attr, Color.RED); break; case R.styleable.CustomProgressbar_circleWidth: // 设置默认圈的宽度为20px mCircleWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics())); break; case R.styleable.CustomProgressbar_speed: // 获取默认加载速度 speed = array.getInt(attr, 20); break; } } // 回收 array.recycle(); mPaint = new Paint(); // 绘图线程 此线程为耗时线程,放在子线程中执行,防止主线程的卡顿 new Thread() { public void run() { while (true) { mProgress++; if (mProgress == 360) { mProgress = 0; // 如果没有开始下一个,则设置isNext为true if (!isNext) { isNext = true; } else { isNext = false; } } // 刷新UI // postInvalidate()此方法可以直接在UI线程调用,invalidate()则需要在handler中进行调用 postInvalidate(); try { Thread.sleep(speed); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); }
很多童鞋肯定会问,为什么这个操作要放在thread里进行操作?重写view是一个耗时操作,所以我们这里就直接放在子线程里进行重绘了,防止程序卡死。还有一个需要注意的就是postInvalidate()方法。postInvalidate()此方法可以直接在UI线程调用,invalidate()则需要在handler中进行调用,想要了解更多的童鞋也可以自己百度两者之间的区别。
3.重写onMeasure方法:本例中没有用到重写onMeasure方法,此处就不贴代码了。
4.重写onDraw方法,进行view 的绘制:CustomProgressbar.java
@Override protected void onDraw(Canvas canvas) { // 获取圆心的x坐标 int center = getWidth() / 2; // 获取圆的半径 int radius = center - mCircleWidth / 2; // 设置填充的宽度 mPaint.setStrokeWidth(mCircleWidth); mPaint.setAntiAlias(true); // 设置填充的style mPaint.setStyle(Paint.Style.STROKE); // new RectF(left, top, right, bottom) 为距离x轴,y轴之间的距离 // 定义rect的形状 RectF f = new RectF(center - radius, center - radius, center + radius, center + radius); if (!isNext) { // 第一圈颜色完整,第二圈颜色跑 mPaint.setColor(mFirstColor);// 设置画笔颜色 // 画出圆环 canvas.drawCircle(center, center, radius, mPaint); // 设置圆环颜色 mPaint.setColor(mSecondColor); /* * public void drawArc(RectF oval, float startAngle, float sweepAngle, * boolean useCenter, Paint paint) oval :指定圆弧的外轮廓矩形区域。 startAngle: * 圆弧起始角度,单位为度。 sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。 useCenter: * 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。 paint: 绘制圆弧的画板属性,如颜色,是否填充等。 */ canvas.drawArc(f, -90, mProgress, false, mPaint); } else { // 第一圈颜色完整,第二圈颜色跑 mPaint.setColor(mSecondColor);// 设置画笔颜色 // 画出圆环 canvas.drawCircle(center, center, radius, mPaint); // 设置圆环颜色 mPaint.setColor(mFirstColor); /* * public void drawArc(RectF oval, float startAngle, float sweepAngle, * boolean useCenter, Paint paint) oval :指定圆弧的外轮廓矩形区域。 startAngle: * 圆弧起始角度,单位为度。 sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。 useCenter: * 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。 paint: 绘制圆弧的画板属性,如颜色,是否填充等。 */ canvas.drawArc(f, -90, mProgress, false, mPaint); } }
这里关于drawArc的方法,我已经加了详细的注释,不明白的同学可以自己百度一下。
由于上面贴的是片段代码,这里给出CustomProgressbar.java的全部代码:
package com.beyole.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; import com.beyole.circlewaitting.R; public class CustomProgressbar extends View { // 设置第一圈颜色 private int mFirstColor=Color.GREEN; // 设置第二圈颜色 private int mSecondColor=Color.RED; // 设置圈的宽度 private int mCircleWidth=20; // 设置颜色填充画笔 private Paint mPaint; // 设置当前进度 private int mProgress; // 设置当前进度加载速度 private int speed=20; // 是否开始下一个 private boolean isNext = false; public CustomProgressbar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomProgressbar(Context context) { this(context, null); } /** * 必要的初始化,获取一些自定义的值 * * @param context * @param attrs * @param defStyle */ public CustomProgressbar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 获取自定义的属性集合 TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomProgressbar, defStyle, 0); // 获取自定义属性的个数 int n = array.getIndexCount(); Log.i("test", "自定义属性的个数:" + n); // 遍历属性值 for (int i = 0; i < n; i++) { int attr = array.getIndex(i); Log.i("test", "自定义的属性为:"+attr); switch (attr) { case R.styleable.CustomProgressbar_firstColor: // 获取第一圈颜色值 mFirstColor = array.getColor(attr, Color.GREEN); break; case R.styleable.CustomProgressbar_secondColor: // 获取第一圈颜色值 mSecondColor = array.getColor(attr, Color.RED); break; case R.styleable.CustomProgressbar_circleWidth: // 设置默认圈的宽度为20px mCircleWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics())); break; case R.styleable.CustomProgressbar_speed: // 获取默认加载速度 speed = array.getInt(attr, 20); break; } } // 回收 array.recycle(); mPaint = new Paint(); // 绘图线程 此线程为耗时线程,放在子线程中执行,防止主线程的卡顿 new Thread() { public void run() { while (true) { mProgress++; if (mProgress == 360) { mProgress = 0; // 如果没有开始下一个,则设置isNext为true if (!isNext) { isNext = true; } else { isNext = false; } } // 刷新UI // postInvalidate()此方法可以直接在UI线程调用,invalidate()则需要在handler中进行调用 postInvalidate(); try { Thread.sleep(speed); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } @Override protected void onDraw(Canvas canvas) { // 获取圆心的x坐标 int center = getWidth() / 2; // 获取圆的半径 int radius = center - mCircleWidth / 2; // 设置填充的宽度 mPaint.setStrokeWidth(mCircleWidth); mPaint.setAntiAlias(true); // 设置填充的style mPaint.setStyle(Paint.Style.STROKE); // new RectF(left, top, right, bottom) 为距离x轴,y轴之间的距离 // 定义rect的形状 RectF f = new RectF(center - radius, center - radius, center + radius, center + radius); if (!isNext) { // 第一圈颜色完整,第二圈颜色跑 mPaint.setColor(mFirstColor);// 设置画笔颜色 // 画出圆环 canvas.drawCircle(center, center, radius, mPaint); // 设置圆环颜色 mPaint.setColor(mSecondColor); /* * public void drawArc(RectF oval, float startAngle, float sweepAngle, * boolean useCenter, Paint paint) oval :指定圆弧的外轮廓矩形区域。 startAngle: * 圆弧起始角度,单位为度。 sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。 useCenter: * 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。 paint: 绘制圆弧的画板属性,如颜色,是否填充等。 */ canvas.drawArc(f, -90, mProgress, false, mPaint); } else { // 第一圈颜色完整,第二圈颜色跑 mPaint.setColor(mSecondColor);// 设置画笔颜色 // 画出圆环 canvas.drawCircle(center, center, radius, mPaint); // 设置圆环颜色 mPaint.setColor(mFirstColor); /* * public void drawArc(RectF oval, float startAngle, float sweepAngle, * boolean useCenter, Paint paint) oval :指定圆弧的外轮廓矩形区域。 startAngle: * 圆弧起始角度,单位为度。 sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。 useCenter: * 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。 paint: 绘制圆弧的画板属性,如颜色,是否填充等。 */ canvas.drawArc(f, -90, mProgress, false, mPaint); } } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:beyole="http://schemas.android.com/apk/res/com.beyole.circlewaitting" android:layout_width="match_parent" android:layout_height="match_parent" > <com.beyole.view.CustomProgressbar android:layout_width="120dp" android:layout_height="120dp" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" beyole:circleWidth="20dp" beyole:firstColor="#D4F668" beyole:secondColor="#2F9DD2" beyole:speed="20" /> </RelativeLayout>
这里注意,我们定义了自己的命名控件,也就是
xmlns:beyole="http://schemas.android.com/apk/res/com.beyole.circlewaitting"这里的com.beyole.circlewaitting就是我们应用程序的包名。如何知道我们应用程序的包名?直接在AndroidManifest.xml文件中
主布局文件里面没有更改内容:MainActivity.java
package com.beyole.circlewaitting; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
github地址:https://github.com/xuejiawei/beyole_android_CircleWaitting
****源码下载地址:http://download.****.net/detail/xuejiawei123/9174395
版权声明:本文为博主原创文章,未经博主允许不得转载。
- 1楼a774057695昨天 14:15
- 这个demo是很好的,但是直接做为一个组件还是有问题的,这个名曰加载进度条,加载进度条,加载进度条,名称说三遍有利于体会其含义。所以应当开放一个接口接收进度,刷新(重绘图片显示)。n不然的话,这个组件真的想象不出使用在哪里。