项目中有时候我们为了博得用户的眼球,需要自定义一些好看的控件,下面记录一个自定义带进度值的圆形进度条
先上效果
下面记录具体的实现过程
在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是否大于最大值,如果不大于,在重新绘制viewpublic 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方法)
所有的源代码下载
新闻热点
疑难解答