首页 > 学院 > 开发设计 > 正文

自定义一个带进度值的圆形进度条

2019-11-06 09:48:20
字体:
来源:转载
供稿:网友

项目中有时候我们为了博得用户的眼球,需要自定义一些好看的控件,下面记录一个自定义带进度值的圆形进度条

先上效果

下面记录具体的实现过程

在Android studio下新建一个PRoject,然后新建一个CircleProgressView,继承系统的view,然后重写它的三个构造方法,如下:

    public CircleProgressView(Context context) {        this(context,null);    }    public CircleProgressView(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;    }然后在定义圆形角度条需要的一些参数:

    private Context mContext;    private Paint mBackgroudPaint;//圆的画笔    private Paint mFrontPaint;//进度条的画笔    private Paint mTextPaint;//数字的画笔    private int mWidth;//控件的宽度    private int mHeight;//控件的高度    private  float redis = 200;//默认圆的半径    private float roundWidth = 50;//默认圆形进度的宽度    private float textSize = 50;//默认字体的大小    private int progress = 0;//记录进度值    private int maxProgress = 100;//最大进度值    private int backProgressColor = 0xff778899;//默认背景的颜色    private int roundProgressColor = 0xff00BFFF;//默认圆形进度条的颜色    private int textColor = 0xff008080;//默认字体的颜色    private int mXCenter;//圆心的x坐标    private int mYCenter;//圆心的Y坐标我们先不自定义属性,先在代码中使用默认的值。写一个方法,得到手机的中心点,我们要以这个中心点绘制圆

 /**     * 得到屏幕的中心点     */    private void getPhoneCenter(){        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);        mXCenter = wm.getDefaultDisplay().getWidth()/2;        mYCenter = wm.getDefaultDisplay().getHeight()/2;    }然后在构造方法中调用getPhoneCenter()方法,获取到中心点的坐标。

得到中心点的坐标后,就可以开始绘制了,重写onDraw()方法,在onDraw()方法中,我们分别绘制背景圆,进度条的扇形,和进度值的数字

先绘制背景圆:

        mBackgroudPaint = new Paint();//初始化背景画笔        mBackgroudPaint.setColor(backProgressColor);        mBackgroudPaint.setAlpha(100);//透明度   0~255  从完全透明到不透明        mBackgroudPaint.setAntiAlias(true);//消除锯齿        mBackgroudPaint.setStyle(Paint.Style.FILL);//实心圆        canvas.drawCircle(mXCenter,mYCenter,redis,mBackgroudPaint);//制定中心点的坐标,半径然后在绘制进度值的数字

        mTextPaint = new Paint();//初始化数字的画笔        mTextPaint.setColor(textColor);//        mTextPaint.setAntiAlias(true);//消除锯齿        mTextPaint.setTextSize(textSize);//        mTextPaint.setTextAlign(Paint.Align.CENTER);//设置文字的对齐方式        canvas.drawText(progress+"%",mXCenter,mYCenter, mTextPaint);//progress是加载的进度值

最后就是绘制进度条了

        mFrontPaint = new Paint();        mFrontPaint.setColor(roundProgressColor);        mFrontPaint.setAntiAlias(true);//消除锯齿        mFrontPaint.setStyle(Paint.Style.STROKE);//空心圆        mFrontPaint.setStrokeWidth(roundWidth);        mFrontPaint.setAlpha(200);//透明度         //确定一个矩形的区域        RectF oval = new RectF(mXCenter-redis+roundWidth/2,mYCenter-redis+roundWidth/2,mXCenter+redis-roundWidth/2,mYCenter+redis-roundWidth/2);        progress = (int)((progress*1.0/maxProgress)*360);        /**         * 绘制扇形         * 第一个参数:确定的矩形区域         * 第二个参数:开始绘制的角度,从12点中开始绘制,角度为-90°         * 第三个参数:是否把弧形的2个点连接起来,false:不连接         * 第四个参数:画笔         */        canvas.drawArc(oval,-90,progress,false,mFrontPaint);然后我们在MainActivity对应的xml文件中添加自定义的CircleProgressView控件(此处的代码省略)。我们在MainActivity中启用一个线程来模拟下载,并结合Handle来处理模拟的结果

class MyThread extends Thread{        @Override        public void run() {            for (int i=0;i<100;i++){                Message msg = myHandler.obtainMessage();                msg.what = UPDATE_PROGRESS;                msg.obj = i+1;                myHandler.sendMessage(msg);                try {                    Thread.sleep(100);//休眠100ms                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }Handle接受到每次接受到消息以后,调用CircleProgressView中的setProgress方法,设置进度的变化

private final int UPDATE_PROGRESS = 001;    CircleProgressView circleProgressView = null;     Handler myHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case 001:                    int progress =(int) msg.obj;                    circleProgressView.setProgress(progress);                    break;            }        }    };那我们就要在CircleProgressView中给progress添加set,get方法,在set方法中,判断当前的progress是否大于最大值,如果不大于,在重新绘制view

public void setProgress(int progress) {        this.progress = progress;        if(progress<=maxProgress){//判断progress是否大于maxProgress            invalidate();//调用这个方法,会重新绘制        }    }    public int getProgress() {        return progress;    }先看一下效果:

需要注意的是:这个时候关于圆形进度控件的属性我们都是在代码中写死的,这样肯定不能达到复用的目的,我们需要把其中一些关键的属性抽取出来,像系统控件的属性那样可以在xml中配置

在res/value文件下新建一个attrs.xml的样式文件,定义以下属性:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="RoundProgressBar">        <attr name="roundColor" format="color"/> <!-- 背景色-->        <attr name="roundProgressColor" format="color"/><!-- 进度条的颜色-->        <attr name="roundWidth" format="dimension"></attr><!-- 进度条的宽度-->        <attr name="textColor" format="color" /><!-- 字体的颜色-->        <attr name="textSize" format="dimension" /><!-- 字体的大小-->    </declare-styleable></resources>然后我们在CircleProgressView的构造函数中获取这些属性,并且赋值。

自定义一个方法,initResource(attrs)获取自定义的属性值,并在构造函数中调用

    private void initResource(AttributeSet attrs) {        //获取我们自定义的文件名,RoundProgressBar是attrs中定义的name        TypedArray mTypeArray = mContext.obtainStyledAttributes(attrs,R.styleable.RoundProgressBar,0,0);        //获取背景色,如果没有设置则使用默认        backProgressColor = mTypeArray.getColor(R.styleable.RoundProgressBar_roundColor,backProgressColor);        //获取进度条颜色,如果没有设置则使用默认        roundProgressColor = mTypeArray.getColor(R.styleable.RoundProgressBar_roundProgressColor,roundProgressColor);        //获取背景圆的半径,如果没有设置则使用默认        redis = mTypeArray.getDimension(R.styleable.RoundProgressBar_roundWidth,redis);        //进度条的宽度设置为半径的1/5        roundWidth = redis/5;        //获取文本的颜色,如果没有设置则使用默认        textColor = mTypeArray.getColor(R.styleable.RoundProgressBar_textColor,textColor);        //获取文本的大小,如果没有设置则使用默认        textSize = mTypeArray.getDimension(R.styleable.RoundProgressBar_textSize,textSize);    }然后我们重写onMeasure()方法,测量控件的大小

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        mWidth = getRealSize(widthMeasureSpec);        mHeight = getRealSize(heightMeasureSpec);    }自定义一个getRealSize()的方法,得到控件的大小

    private int getRealSize(int measureSpec){        float res = -1;        int mode = MeasureSpec.getMode(measureSpec);        int size = MeasureSpec.getSize(measureSpec);        if(mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED){            res = redis*2;        }else{            res = size;        }        return (int)res;    }到此代码写的已经差不多了。这儿需要注意:

在eclipse中使用自定义属性,我们要加上

xmlns:app="http://schemas.android.com/apk/res/我们自己定义的view的包名"但是在Android studio中使用的时候这么写会报错,提示找不到,我们直接让它自动搜索,需要这么写

xmlns:app ="http://schemas.android.com/apk/res-auto"

最后就是在xml中对自定义属性的使用了

   <main.zhaocd.com.circleprogressview.CircleProgressView        android:id="@+id/my_view"        android:layout_width="600dp"        android:layout_height="600dp"        app:roundWidth="80dp"        app:roundColor="#7f8d9c"        app:roundProgressColor="#07ce7e"        app:textColor="#e21e17"        app:textSize="40dp"        />最后在总结一下,一般自定义view的步骤:

1、创建attrs文件,自定义属性(如果一开始不能很好的把握需要那些属性,可以把这一步放到最后)

2、在构造方法中获取自定义的属性

3、重写onMeasure()方法(测量控件的大小)

4、重写onDraw()方法(具体的绘制,调用invalidate()方法会强制执行onDraw方法)

所有的源代码下载


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表