转载著名出处 :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;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //加载 Fragment        if (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() {        }        @Override        public 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;        }    }            @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.main, menu);        return true;    }    @Override    public 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;

--工程示例:




更多相关文章

  1. Android中软键盘(输入法)收起的方法
  2. android 耳机左右声道接反,软件如何修正
  3. Android(安卓)ListView根据项数的大小自动改变高度
  4. android View绘制流程
  5. [Android]在代码里运行另一个程序的方法
  6. Android中安全退出程序的六种方法
  7. Android事件分发机制与嵌套导致触摸事件冲突的解决方案
  8. 设置android studio textview 的点击打开新activity页面方法
  9. Android中JSON解析细解及实例

随机推荐

  1. google maps api 地址
  2. Android(安卓)Activity界面切换添加动画
  3. android 服务前台运行startForeground
  4. 第一节(搭建环境)
  5. Android系统信息获取
  6. android中GridView关于间距的属性值介绍
  7. android工具链与GNU工具链的比较
  8. 对android的Activity切换时输入法不弹出
  9. Android短彩信数据库信息整理
  10. Android在顶部标题栏添加按钮