先看效果图吧

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

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

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

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

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

  1. package com.yztc.customprogressview;
  2.  
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.Paint;
  8. import android.graphics.RectF;
  9. import android.util.AttributeSet;
  10. import android.view.View;
  11.  
  12. /**
  13. * 自定义的
  14. */
  15. public class ProgressView extends View {
  16. private int sweepStep = 10;//扇形变换的步长(就是角度)
  17. private int padding = 40;//外边框距离扇形的距离 填充
  18. private int circleColor = Color.GRAY;//边框的颜色
  19. private int sweepColor = Color.BLUE;//扇形颜色
  20. private int startAngle = 90;//起始角度
  21. //设置外边框圆的边框粗细
  22. private int storke = 20;
  23.  
  24. private int sweepAngle = 0;//扫过的角度
  25.  
  26. private static final int DEFAULT_WIDTH = 200;
  27. private static final int DEFAULT_HEIGHT = 200;
  28.  
  29. public ProgressView(Context context) {
  30. super(context);
  31. }
  32.  
  33. //如果要在xml文件中使用该自定义控件,则必须重写两个参数的构造函数
  34. //因为我们使用自定义的属性的时候,会默认传递过来一个AttributeSet集合
  35. public ProgressView(Context context, AttributeSet attrs) {
  36. super(context, attrs);
  37. TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);
  38. if (array != null) {
  39. //获取我们在xml中设置的各个自定义属性
  40. sweepStep = array.getInteger(R.styleable.ProgressView_sweepStep, sweepStep);
  41. padding = array.getInteger(R.styleable.ProgressView_padding, padding);
  42. circleColor = array.getColor(R.styleable.ProgressView_circleColor, circleColor);
  43. sweepColor = array.getColor(R.styleable.ProgressView_sweepColor, sweepColor);
  44. startAngle = array.getInteger(R.styleable.ProgressView_startAngle, startAngle);
  45.  
  46. //回收TypeArray资源
  47. array.recycle();
  48. }
  49. }
  50.  
  51. /**
  52. * 先绘制外边框 --内部扇形
  53. *
  54. * @param canvas
  55. */
  56. @Override
  57. protected void onDraw(Canvas canvas) {
  58. Paint mPaint = new Paint();
  59. mPaint.setAntiAlias(true); //设置抗锯齿
  60. //绘制外层的圆框
  61. mPaint.setColor(circleColor);
  62. mPaint.setStrokeWidth(storke);
  63. mPaint.setStyle(Paint.Style.STROKE);//设置圆形为空心的圆
  64. //这里我们得到控件的Height和Width,根据Heigh和Width来确定圆心的位置,来绘制外层圆
  65. canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - storke / 2, mPaint);
  66. // Log.d("tag", "onDraw: "+getWidth());
  67. invalidate();//请求重新绘制view
  68.  
  69. //绘制内部的扇形
  70. mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
  71. mPaint.setColor(sweepColor);
  72. /*
  73. drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
  74. RectF oval 指定扇形的矩形容器对象 指定圆弧的外轮廓的矩形
  75. float startAngle 表示圆弧的起始角度
  76. float sweepAngle 表示圆弧走过扫过的角度 顺时针方向
  77. boolean useCenter 如果设置为true 在绘制圆弧时将圆心包括在内,是指以一个固定的圆心来绘制弧形(扇形),
  78. 如果指定为false,则不规则绘制扇形
  79. Paint paint 画笔 颜色 填充
  80.  
  81. public RectF(float left, float top, float right, float bottom)
  82. float left 矩形的左边点(左切点)的x坐标
  83. float top 矩形上边点(上切点)的y轴坐标
  84. float right, 矩形的右边点(右切点)的x坐标
  85. float bottom 矩形的下边点(下切点)的y坐标
  86. */
  87. //RectF中的四个参数,分别对应其内切圆的四个切点的坐标
  88. RectF rectF = new RectF(padding + storke, padding + storke, getWidth() - padding - storke, getWidth() - padding - storke);
  89. canvas.drawArc(rectF, startAngle, sweepAngle, true, mPaint);
  90.  
  91. sweepAngle += sweepStep;//根据步长更新扫过的角度
  92. sweepAngle = sweepAngle > 360 ? 0 : sweepAngle;
  93. invalidate();//重绘view
  94. }
  95.  
  96. //因为我们是继承的View来自定义的View,所以onMeasure()方法要重写
  97. @Override
  98. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  99. int wMode = MeasureSpec.getMode(widthMeasureSpec);
  100. int hMode = MeasureSpec.getMode(heightMeasureSpec);
  101. int wSize = MeasureSpec.getSize(widthMeasureSpec);
  102. int hSize = MeasureSpec.getSize(heightMeasureSpec);
  103.  
  104. //因为绘制的是圆,所以判断一下高度或者宽度中的一个就可以。
  105. switch (wMode) {
  106. case MeasureSpec.AT_MOST://android:layout_width="warp_content"
  107. //获取屏幕像素
  108. float density = getResources().getDisplayMetrics().density;
  109. wSize = (int) (DEFAULT_WIDTH * density);
  110. hSize = (int) (DEFAULT_HEIGHT * density);
  111. break;
  112. //当在xml中指定控件的宽高为match_parent或者指定数值的宽高时,回调以下代码
  113. case MeasureSpec.EXACTLY://android:layout_width="match_parent" android:layout_width="40dp"
  114. wSize = hSize = Math.min(wSize, hSize);
  115. break;
  116. }
  117. //只要重写onMeasure()方法,一定要调用以下方法,不然会报错
  118. setMeasuredDimension(wSize, hSize);
  119. }
  120. }

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

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

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. xmlns:app="http://schemas.android.com/apk/res-auto"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. android:paddingBottom="@dimen/activity_vertical_margin"
  9. android:paddingLeft="@dimen/activity_horizontal_margin"
  10. android:paddingRight="@dimen/activity_horizontal_margin"
  11. android:paddingTop="@dimen/activity_vertical_margin"
  12. tools:context="com.yztc.customprogressview.MainActivity">
  13.  
  14. <com.yztc.customprogressview.ProgressView
  15. android:id="@+id/pv"
  16. android:layout_width="match_parent"
  17. android:layout_height="match_parent"
  18. android:background="#0000ff"
  19. app:padding="20"
  20. app:circleColor="#aa0000"
  21. app:sweepColor="#00aa00"
  22. app:sweepStep="1"
  23. app:startAngle="180"
  24. />
  25. </RelativeLayout>

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

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

还有需要注意的是:

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

自定义View--一个简单地圆形Progress效果的更多相关文章

  1. python+selenium之自定义封装一个简单的Log类

    python+selenium之自定义封装一个简单的Log类 一. 问题分析: 我们需要封装一个简单的日志类,主要有以下内容: 1. 生成的日志文件格式是 年月日时分秒.log 2. 生成的xxx.l ...

  2. Python+Selenium中级篇之8-Python自定义封装一个简单的Log类《转载》

    Python+Selenium中级篇之8-Python自定义封装一个简单的Log类: https://blog.csdn.net/u011541946/article/details/70198676

  3. Android 自定义View修炼-Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)

    一.概述 Android实现圆角矩形,圆形或者椭圆等图形,一般主要是个自定义View加上使用Xfermode实现的.实现圆角图片的方法其实不少,常见的就是利用Xfermode,Shader.本文直接继 ...

  4. Android -- 自定义view实现keep欢迎页倒计时效果

    1,最近打开keep的app的时候,发现它的欢迎页面的倒计时效果还不错,所以打算自己来写写,然后就有了这篇文章. 2,还是老规矩,先看一下我们今天实现的效果 相较于我们常见的倒计时,这次实现的效果是多 ...

  5. Python之自定义封装一个简单的Log类

    参考:http://www.jb51.net/article/42626.htm 参考:http://blog.csdn.net/u011541946/article/details/70198676 ...

  6. 自定义View绘制简单的圆环的实现

    package com.loaderman.mywave; import android.content.Context; import android.graphics.Canvas; import ...

  7. 自定义View(1)简单流程及示例模板

    1,继承View , ViewGroup,或TextView等等 2,绘制相关的api, canvas 画布, paint 画笔 2,重写重要的函数(注意这个顺序) onMeasure 属于View的 ...

  8. android自定义view仿照MIUI中音量控制效果

    先看效果图: 这就是miui中的音量效果图,实现思路是自定义视图,绘制圆环,然后设置进度显示. 核心代码在onDraw中实现如下: @Override protected void onDraw(Ca ...

  9. 一个简单的tr:hover效果

    昨天,搞项目的时候,在一个小问题上卡了40分钟,现在想想,还是平时比较少去注意一些细节,经过这次,一定要去多注意细节了. 好了废话不多说,我现在说明下遇到的问题,一个表格中,要求是当鼠标滑过每一行时, ...

随机推荐

  1. TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE

    TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE 的区别 TASK_INTERRUPT ...

  2. Zabbix监控解决方案

    思通运维监控主要用来监控IT 基础设施组件的可用性和性能.监控项目是不受限制的,并且可以对IT 基础设施健康状态进行复杂分析.通过确定IT 系统问题的“来源”,使用户快速响应故障来降低宕机成本. 网络 ...

  3. Netty4.x中文教程系列(三) Hello World !详解

    Netty 中文教程 (二) Hello World !详解 上一篇文章,笔者提供了一个Hello World 的Netty示例. 时间过去了这么久,准备解释一下示例代码. 1.HelloServer ...

  4. DeepLearning常用库简要介绍与对比

    网上近日流传一张DL相关库在Github上的受关注度对比(数据应该是2016/03/15左右统计的): 其中tensorflow,caffe,keras和Theano排名比较靠前. 今日组会报告上tj ...

  5. C# 读取指定URL的内容

    #region 读取指定URL的内容 /// <summary> /// 读取指定URL的内容 /// </summary> /// <param name=" ...

  6. [原]1856-More is better-基础并查集

    思路:注意n为0的时候输出1,还有内存.这题是数据水了,要不我的Count[ ]数组,开10^5绝对会WA.离散化还没想清楚,想清楚了再更新代码.[水过代码下面是正经的AC代码,其实这道题不用离散化, ...

  7. IO(一)

    文件相关 package com.bjsxt.io.file; import java.io.File; /** * 两个常量 * 1.路径分隔符 ; * 2.名称分隔符 /(windows) /(l ...

  8. CString和string

    CString和string(一) 概述 string和CString均是字符串模板类,string为标准模板类(STL)定义的字符串类,已经纳入C++标准之中: CString(typedef CS ...

  9. Open Explorer Plugin for Eclipse (eclipse 插件 在eclipse里面打开文件目录)

    就是在eclipse里面直接打开文件所在的目录地址 只要将下面的jar 文件放到你的 “$ECLIPSE_HOME/plugins”  下面,重启eclipse就ok了 要想卸载的话  停止eclip ...

  10. HDU 2686 (双线程) Matrix

    这也是当初卡了很久的一道题 题意:从左上角的格子出发选一条路径到右上角然后再回到左上角,而且两条路径除了起点和终点不能有重合的点.问所经过的格子中的最大和是多少 状态设计:我们可以认为是从左上角出发了 ...