Android自定义View实现简单的圆形Progress效果

时间:2021-05-20

先给大家展示下效果图,如果感觉不错,请参考实现思路:

我们要实现一个自定义的再一个圆形中绘制一个弧形的自定义View,思路是这样的:

  先要创建一个类ProgressView,继承自View类,然后重写其中的两个构造方法,一个是一个参数的,一个是两个参数的,因为我们要在xml文件中使用该自定义控件,所以必须要定义这个两个参数的构造函数。创建完了这个类后,我们先不去管它,先考虑我们实现的这个自定义View,我们想让它的哪些部分可以由使用者自己指定,比如说这个Demo中我们让他的外面圆的外边框颜色和宽度,还有扇形部分的颜色,扇形增长的速度等等属性,这时候我们要在项目工程目录的res/values目录下创建一个资源文件命名为attrs(注意,名字随意,只是大多数情况下都这么叫而已),然后我们在这个资源文件中添加我们想要的属性,如下:

<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="ProgressView"><!--circleColor 设置圆形边框的颜色 sweepColor设置扇形变换的颜色startAngle 设置起始角度 sweepStep 设置变换的步长--><attr name="circleColor" format="color|reference"></attr><attr name="sweepColor" format="color|reference"></attr><attr name="startAngle" format="integer"></attr><attr name="sweepStep" format="integer"></attr><attr name="padding" format="integer"></attr></declare-styleable></resources>

可以看到,<declare-styleable>标签中的name属性是为了方便我们获取AttributeSet时候使用,而<attr>标签中name,则是我们希望控件可以自定义的属性部分,类似于xml文件中的android:name=""等标签的使用。format属性是设置该属性可以接受什么类型的参数。

定义好了自定义资源类,我们开始写ProgressView中的主要代码:

package com.yztc.customprogressview;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.view.View;/*** 自定义的*/public class ProgressView extends View {private int sweepStep = 10;//扇形变换的步长(就是角度)private int padding = 40;//外边框距离扇形的距离 填充private int circleColor = Color.GRAY;//边框的颜色private int sweepColor = Color.BLUE;//扇形颜色private int startAngle = 90;//起始角度//设置外边框圆的边框粗细private int storke = 20;private int sweepAngle = 0;//扫过的角度private static final int DEFAULT_WIDTH = 200;private static final int DEFAULT_HEIGHT = 200;public ProgressView(Context context) {super(context);}//如果要在xml文件中使用该自定义控件,则必须重写两个参数的构造函数//因为我们使用自定义的属性的时候,会默认传递过来一个AttributeSet集合public ProgressView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);if (array != null) {//获取我们在xml中设置的各个自定义属性sweepStep = array.getInteger(R.styleable.ProgressView_sweepStep, sweepStep);padding = array.getInteger(R.styleable.ProgressView_padding, padding);circleColor = array.getColor(R.styleable.ProgressView_circleColor, circleColor);sweepColor = array.getColor(R.styleable.ProgressView_sweepColor, sweepColor);startAngle = array.getInteger(R.styleable.ProgressView_startAngle, startAngle);//回收TypeArray资源array.recycle();}}/*** 先绘制外边框 --内部扇形** @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {Paint mPaint = new Paint();mPaint.setAntiAlias(true); //设置抗锯齿//绘制外层的圆框mPaint.setColor(circleColor);mPaint.setStrokeWidth(storke);mPaint.setStyle(Paint.Style.STROKE);//设置圆形为空心的圆//这里我们得到控件的Height和Width,根据Heigh和Width来确定圆心的位置,来绘制外层圆canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - storke / 2, mPaint);// Log.d("tag", "onDraw: "+getWidth());invalidate();//请求重新绘制view//绘制内部的扇形mPaint.setStyle(Paint.Style.FILL_AND_STROKE);mPaint.setColor(sweepColor);//RectF中的四个参数,分别对应其内切圆的四个切点的坐标RectF rectF = new RectF(padding + storke, padding + storke, getWidth() - padding - storke, getWidth() - padding - storke);canvas.drawArc(rectF, startAngle, sweepAngle, true, mPaint);sweepAngle += sweepStep;//根据步长更新扫过的角度sweepAngle = sweepAngle > 360 ? 0 : sweepAngle;invalidate();//重绘view}//因为我们是继承的View来自定义的View,所以onMeasure()方法要重写@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int wMode = MeasureSpec.getMode(widthMeasureSpec);int hMode = MeasureSpec.getMode(heightMeasureSpec);int wSize = MeasureSpec.getSize(widthMeasureSpec);int hSize = MeasureSpec.getSize(heightMeasureSpec);//因为绘制的是圆,所以判断一下高度或者宽度中的一个就可以。switch (wMode) {case MeasureSpec.AT_MOST://android:layout_width="warp_content"//获取屏幕像素float density = getResources().getDisplayMetrics().density;wSize = (int) (DEFAULT_WIDTH * density);hSize = (int) (DEFAULT_HEIGHT * density);break;//当在xml中指定控件的宽高为match_parent或者指定数值的宽高时,回调以下代码case MeasureSpec.EXACTLY://android:layout_width="match_parent" android:layout_width="40dp"wSize = hSize = Math.min(wSize, hSize);break;}//只要重写onMeasure()方法,一定要调用以下方法,不然会报错setMeasuredDimension(wSize, hSize);}}

我们先画一个外部的圆,也就是都用drawCircle()方法,这里我们调用getWidth(),getHeight()表示得到布局中设置的控件的尺寸,因为是圆,所以可以使用getWidth()/2来表示圆心位置。而画内部弧形的时候,确定其圆心位置,左部点的坐标是外面圆的边框加上弧形与原的间距padding来确定,右侧点的x坐标则是getWidth()得到总长度,减去边框宽度,再减去padding得到,上边距和下边距同样,不明白的画一个图立马明白。大部分不好理解的地方都有注释,只要认真看不会看不明白的~

然后就是在布局中引入我们的自定义View了:

<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"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="com.yztc.customprogressview.MainActivity"><com.yztc.customprogressview.ProgressViewandroid:id="@+id/pv"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#0000ff"app:padding="20"app:circleColor="#aa0000"app:sweepColor="#00aa00"app:sweepStep="1"app:startAngle="180"/></RelativeLayout>

需要注意的是我们需要在布局的跟标签下面加上这么一段代码:xmlns:app="http://schemas.android.com/apk/res-auto"

用来指定我们可以使用自己再attrs中自定义的属性啦~,使用的形式就是app:你定义的属性。当然,这个app也不是固定的写法,只要跟上面你加的那段代码的xmlns后面的字段一致就可以了~

还有需要注意的是:

默认使用Canvas类的drawCircle()方法来画圆的时候,圆的半径是你指定的半径,再加上一半的边长,比如你的边框size=10,radius=50,则实际空心部分的半径为55.注意这点,否则画出来的圆的四个切点位置会与其他位置有些不一样,类似出现锯齿的效果。具体请看drawCircle()的Rect边框问题。

以上所述是小编给大家介绍的Android自定义View实现简单的圆形Progress效果,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章