先看效果(原谅我的渣像素),进度的刻度、宽度、颜色可以随意设定:

【项目github地址: https://github.com/zhangke3016/CircleLoading

实现起来并不难,通过本文,我们可以学到:

1、自定义属性的使用。

2、shader的使用

3、自定义View中对onmeasure的处理

4、增深对PathMeasure工具类的了解

5、最主要的是对自定义View有个比较清晰的思路认识

一、原理介绍

做这样一个进度效果,我们可以拆分如下步骤来实现:

1、从外部圆环开始测量绘制;

2、再加入刻度条效果;

3、再加入刻度随进度增加而增加效果;

4、增加自定义属性增加可定制性;

5、控件使用方法介绍

【zhangke3016 http://blog.csdn.net/zhangke3016

OK,有了这个思路,那我们开始吧:

1、测量绘制外部圆环

首先我们要开始绘制外部的圆环,这步很简单,主要是使用canvas的drawArc()方法,

    /*
     * @param oval 画弧线矩形区域
     * @param startAngle 开始的角度
     * @param sweepAngle 划过的角度
     * @param useCenter 如果为true 为实心圆弧
     * @param paint     画笔
     * /
 public void drawArc(RectF oval, float startAngle, float sweepAngle,boolean useCenter,Paint paint)

这个相对简单,主要是确定开始角度,并不断增加绘制划过角度,圆弧就出现在界面中了,这里需要注意的是RectF oval的大小确定:

在确定RectF oval之前,我们要先测量确定当前控件的宽高,根据当前控件的宽高来确定oval的合适大小。

测量当前控件的大小一般我们在onmeasure()方法中处理,resolveMeasured()方法传递两个参数,第一各参数为widthMeasureSpec或者heightMeasureSpec,第二个参数为期望值也就是默认值。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(resolveMeasured(widthMeasureSpec, nDesired), resolveMeasured(heightMeasureSpec, nDesired));
    }

/**
     *
     * @param measureSpec
     * @param desired
     * @return
     */
    private int resolveMeasured(int measureSpec, int desired)
    {
        int result = 0;
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (MeasureSpec.getMode(measureSpec)) {
            case MeasureSpec.UNSPECIFIED: //
                result = desired;
                break;
            case MeasureSpec.AT_MOST:  //wrap-content
                result = Math.min(specSize, desired);
                break;
            case MeasureSpec.EXACTLY:  //match-content
            default:
                result = specSize;
        }
        return result;
    } 

在测量之后我们就可以来求oval的具体大小 ,我把这步操作放在了onSizeChanged方法中,求出最小一边的一半减去圆环的宽度,得到圆弧的半径,然后根据半径以控件中心为中心绘制我们所需要的矩形区域。

radiu = (int) ((Math.min(getWidth(), getHeight()))/2-mPaint.getStrokeWidth());

            oval.left = getWidth()/2-radiu;
            oval.top = getHeight()/2-radiu;
            oval.right = getWidth()/2+radiu;
            oval.bottom = getHeight()/2+radiu;

这样下来,基本上就可以绘制比较理想的弧线了,在这里也把绘制中心文字也说下吧,主要通过getTextBounds()方法获取文字区域的宽高,然后在drawText()方法中将坐标进行适当偏移以使文字居中显示。

String strProgressText = "";
        if(mOnProgressListener !=null){//如果不为空  则为接口返回的值
            strProgressText = mOnProgressListener.OnProgress(mMax, mProgress);
        }else{
            strProgressText = mProgress+"/"+mMax;
        }
        mTextPaint.getTextBounds(strProgressText, 0, strProgressText.length(), bounds);
        canvas.drawText(strProgressText, oval.centerX()-bounds.width()/2, oval.centerY()+bounds.height()/2, mTextPaint);

最后还有一个小点就是渐变色的绘制,用的SweepGradient,我们可以看下Shader的子类,shader类是很强大的,类似与圆形图片、渐变效果都可以用它来实现,这里就不过多展开了:

SweepGradient sweepGradient = new SweepGradient(getWidth()/2, getHeight()/2, colors, null);
  p.setShader(sweepGradient);

到这里为止,我们的圆环已经绘制好了,包括中间的文字以及圆环的渐变效果都已经实现了,就是这样的:

2、加入刻度效果

接下来要加入刻度效果,实现思路是这样的,我先默认实现两个圆弧(注意这两个圆弧只是我们假定添加的,并不是真正加在控件中显示),然后获取相同角度,根据相对位置获取两个圆环上的点进行连线,将这两个点连起的刻度线封装成对象添加在集合中,最后在onDraw方法中遍历集合,进行绘制。
oval2 = new RectF();//内环
            oval2.left = getWidth()/2-radiu/4f*3;
            oval2.top = getHeight()/2-radiu/4f*3;
            oval2.right = getWidth()/2+radiu/4f*3;
            oval2.bottom = getHeight()/2+radiu/4f*3;

            oval3 = new RectF();//外环
            oval3.left = getWidth()/2-radiu/8f*7;
            oval3.top = getHeight()/2-radiu/8f*7;
            oval3.right = getWidth()/2+radiu/8f*7;
            oval3.bottom = getHeight()/2+radiu/8f*7;

//然后初始化数据
    /**
     * 初始化数据
     */
    private void initData() {
        mLinesList.clear();

        Path path = new Path();
        Path path1 = new Path();
        //从startAngle开始 绘制180角度
        path.addArc(oval2, mStartAngle, mGraduationSweepAngle);
        path1.addArc(oval3, mStartAngle, mGraduationSweepAngle);

        PathMeasure pm = new PathMeasure(path, false);
        float itemLength = pm.getLength()/(nGraduationCount-1);

        PathMeasure pm1 = new PathMeasure(path1, false);

        float[] pos = new float[2];
        float[] postemp = new float[2];
        for (int i = 0; i < nGraduationCount; i++) {
            pm.getPosTan(itemLength*i, pos , null );
     pm1.getPosTan(itemLength*i/pm.getLength()*pm1.getLength(), postemp , null);
            Line line = new Line();
            line.p1.x = pos[0];
            line.p1.y = pos[1];
            line.p2.x = postemp[0];
            line.p2.y = postemp[1];
            mLinesList.add(line);
        }
    }

//ondraw方法:
for (int i = 0; i < mLinesList.size(); i++) {
                Line line = mLinesList.get(i);
                canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollPaint);
            }

这里用到了PathMeasure这个辅助工具,这里简单讲一下:

public PathMeasure()

//path:需要测量的path  forceClosed:是否关闭path
public PathMeasure(Path path, boolean forceClosed)

//指定需要测量的path
public void setPath(Path path, boolean forceClosed)

//返回当前path的总长度。
getLength()

//返回值是boolean,如过path为空,则返回false 传入参数有三个:
//distance:传入距离起点的距离。
//pos[]:意思是position,分别对应点的x,y坐标
//tan[]:获取切线值,不常用。
public boolean getPosTan(float distance, float pos[], float tan[])

//返回一个处理好的matrix,但是这个matrix是以左上角作为旋转点,所以需要将这个点移动到中心点。 其中一个参数flags,指这个martrix需要什么信息。flags的值有如下两个
PathMeasure.POSITION_MATRIX_FLAG:位置信息
pathMeasure.TANGENT_MATRIX_FLAG:切边信息,方位角信息
public boolean getMatrix(float distance, Matrix matrix, int flags)

//这个方法返回boolean,如果截取的长度为0则返回false,否则为true。参数如下
//startD:起始距离
//stopD:终点距离
//dst:接收截取的path
//startWithMoveTo:是否把截取的path,moveto到起始点。
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

3、加入刻度随进度增加而增加效果,并增加进度变化回调方便操作

加入刻度随进度增加而增加,我们可以这样想,首先我总的刻度数是一定的,判断划过的角度占圆周的百分比,随之就可以得到划过刻度数占总刻度数的百分比,进而就求出划过的刻度数了。

for (int i = 0; i < Math.round(mSweepAngle*nGraduationCount/360f); i++) {
            if(i<mLinesList.size()){
                Line line = mLinesList.get(i);
                canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollDrawPaint);
            }
        }

将划过的刻度数用画笔再绘制一次,随进度增加的刻度效果就出现啦!

/**
     * 设置进度监听
     * @param mOnProgressListener
     */
    public void setOnProgressListener(OnProgressListener mOnProgressListener) {
        this.mOnProgressListener = mOnProgressListener;
    }
    /**
     * 用于外部判断当前进度状态
     */
    interface OnProgressListener{
        /**
         * 返回中间部分文字内容
         * @param max
         * @param progress
         * @return
         */
        String OnProgress(int max,int progress);
    }

设置回调监听,这样在每次进度变化的时候,可以随意变化中间部分文字显示的内容。

4、增加自定义属性增加可定制性

attrs.xml:

       <declare-styleable name="LoadingStyle">
        <attr name="textSize" format="dimension|reference"/><!-- 字体大小 -->
        <attr name="textColor" format="color|reference"/><!-- 字体颜色 -->
        <attr name="strokeWidth" format="dimension|reference"/><!-- 圆环大小 -->
        <attr name="isShowGraduationBackground" format="boolean"/><!-- 是否显示背景刻度 -->
        <attr name="isShowOutRoll" format="boolean"/><!-- 是否显示外部进度框 -->
        <attr name="startAngle" format="integer|reference"/><!-- 开始的角度 -->
        <attr name="max" format="integer|reference"/><!-- 最大值 -->
        <attr name="progress" format="integer|reference"/><!-- 默认进度值 -->
        <attr name="graduationBackgroundColor" format="color|reference"/><!-- 刻度的背景颜色 -->
        <attr name="graduationWidth" format="dimension|reference"/><!-- 刻度的宽度 -->
        <attr name="graduationCount" format="integer|reference"/><!-- 刻度的个数 -->
    </declare-styleable>

layout文件:

 xmlns:app="http://schemas.android.com/apk/res-auto"<!--设置命名空间 -->

  <com.mrzk.circleloadinglibrary.CircleLoadingView
        android:id="@+id/lv_loading"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_centerInParent="true"
        app:textSize="35sp"
        app:textColor="#f60"
        app:strokeWidth="10dp"
        app:isShowGraduationBackground="true"
        app:startAngle="0"
        app:max="300"
        app:progress="100"
        app:graduationBackgroundColor="#ccc"
        app:graduationWidth="5dp"
        app:graduationCount="10"
        app:isShowOutRoll="false"
        />

获取自定义属性值:

TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.LoadingStyle);
        mTextSize = (int) typedArray.getDimension(R.styleable.LoadingStyle_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
        mStrokeWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_strokeWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
        mGraduationWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_graduationWidth, mStrokeWidth/2);
        mTextColor = (int) typedArray.getColor(R.styleable.LoadingStyle_textColor, Color.BLACK);
        mGraduationBackgroundColor = (int) typedArray.getColor(R.styleable.LoadingStyle_graduationBackgroundColor, Color.BLACK);
        mStartAngle = (int) typedArray.getInt(R.styleable.LoadingStyle_startAngle, 180);
        mMax = (int) typedArray.getInt(R.styleable.LoadingStyle_max, 0);
        mProgress = (int) typedArray.getInt(R.styleable.LoadingStyle_progress, 0);
        nGraduationCount = (int) typedArray.getInt(R.styleable.LoadingStyle_graduationCount, 35);
        isShowGraduationBackground =  typedArray.getBoolean(R.styleable.LoadingStyle_isShowGraduationBackground, true);
        isShowOutRoll =  typedArray.getBoolean(R.styleable.LoadingStyle_isShowOutRoll, true);
        typedArray.recycle();

5、使用方法

int[] colors = {0xFFE5BD7D, 0xFFFAAA64,
                    0xFFFFFFFF, 0xFF6AE2FD,
                    0xFF8CD0E5, 0xFFA3CBCB,
                    0xFFBDC7B3, 0xFFD1C299, 0xFFE5BD7D};
        lv_loading.setTextColor(Color.BLACK);//设置中心文字颜色
        lv_loading.setMax(500);//设置最大进度
        lv_loading.setShowGraduationBackgroundEnable(true);//是否显示刻度背景
        lv_loading.setGraduationBackgroundColor(Color.GRAY);//刻度的背景颜色
        lv_loading.setStartAngle(180);//设置开始旋转角度
        lv_loading.setGraduationCount(10);//设置刻度数
        lv_loading.setGraduationWidth(5);//设置刻度的宽度
        lv_loading.setOutColors(colors);//设置外部圆环颜色
        lv_loading.setInnerGraduationColors(colors);//设置内部刻度进度颜色
        lv_loading.setTextSize(35);//设置内部文字字体大小
        lv_loading.setShowOutRollEnable(false);//设置是否显示外部进度框
        lv_loading.setOnProgressListener(new OnProgressListener() {
            @Override
            public String OnProgress(int max, int progress) {

                return progress*100f/max+"%";
            }
        });

二、源码附上

/**
 * 进度视图
 * @author zhang
 *
 */
public class CircleLoadingView extends View{
    /** 圆环的画笔 */
    private Paint mPaint;
    /** 文字的画笔 */
    private Paint mTextPaint;
    /** 刻度的画笔 */
    private Paint mRollPaint;
    //进度刻度的画笔
    private Paint mRollDrawPaint;
    /** 圆环的宽度 */
    private int mStrokeWidth = 0;
    /** 字体的大小 */
    private int mTextSize = 0;
    /** 字体的颜色 */
    private int mTextColor = 0;
    /** 圆环所在区域 */
    private RectF oval;
    private Rect bounds;//获取文字的宽高  使文字居中
    private float mStartAngle = 180;//开始的角度
    private float mSweepAngle = 0;//划过的角度
    /** 刻度的背景色 */
    private int mGraduationBackgroundColor = Color.BLACK;
    /** 刻度的宽度 */
    private int mGraduationWidth = 0;
    private float mGraduationSweepAngle = 359.9f;//刻度划过的角度 如果为360度  获取刻度会默认从右边划过
    private int mMax = 0;//设置的最大值
    private int mProgress = 0;//设置的进度
    //分段颜色 外环
    private int[] OUT_SECTION_COLORS = {
            0xFFE5BD7D, 0xFFFAAA64,
            0xFFFFFFFF, 0xFF6AE2FD,
            0xFF8CD0E5, 0xFFA3CBCB,
            0xFFBDC7B3, 0xFFD1C299,
            0xFFE5BD7D};
    //内部刻度
    private int[] INNER_SECTION_COLORS = {
            0xFFE5BD7D, 0xFFFAAA64,
            0xFFFFFFFF, 0xFF6AE2FD,
            0xFF8CD0E5, 0xFFA3CBCB,
            0xFFBDC7B3, 0xFFD1C299,
            0xFFE5BD7D};
    /** 宽高的默认值 */
    private int nDesired = 0;
    private RectF oval2;//临时的内圆
    private RectF oval3;//临时的外圆
    /** 刻度的个数 */
    private int nGraduationCount = 35;
    /** 所有线的集合 */
    private List<Line> mLinesList = new ArrayList<LoadingView.Line>();
    /** 进度监听器 */
    private OnProgressListener mOnProgressListener;
    /**
     * 是否显示进度条的背景 默认为
     * @see #setShowGraduationBackgroundEnable(boolean)
     * */
    private boolean isShowGraduationBackground = true;
    /**
     *  是否显示外部进度框
     *  @see #setShowOutRollEnable(boolean)
     *  */
    private boolean isShowOutRoll = true;
    public CircleLoadingView(Context context) {
        this(context,null);
    }
    public CircleLoadingView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CircleLoadingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.LoadingStyle);
        mTextSize = (int) typedArray.getDimension(R.styleable.LoadingStyle_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
        mStrokeWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_strokeWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
        mGraduationWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_graduationWidth, mStrokeWidth/2);
        mTextColor = (int) typedArray.getColor(R.styleable.LoadingStyle_textColor, Color.BLACK);
        mGraduationBackgroundColor = (int) typedArray.getColor(R.styleable.LoadingStyle_graduationBackgroundColor, Color.BLACK);
        mStartAngle = (int) typedArray.getInt(R.styleable.LoadingStyle_startAngle, 180);
        mMax = (int) typedArray.getInt(R.styleable.LoadingStyle_max, 0);
        mProgress = (int) typedArray.getInt(R.styleable.LoadingStyle_progress, 0);
        nGraduationCount = (int) typedArray.getInt(R.styleable.LoadingStyle_graduationCount, 35);
        isShowGraduationBackground =  typedArray.getBoolean(R.styleable.LoadingStyle_isShowGraduationBackground, true);
        isShowOutRoll =  typedArray.getBoolean(R.styleable.LoadingStyle_isShowOutRoll, true);
        typedArray.recycle();
        init();
    }
    /**
     * 设置进度监听
     * @param mOnProgressListener
     */
    public void setOnProgressListener(OnProgressListener mOnProgressListener) {
        this.mOnProgressListener = mOnProgressListener;
    }
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//消除锯齿
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(0xFF0099CC);
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setFakeBoldText(true);//设置字体加粗
        mTextPaint.setTextSize(mTextSize);

        mRollPaint = new Paint(mPaint);
        mRollPaint.setColor(mGraduationBackgroundColor);
//        mRollPaint.setStrokeWidth(mStrokeWidth/2);
        mRollPaint.setStrokeWidth(mGraduationWidth);

        mRollDrawPaint = new Paint(mPaint);
        mRollDrawPaint.setStrokeWidth(mGraduationWidth);

        oval = new RectF();
        bounds = new Rect();

        nDesired = dip2px(60);
    }
    /**
     * 初始化数据
     */
    private void initData() {
        mLinesList.clear();

        Path path = new Path();
        Path path1 = new Path();
        //从startAngle开始 绘制180角度
        path.addArc(oval2, mStartAngle, mGraduationSweepAngle);
        path1.addArc(oval3, mStartAngle, mGraduationSweepAngle);

        PathMeasure pm = new PathMeasure(path, false);
        float itemLength = pm.getLength()/(nGraduationCount-1);

        PathMeasure pm1 = new PathMeasure(path1, false);

        float[] pos = new float[2];
        float[] postemp = new float[2];
        for (int i = 0; i < nGraduationCount; i++) {
            pm.getPosTan(itemLength*i, pos , null );
            pm1.getPosTan(itemLength*i/pm.getLength()*pm1.getLength(), postemp , null);
            Line line = new Line();
            line.p1.x = pos[0];
            line.p1.y = pos[1];
            line.p2.x = postemp[0];
            line.p2.y = postemp[1];
            mLinesList.add(line);
        }
    }

    public int dip2px(int dip){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(resolveMeasured(widthMeasureSpec, nDesired), resolveMeasured(heightMeasureSpec, nDesired));
    }
    /**
     * 设置渐变颜色
     */
    private void setSweepShader(int[] colors,Paint p) {
        SweepGradient sweepGradient = new SweepGradient(getWidth()/2, getHeight()/2, colors, null);
        p.setShader(sweepGradient);
    }
    /** 设置层叠颜色 */
    public void setOutColors(int[] colors){
        OUT_SECTION_COLORS = colors;
        setSweepShader(OUT_SECTION_COLORS,mPaint);
    }
    /** 设置层叠颜色 */
    public void setInnerGraduationColors(int[] colors){
        INNER_SECTION_COLORS = colors;
        setSweepShader(INNER_SECTION_COLORS,mRollDrawPaint);
    }
    /**
     *
     * @param measureSpec
     * @param desired
     * @return
     */
    private int resolveMeasured(int measureSpec, int desired)
    {
        int result = 0;
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (MeasureSpec.getMode(measureSpec)) {
            case MeasureSpec.UNSPECIFIED: //
                result = desired;
                break;
            case MeasureSpec.AT_MOST:  //wrap
                result = Math.min(specSize, desired);
                break;
            case MeasureSpec.EXACTLY:  //match
            default:
                result = specSize;
        }
        return result;
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int radiu = 0;
        if(oval.bottom<=0){
             radiu = (int) ((Math.min(getWidth(), getHeight()))/2-mPaint.getStrokeWidth());

            oval.left = getWidth()/2-radiu;
            oval.top = getHeight()/2-radiu;
            oval.right = getWidth()/2+radiu;
            oval.bottom = getHeight()/2+radiu;

            oval2 = new RectF();
            oval2.left = getWidth()/2-radiu/4f*3;
            oval2.top = getHeight()/2-radiu/4f*3;
            oval2.right = getWidth()/2+radiu/4f*3;
            oval2.bottom = getHeight()/2+radiu/4f*3;

            oval3 = new RectF();
            oval3.left = getWidth()/2-radiu/8f*7;
            oval3.top = getHeight()/2-radiu/8f*7;
            oval3.right = getWidth()/2+radiu/8f*7;
            oval3.bottom = getHeight()/2+radiu/8f*7;
        }
         //初始化数据
        initData();
        //设置渐变色
        setSweepShader(OUT_SECTION_COLORS,mPaint);
        setSweepShader(INNER_SECTION_COLORS,mRollDrawPaint);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        if(isShowOutRoll){
            canvas.drawArc(oval, mStartAngle, mSweepAngle, false, mPaint);
        }else{
            canvas.drawArc(oval, mStartAngle, 360, false, mPaint);
        }

        if(isShowGraduationBackground){
            for (int i = 0; i < mLinesList.size(); i++) {
                Line line = mLinesList.get(i);
                canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollPaint);
            }
        }

//      int degree = (int) (Math.round(mGraduationSweepAngle)/ITEMCOUNT);
//      for (int i = 0; i < Math.round(mSweepAngle/degree); i++) {
        for (int i = 0; i < Math.round(mSweepAngle*nGraduationCount/360f); i++) {
            if(i<mLinesList.size()){
                Line line = mLinesList.get(i);
                canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollDrawPaint);
            }
        }
        String strProgressText = "";
        if(mOnProgressListener !=null){//如果不为空  则为接口返回的值
            strProgressText = mOnProgressListener.OnProgress(mMax, mProgress);
        }else{
            strProgressText = mProgress+"/"+mMax;
        }
        mTextPaint.getTextBounds(strProgressText, 0, strProgressText.length(), bounds);
        canvas.drawText(strProgressText, oval.centerX()-bounds.width()/2, oval.centerY()+bounds.height()/2, mTextPaint);
    }

    /**
     * 设置是否显示外部进度条
     * @param isShowOutRoll
     */
    public void setShowOutRollEnable(boolean isShowOutRoll){
        this.isShowOutRoll = isShowOutRoll;
    }
    /**
     * 设置是否显示进度条的背景
     * @param isShowGraduationBackground
     */
    public void setShowGraduationBackgroundEnable(boolean isShowGraduationBackground){
        this.isShowGraduationBackground = isShowGraduationBackground;
    }
    /**
     * 设置显示进度数量
     * @param nGraduationCount
     */
    public void setGraduationCount(int nGraduationCount){
        this.nGraduationCount = nGraduationCount;
    }
    /**
     * 设置进度的背景颜色
     * @param mGraduationBackgroundColor
     */
    public void setGraduationBackgroundColor(int mGraduationBackgroundColor){
        this.mGraduationBackgroundColor = mGraduationBackgroundColor;
         mRollPaint.setColor(mGraduationBackgroundColor);
    }
    /**
     * 设置刻度的宽度
     * @param mGraduationWidth
     */
    public void setGraduationWidth(int mGraduationWidth){
        this.mGraduationWidth = mGraduationWidth;
        mRollPaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mGraduationWidth, getResources().getDisplayMetrics()));
        mRollDrawPaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mGraduationWidth, getResources().getDisplayMetrics()));
    }

    /**
     * 设置最大进度值
     * @param max
     */
    public void setMax(int max){
        this.mMax = max;
    }
    /**
     * 设置进度
     * @param progress
     */
    public void setProgress(int progress){
        this.mProgress = progress;
        if(mMax==0){
            throw new IllegalArgumentException("Max不能为0!");
        }
        mSweepAngle = 360f*mProgress/mMax;
        postInvalidate();
    }
    /**
     * 设置开始的角度  可以控制开始的位置 默认为180  即从左边开始
     * @param mStartAngle
     */
    public void setStartAngle(float mStartAngle){
        this.mStartAngle = mStartAngle;
    }

    /**
     * 设置字体颜色
     * @param mTextColor
     */
    public void setTextColor(int mTextColor){
        this.mTextColor = mTextColor;
        mTextPaint.setColor(mTextColor);
    }
    /**
     * 设置字体大小
     * @param mTextSize
     */
    public void setTextSize(int mTextSize){
        this.mTextSize = mTextSize;
        mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));
    }
    /**
     * 用于外部判断当前进度状态
     */
    interface OnProgressListener{
        /**
         * 返回中间部分文字内容
         * @param max
         * @param progress
         * @return
         */
        String OnProgress(int max,int progress);
    }
    /**
     * 刻度对象
     */
    class Line{
        PointF p1 = new PointF();
        PointF p2 = new PointF();
    }
}

从一个简洁的进度刻度绘制中了解自定义View的思路流程的更多相关文章

  1. Dialog详解(包括进度条、PopupWindow、自定义view、自定义样式的对话框)

    Dialog详解(包括进度条.PopupWindow.自定义view.自定义样式的对话框)   Android中提供了多种对话框,在实际应用中我们可能会需要修改这些已有的对话框.本实例就是从实际出发, ...

  2. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  3. iOS 在UITableViewCell中加入自定义view时view的frame设定注意

    由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...

  4. Android中使用自定义View实现下载进度的显示

    一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...

  5. iOS中 xib自定义View在storyboard中的使用

    1,创建UIView 的SubClass 命名为MyView 2, new一个名为MyView的xib p1 3,配置xib的属性 p2 4,为View 添加背景色,添加一个按钮并定制按钮约束,这里我 ...

  6. Android中实现自定义View组件并使其能跟随鼠标移动

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...

  7. iOS开发小技巧--获取自定义的BarButtonItem中的自定义View的方法(customView)

    如果BarButtonItem是通过[[UIBarButtonItem alloc] initWithCustomView:(nonnull UIView *)]方法设置的.某些情况下需要修改BarB ...

  8. 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)

    今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...

  9. 自定义View实战--实现一个清新美观的加载按钮

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 在 Dribble 上偶然看到了一组交互如下: 当时在心里问自己能不能做,答案肯定是能做的,不过我比较懒,觉得中间那个伸缩变化要编写 ...

随机推荐

  1. 51 nod 1188 最大公约数之和 V2

    1188 最大公约数之和 V2 题目来源: UVA 基准时间限制:2 秒 空间限制:262144 KB 分值: 160 难度:6级算法题   给出一个数N,输出小于等于N的所有数,两两之间的最大公约数 ...

  2. [APIO2011]

    来自FallDream的博客,未经允许,请勿转载,谢谢. ------------------------------------------------------ A.[Apio2011]方格染色 ...

  3. TDMA over WiFi

    0 引言 TDMA可以修正WiFi中DCF机制中连接速率不同终端间信道占用时间片公平性缺陷,从而提升整体WiFi网络的性能.著名的UBNT的网桥就用其独创的TDMA技术为其赢得了市场.以前是不同的公司 ...

  4. CRM客户关系管理系统(一)

    第一章.CRM介绍和开发流程 1.1.CRM简介 客户关系管理(CRM) 客户关系管理(customer relationship management)的定义是:企业为提高核心竞争力,利用相应的信息 ...

  5. Python小代码_13_生成两个参数的最小公倍数和最大公因数

    def demo(m, n): if m > n: m, n = n, m p = m * n while m != 0: r = n % m n = m m = r return (int(p ...

  6. 解释session

    我理解的session就是,多个页面都要使用某一个或一些数据,这时就可以用session,将数据暂时保存起来,这样其他的页面开启session,就能将那些数据拿出来使用.

  7. 打印n阶菱形

    #打印n阶菱形 def print_rhombus(n): #打印正三角 for i in range(1,n+1): x_num = 2*i-1 #每一层的*数量 space_num = n - i ...

  8. machine learning 之 Neural Network 1

    整理自Andrew Ng的machine learning课程week 4. 目录: 为什么要用神经网络 神经网络的模型表示 1 神经网络的模型表示 2 实例1 实例2 多分类问题 1.为什么要用神经 ...

  9. leetcode刷题笔记231 2的幂

    题目描述: 给定一个整数,写一个函数来判断它是否是2的幂. 题目分析: 判断一个整数是不是2的幂,可根据二进制来分析.2的幂如2,4,8,等有一个特点: 二进制数首位为1,其他位为0,如2为10,4为 ...

  10. log4j日志记录级别是如何工作?

    级别p的级别使用q,在记录日志请求时,如果p>=q启用.这条规则是log4j的核心.它假设级别是有序的.对于标准级别它们关系如下:ALL < DEBUG < INFO < WA ...