转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487

代码下载 :

-- CSDN 下载地址http://download.csdn.net/detail/han1202012/8069497 ;

-- GitHub 地址https://github.com/han1202012/CircleProcess.git ;

-- 工程示例 :

一. 相关知识点解析

1. 自定义 View 组件构造方法

构造方法 : 自定义的 View 组件, 一般需要实现 三个构造方法, 分别有 一个, 两个, 三个参数;

-- 一个参数public CircleProcess(Context context);

-- 两个参数public CircleProcess(Context context, AttributeSet attrs);

-- 三个参数public CircleProcess(Context context, AttributeSet attrs, int defStyle);

构造方法注意点 :

-- 调用上级方法 : 每个构造方法中必须调用 super() 方法, 方法中的参数与该构造方法参数一样;

-- 常用构造方法 : 一般在2参数构造方法中实现逻辑;

构造方法示例 :

 /** 画笔 */private Paint mPaint;/** 上下文对象 */private Context mContext;/** 进度条的值 */private int mProcessValue;public CircleProcess(Context context, AttributeSet attrs) {super(context, attrs);// 初始化成员变量 ContextmContext = context;// 创建画笔, 并设置画笔属性mPaint = new Paint();// 消除绘制时产生的锯齿mPaint.setAntiAlias(true);// 绘制空心圆形需要设置该样式mPaint.setStyle(Style.STROKE);}/*** 自定义布局实现的 只有 Context 参数的构造方法* @param context*/public CircleProcess(Context context) {super(context);}/*** 自定义布局实现的 三个参数的构造方法* @param context* @param attrs* @param defStyle*/public CircleProcess(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}

2. dip 和 px 单位转换

(1) dip 转 px

公式 :

-- 基本公式px / dip = dpi / 160;

-- 计算公式 : px = dpi / 160 * dip;

一些概念解析 :

-- dpi 概念 : dpi (dot per inch), 每英寸像素数 归一化的值 120 160 240 320 480;

-- 区分 dpi 和 density : dpi 是归一化的值, density 是实际的值, 可能不是整数;

代码示例 :

 /*** 将手机的 设备独立像素 转为 像素值* *      公式 : px / dip = dpi / 160*                px = dip * dpi / 160;* @param context*                 上下文对象* @param dpValue*                 设备独立像素值* @return*              转化后的 像素值*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}

(2) px 转 dip

公式 :

-- 基本公式 : px / dip = dpi / 160;

-- 计算公式 : dip = 160 / dpi * px;

代码 :

 /*** 将手机的 像素值 转为 设备独立像素*        公式 : px/dip = dpi/160*                dip = px * 160 / dpi*              dpi (dot per inch) : 每英寸像素数 归一化的值 120 160 240 320 480;*             density : 每英寸的像素数, 精准的像素数, 可以用来计算准确的值*              从 DisplayMetics 中获取的* @param context*               上下文对象* @param pxValue*                 像素值* @return*              转化后的 设备独立像素值*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}

3. 关于 组件 宽 和 高 计算

(1) MesureSpec 简介

MeasureSpec 组成 : 每个 MeasureSpec 都代表一个 int 类型数值, 共 32 位, 前两位是模式位, 后 30 位 是数值位;

-- 模式 : int 类型的前 2 位, 共有三种模式, 通过 MeasureSpec.getMode(int) 方法获取, 下面会详细介绍模式;

-- 大小 : int 类型的后 30 位, 通过 MeasureSpec.getSize(int) 方法获取大小;

MeasureSpec 模式简介 : 注意下面的数字是二进制的

-- 00 : MeasureSpec.UNSPECIFIED, 未指定模式;
-- 01 : MeasureSpec.EXACTLY, 精准模式;
-- 11 : MeasureSpec.AT_MOST, 最大模式;

MeasureSpec 常用方法介绍 :

-- MeasureSpec.getMode(int) : 获取模式;
-- MeasureSpec.getSize(int) : 获取大小;
-- MeasureSpec.makeMeasureSpec(int size, int mode) : 创建一个 MeasureSpec;
-- MeasureSpec.toString(int) : 模式 + 大小 字符串;

(2) 通过 MeasureSpec 计算组件大小

计算方法 :

-- 精准模式 : 该模式下 长度的大小 就是 从 MeasureSpec 中获取的 size 大小;

-- 最大模式 : 获取 默认大小 和 size 中的较小的那个;

-- 未定义模式 : 默认大小;

通用计算方法代码 :

 /*** 获取组件宽度* * MeasureSpec : 该 int 类型有 32 位, 前两位是状态位, 后面 30 位是大小值;*         常用方法 : *        -- MeasureSpec.getMode(int) : 获取模式*      -- MeasureSpec.getSize(int) : 获取大小*      -- MeasureSpec.makeMeasureSpec(int size, int mode) : 创建一个 MeasureSpec;*      -- MeasureSpec.toString(int) : 模式 + 大小 字符串*      *      模式介绍 : 注意下面的数字是二进制的*      -- 00 : MeasureSpec.UNSPECIFIED, 未指定模式;*      -- 01 : MeasureSpec.EXACTLY, 精准模式;*      -- 11 : MeasureSpec.AT_MOST, 最大模式;*      *      注意 : 这个 MeasureSpec 模式是在 onMeasure 方法中自动生成的, 一般不用去创建这个对象*      * @param widthMeasureSpec*                 MeasureSpec 数值* @return*               组件的宽度*/private int measure(int measureSpec) {//返回的结果, 即组件宽度int result = 0;//获取组件的宽度模式int mode = MeasureSpec.getMode(measureSpec);//获取组件的宽度大小 单位pxint size = MeasureSpec.getSize(measureSpec);if(mode == MeasureSpec.EXACTLY){//精准模式result = size;}else{//未定义模式 或者 最大模式//注意 200 是默认大小, 在 warp_content 时使用这个值, 如果组件中定义了大小, 就不使用该值result = dip2px(mContext, 200);if(mode == MeasureSpec.AT_MOST){//最大模式//最大模式下获取一个稍小的值result = Math.min(result, size);}}return result;}

(3) 设置 组件大小方法

setMeasuredDimension() 方法 : 该方法决定 View 组件的大小;

-- 使用场所 : 在 onMeasure() 方法中调用该方法, 就设置了组件的宽 和 高, 然后在其它位置调用 getWidth() 和 getHeight() 方法时, 获取的就是 该方法设置的值;

-- 代码示例 :

 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);/** setMeasuredDimension 方法 : 该方法决定当前的 View 的大小* 根据 View 在布局中的显示, 动态获取 View 的宽高* * 当布局组件 warp_content 时 : * 从 MeasureSpec 获取的宽度 : 492 高度 836 , * 默认 宽高 都是 120dip转化完毕后 180px * * 当将布局组件 的宽高设置为 240 dp : * 宽度 和 高度 MeasureSpec 获取的都是 360, 此时 MeasureSpec 属于精准模式* */setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec));}

4. 图形绘制

(1) 设置画笔

画笔相关方法 :

-- 消除锯齿 : setAntiAlias(boolean);

     // 消除绘制时产生的锯齿mPaint.setAntiAlias(true);

-- 绘制空心圆设置的样式 : setStyle(Style.STROKE);

     // 绘制空心圆形需要设置该样式mPaint.setStyle(Style.STROKE);

-- 绘制实心图形文字需要设置的样式 : mPaint.setStrokeWidth(0);

-- 设置画笔颜色 : setColor(Color.BLUE);

-- 设置文字大小 : setTextSize(float);

     //设置数字的大小, 注意要根据 内圆半径设置mPaint.setTextSize(innerRadius / 2);

(2) 绘制图形

绘制圆 : canvas.drawCircle(float cx, float cy, float radius, Paint paint);

-- cx 参数 : 圆心的 x 轴距离;

-- cy 参数 : 圆心的 y 轴距离;

-- radius 参数 : 半径;

-- paint : 画笔;

绘制圆弧 :

-- 创建圆弧 : RectF rectf = new RectF(left, top, right, bottom);

-- 绘制 : canvas.drawArc(rectf, 270, mProcessValue, false, mPaint);

-- 示例 :

     //创建圆弧对象RectF rectf = new RectF(left, top, right, bottom);//绘制圆弧 参数介绍 : 圆弧, 开始度数, 累加度数, 是否闭合圆弧, 画笔canvas.drawArc(rectf, 270, mProcessValue, false, mPaint);

绘制文字 : canvas.drawText(str, textX, textY, mPaint);

二. 代码示例

1. 自定义 View 代码

package cn.org.octopus.circle;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.ImageView;public class CircleProcess extends ImageView {/** 画笔 */private Paint mPaint;/** 上下文对象 */private Context mContext;/** 进度条的值 */private int mProcessValue;public CircleProcess(Context context, AttributeSet attrs) {super(context, attrs);// 初始化成员变量 ContextmContext = context;// 创建画笔, 并设置画笔属性mPaint = new Paint();// 消除绘制时产生的锯齿mPaint.setAntiAlias(true);// 绘制空心圆形需要设置该样式mPaint.setStyle(Style.STROKE);}/*** 自定义布局实现的 只有 Context 参数的构造方法* @param context*/public CircleProcess(Context context) {super(context);}/*** 自定义布局实现的 三个参数的构造方法* @param context* @param attrs* @param defStyle*/public CircleProcess(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//获取圆心的 x 轴位置int center = getWidth() / 2;/** 中间位置 x 减去左侧位置 的绝对值就是圆半径, * 注意 : 由于 padding 属性存在, |left - right| 可能与 width 不同*/int outerRadius = Math.abs(getLeft() - center);//计算内圆半径大小, 内圆半径 是 外圆半径的一般int innerRadius = outerRadius / 2;//设置画笔颜色mPaint.setColor(Color.BLUE);//设置画笔宽度mPaint.setStrokeWidth(2);//绘制内圆方法 前两个参数是 x, y 轴坐标, 第三个是内圆半径, 第四个参数是 画笔canvas.drawCircle(center, center, innerRadius, mPaint);/** 绘制进度条的圆弧* * 绘制图形需要 left top right bottom 坐标, 下面需要计算这个坐标*///计算圆弧宽度int width = outerRadius - innerRadius;//将圆弧的宽度设置给 画笔mPaint.setStrokeWidth(width);/** 计算画布绘制圆弧填入的 top left bottom right 值, * 这里注意给的值要在圆弧的一半位置, 绘制的时候参数是从中间开始绘制*/int top = center - (innerRadius + width/2);int left = top;int bottom = center + (innerRadius + width/2);int right = bottom;//创建圆弧对象RectF rectf = new RectF(left, top, right, bottom);//绘制圆弧 参数介绍 : 圆弧, 开始度数, 累加度数, 是否闭合圆弧, 画笔canvas.drawArc(rectf, 270, mProcessValue, false, mPaint);//绘制外圆mPaint.setStrokeWidth(2);canvas.drawCircle(center, center, innerRadius + width, mPaint);/** 在内部正中央绘制一个数字*///生成百分比数字String str = (int)(mProcessValue * 1.0 / 360 * 100) + "%"; /** 测量这个数字的宽 和 高*///创建数字的边界对象Rect textRect = new Rect();//设置数字的大小, 注意要根据 内圆半径设置mPaint.setTextSize(innerRadius / 2);mPaint.setStrokeWidth(0);//获取数字边界mPaint.getTextBounds(str, 0, str.length(), textRect);int textWidth = textRect.width();int textHeight = textRect.height();//根据数字大小获取绘制位置, 以便数字能够在正中央绘制出来int textX = center - textWidth / 2;int textY = center + textHeight / 2;//正式开始绘制数字canvas.drawText(str, textX, textY, mPaint);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);/** setMeasuredDimension 方法 : 该方法决定当前的 View 的大小* 根据 View 在布局中的显示, 动态获取 View 的宽高* * 当布局组件 warp_content 时 : * 从 MeasureSpec 获取的宽度 : 492 高度 836 , * 默认 宽高 都是 120dip转化完毕后 180px * * 当将布局组件 的宽高设置为 240 dp : * 宽度 和 高度 MeasureSpec 获取的都是 360, 此时 MeasureSpec 属于精准模式* */setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec));}/*** 获取组件宽度* * MeasureSpec : 该 int 类型有 32 位, 前两位是状态位, 后面 30 位是大小值;*       常用方法 : *        -- MeasureSpec.getMode(int) : 获取模式*      -- MeasureSpec.getSize(int) : 获取大小*      -- MeasureSpec.makeMeasureSpec(int size, int mode) : 创建一个 MeasureSpec;*      -- MeasureSpec.toString(int) : 模式 + 大小 字符串*      *      模式介绍 : 注意下面的数字是二进制的*      -- 00 : MeasureSpec.UNSPECIFIED, 未指定模式;*      -- 01 : MeasureSpec.EXACTLY, 精准模式;*      -- 11 : MeasureSpec.AT_MOST, 最大模式;*      *      注意 : 这个 MeasureSpec 模式是在 onMeasure 方法中自动生成的, 一般不用去创建这个对象*      * @param widthMeasureSpec*                 MeasureSpec 数值* @return*               组件的宽度*/private int measure(int measureSpec) {//返回的结果, 即组件宽度int result = 0;//获取组件的宽度模式int mode = MeasureSpec.getMode(measureSpec);//获取组件的宽度大小 单位pxint size = MeasureSpec.getSize(measureSpec);if(mode == MeasureSpec.EXACTLY){//精准模式result = size;}else{//未定义模式 或者 最大模式//注意 200 是默认大小, 在 warp_content 时使用这个值, 如果组件中定义了大小, 就不使用该值result = dip2px(mContext, 200);if(mode == MeasureSpec.AT_MOST){//最大模式//最大模式下获取一个稍小的值result = Math.min(result, size);}}return result;}/*** 将手机的 设备独立像素 转为 像素值* *        公式 : px / dip = dpi / 160*                px = dip * dpi / 160;* @param context*                 上下文对象* @param dpValue*                 设备独立像素值* @return*              转化后的 像素值*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 将手机的 像素值 转为 设备独立像素*        公式 : px/dip = dpi/160*                dip = px * 160 / dpi*              dpi (dot per inch) : 每英寸像素数 归一化的值 120 160 240 320 480;*             density : 每英寸的像素数, 精准的像素数, 可以用来计算准确的值*              从 DisplayMetics 中获取的* @param context*               上下文对象* @param pxValue*                 像素值* @return*              转化后的 设备独立像素值*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}/*** 获取当前进度值* @return*                 返回当前进度值*/public int getmProcessValue() {return mProcessValue;}/*** 为该组件设置进度值* @param mProcessValue*                设置的进度值参数*/public void setmProcessValue(int mProcessValue) {this.mProcessValue = mProcessValue;}}

2. Activity 代码

package cn.org.octopus.circle;import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;public class MainActivity extends Activity {private static CircleProcess circle_process;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//加载 Fragmentif (savedInstanceState == null) {getFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();}new CircleProcessAnimation().execute();}/*** 设置 异步任务, 在这个任务中 设置 圆形进度条的进度值* @author octopus **/class CircleProcessAnimation extends AsyncTask<Void, Integer, Void>{@Overrideprotected Void doInBackground(Void... arg0) {for(int i = 1; i <= 360; i ++){try {//激活圆形进度条显示方法publishProgress(i);//每隔 50 毫秒更新一次数据Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}return null;}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);//为圆形进度条组件设置进度值circle_process.setmProcessValue(values[0]);//刷新圆形进度条显示circle_process.invalidate();}}/*** 界面显示的 Fragment * @author octopus*/public static class PlaceholderFragment extends Fragment {public PlaceholderFragment() {}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View rootView = inflater.inflate(R.layout.fragment_main, container, false);circle_process = (CircleProcess) rootView.findViewById(R.id.circle_process);return rootView;}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {int id = item.getItemId();if (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}
}

3. 布局文件代码

<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"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="cn.org.octopus.circle.MainActivity$PlaceholderFragment"android:gravity="center"><cn.org.octopus.circle.CircleProcess android:id="@+id/circle_process"android:layout_width="300dip"android:layout_height="300dip"/></RelativeLayout>

代码下载 :

-- CSDN 下载地址 : http://download.csdn.net/detail/han1202012/8069497 ;

-- GitHub 地址 : https://github.com/han1202012/CircleProcess.git ;

-- 工程示例 :

【Android 应用开发】 自定义 圆形进度条 组件相关推荐

  1. android 环形时间显示_Android_Android实现自定义圆形进度条,今天无意中发现一个圆形进度 - phpStudy...

    Android实现自定义圆形进度条 今天无意中发现一个圆形进度,想想自己实现一个,如下图: 基本思路是这样的: 1.首先绘制一个实心圆 2.绘制一个白色实心的正方形,遮住实心圆 3.在圆的中心动态绘制 ...

  2. Android自定义圆形进度条

    Android自定义圆形进度条 github地址:https://github.com/opq1289/CircleProgressView 效果图: 无动画: 有动画: 整圆: 切割圆: 具体步骤: ...

  3. android绘制环形进度_Android动态自定义圆形进度条

    这篇文章主要介绍了Android动态自定义圆形进度条,需要的朋友可以参考下 效果图: A.绘制圆环,圆弧,文本 //1.画圆环 //原点坐标 float circleX = width / 2; fl ...

  4. android自定义圆形进度条,实现动态画圆效果

    自定义圆形进度条效果图如下:应用场景如动态显示分数等. view的自定义属性如下attr.xml <?xml version="1.0" encoding="UTF ...

  5. Vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果

    vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果 需求分析: 类似于大多数音乐播放器中等mini播放器控制按钮,显示播放进度,实时更新进度. progress-cir ...

  6. 自定义圆形进度条的实现方式

    如何自定义圆形进度条哪,也就是替换一下进度条的图片而已. 先分析一下,系统对进度条如何定义的: 咱们一般情况下载布局文件中这么书写: //在布局文件里的代码<ProgressBarandroid ...

  7. VC用MFC开发的圆形进度条控件

    DownLoad Src VC用MFC开发的圆形进度条控件 visualsan@yahoo.cn NUAA zss 在NBA2007游戏里,还有很多科幻电影里,经常可以看到圆形进度条.有的用来显示导弹 ...

  8. 微信小程序进度条样式_详解微信小程序——自定义圆形进度条

    微信小程序 自定义圆形进度条,具体如下: 无图无真相,先上图: 实现思路,先绘制底层的灰色圆圈背景,再绘制上层的蓝色进度条. 代码实现: JS代码: 页面初始化 options为页面跳转所带来的参数 ...

  9. 自定义圆形进度条 自定义倒计时进度条

    自定义圆形进度条 自定义倒计时进度条 版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003 此控件源码已开源到Github:https: ...

最新文章

  1. canvas绘制闹钟-方法1
  2. Arduino方式开发ESP32笔记:使用Preferences保存数据
  3. Android:安卓布局分类及布局和页面的关系
  4. java path util,Java URIUtil.canonicalPath方法代码示例
  5. PHP使用Zend Opcache之优化加速和缓存清理总结
  6. 免费报名 | DataFunCon:自然语言处理论坛
  7. 《Android内核剖析》读书笔记 第13章 View工作原理【View树遍历】
  8. HDU 1754 I Hate It 线段树RMQ
  9. 最新python中文手册_python中文手册下载
  10. word怎么只删除英语保留汉语或删除汉语保留英文
  11. 如何在论文后面插参考文献
  12. 前端开发工程师需要具备哪些专业技能?
  13. 多力食用油:技术赋能产品品质,健康深植品牌理念
  14. 微信公众号里的视频下载
  15. Android系列之Navigation的目的地(Destination)
  16. 教你如何使用 python 制作一个简单的密码本
  17. 城南花未开,星海依然在
  18. 〖Python 数据库开发实战 - Python与Redis交互篇⑪〗- 综合案例 - 新闻管理系统 - 缓存新闻数据至redis
  19. 快速实现抖音的分享登录(android) 1
  20. 如何利用测试自动化,实现 DevOps?

热门文章

  1. opencv imshow
  2. 链接多个数据库的方法
  3. Socket.IO 客户端 API IO
  4. Flask-Migrate拓展数据库表结构
  5. IHttpHandler的学习(0-2)
  6. CentOS7下安装ELK三件套
  7. oracle数据库访问order by不起作用分析
  8. Linux下rz/sz安装及使用方法
  9. jquery书写一个简易的二级联动
  10. java追加文本到文件末尾