Android之自定义圆形进度条


       

Android开发中,对于进度条想必大家不会陌生。例如,应用在执行一个耗时操作时,会通过展示一个进度条来显示“加载中...”的动画作为友好页面以提高用户体验。对于这样的进度条,最简单的实现方式就是通过美工给我们切几张不同的图片,最后通过帧动画的方式来实现。通过帧动画实现固然可以,但是对美工的依赖很大。所以今天在这里给大家介绍通过自定义控件来实现一个圆形的进度条。


下载地址: http://download.csdn.net/detail/shenggaofei/9622419

先看效果图:



1. 在res/values目录下创建一个attrs.xml的文件,自定义控件的样式:

[html]  view plain  copy
 print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <attr name="pointWidth" format="dimension|reference" />  
  5.     <attr name="circleColor" format="color|reference" />  
  6.     <attr name="pointColor" format="color|reference" />  
  7.     <attr name="centerImage" format="reference" />  
  8.   
  9.     <declare-styleable name="ProgressView">  
  10.         <attr name="pointWidth"/>  
  11.         <attr name="circleColor"/>  
  12.         <attr name="pointColor"/>  
  13.         <attr name="centerImage"/>  
  14.     </declare-styleable>  
  15.   
  16. </resources>  

2. 创建一个类继承自View,在构造方法中获得自定义属性:

[java]  view plain  copy
 print ?
  1. public ProgressView(Context context, AttributeSet attrs, int defStyle) {  
  2.         super(context, attrs, defStyle);  
  3.   
  4.         /** 
  5.          * 获得属性 
  6.          */  
  7.         TypedArray typedArray = context.getTheme().obtainStyledAttributes(  
  8.                 attrs, R.styleable.ProgressView, defStyle, 0);  
  9.         int count = typedArray.getIndexCount();  
  10.         for (int i = 0; i < count; i++) {  
  11.             int attr = typedArray.getIndex(i);  
  12.             switch (attr) {  
  13.             case R.styleable.ProgressView_pointWidth:  
  14.                 mPointWidth = typedArray.getDimensionPixelSize(attr,  
  15.                         (int) TypedValue.applyDimension(  
  16.                                 TypedValue.COMPLEX_UNIT_SP, 16, getResources()  
  17.                                         .getDisplayMetrics()));  
  18.                 break;  
  19.             case R.styleable.ProgressView_circleColor:  
  20.                 mCircleColor = typedArray.getColor(attr, Color.BLACK);  
  21.                 break;  
  22.             case R.styleable.ProgressView_pointColor:  
  23.                 mPointColor = typedArray.getColor(attr, Color.GREEN);  
  24.                 break;  
  25.             case R.styleable.ProgressView_centerImage:  
  26.                 mCenterImage = BitmapFactory.decodeResource(getResources(),  
  27.                         typedArray.getResourceId(attr, 0));  
  28.                 break;  
  29.             }  
  30.         }  
  31.         typedArray.recycle();  
  32.         initPaint();  
  33.     }  


3. 重写ondraw方法:

[java]  view plain  copy
 print ?
  1. @Override  
  2.     protected void onDraw(Canvas canvas) {  
  3.         super.onDraw(canvas);  
  4.         mWidth = getWidth();  
  5.         mHeight = getHeight();  
  6.         mViewSize = Math.min(mWidth, mHeight) - mPointWidth;  
  7.   
  8.         // 计算中间圆形大小  
  9.         int left = (mWidth - mViewSize) / 2;  
  10.         int top = (mHeight - mViewSize) / 2;  
  11.         int right = left + mViewSize;  
  12.         int bottom = top + mViewSize;  
  13.         mRect = new Rect(left, top, right, bottom);  
  14.         mRectf = new RectF(mRect);  
  15.   
  16.         // 绘制中间图片  
  17.         drawBitmap(canvas);  
  18.           
  19.         // 绘制圆环  
  20.         canvas.drawArc(mRectf, mStartAngle, mDestAngle, false, mCirclePaint);  
  21.   
  22.         //绘制终点位置圆形  
  23.         drawCicle(canvas);  
  24.     }  

在ondraw中提供有三个步骤:
  1. 绘制中间的圆形图片
  2. 绘制圆环
  3. 绘制终点圆形图标

1、绘制中间的圆形图片

[java]  view plain  copy
 print ?
  1. <span style="white-space:pre">    </span>/** 
  2.      * 绘制中间圆形图片 
  3.      * @param canvas 
  4.      */  
  5.     private void drawBitmap(Canvas canvas) {  
  6.         // 绘制中间图片  
  7.         mBitmapShader = new BitmapShader(mCenterImage, Shader.TileMode.CLAMP,  
  8.                 Shader.TileMode.CLAMP);  
  9.         //计算压缩比例,<span style="font-family: Arial, Helvetica, sans-serif;">让图片能够适应控件的大小</span>  
  10.         int bSize = Math.min(mCenterImage.getWidth(), mCenterImage.getHeight());    
  11.         float scale = mViewSize * 1.0f / bSize;  
  12.         Matrix matrix = new Matrix();  
  13.         matrix.setScale(scale, scale);  
  14.         mBitmapShader.setLocalMatrix(matrix);  
  15.           
  16.         //创建圆形shape,将BitmapShader作为属性设置给shape  
  17.         mShapeDrawable = new ShapeDrawable(new OvalShape());  
  18.         mShapeDrawable.getPaint().setShader(mBitmapShader);  
  19.         mShapeDrawable.setBounds(mRect);  
  20.         mShapeDrawable.draw(canvas);  
  21.   
  22.     }  

2、绘制终点位置圆形

[java]  view plain  copy
 print ?
  1. /** 
  2.      * 绘制终点位置圆形 
  3.      * @param canvas 
  4.      */  
  5.     private void drawCicle(Canvas canvas) {  
  6.         // 半径  
  7.         int arcRadius = mViewSize / 2;  
  8.           
  9.         // 偏转角度  
  10.         double angle = (double) (mStartAngle + mDestAngle);  
  11.           
  12.         // 通过三角函数计算终点坐标  
  13.         float cx = (float) (arcRadius * Math.cos(Math.toRadians(angle)))  
  14.                 + mWidth / 2;  
  15.         float cy = (float) (arcRadius * Math.sin(Math.toRadians(angle)))  
  16.                 + mHeight / 2;  
  17.         canvas.drawCircle(cx, cy, mPointWidth / 2, mPointPaint);  
  18.     }  

在绘制终点位置圆形时要通过三角函数计算圆心坐标,具体计算方法在这里不在赘述,给大家看下图:



4. 控制绘制

通过设置一个mProgress的变量来控制进度的绘制:
[java]  view plain  copy
 print ?
  1. /** 
  2.      * 设置进度 
  3.      */  
  4.     public void setProgress(double progress){  
  5.         mProgress = progress;  
  6.         new Thread() {  
  7.             @Override  
  8.             public void run() {  
  9.                 try {  
  10.                     float totleAngle = (float) (360 * mProgress);  
  11.                     for (float angle = 0; angle <= totleAngle; angle += 10) {  
  12.                         mDestAngle = angle;  
  13.                         postInvalidate();  
  14.                         Thread.sleep(100);  
  15.                     }  
  16.                 } catch (InterruptedException e) {  
  17.                     e.printStackTrace();  
  18.                 }  
  19.   
  20.             };  
  21.         }.start();  
  22.     }  

这样,一个自定义进度条就完成了。

5.使用该控件

因为该控件中使用了自定义属性,所以在使用该控件时,在布局文件中需要使用到自定义命名空间以引用自定义属性,布局如下:

[html]  view plain  copy
 print ?
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:zk="http://schemas.android.com/apk/res/com.zk.progressdemo"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.    android:orientation="vertical"  
  7.     tools:context="com.zk.progressdemo.MainActivity" >  
  8.   
  9.      
  10.       
  11.     <com.zk.progressdemo.ProgressView  
  12.         android:id="@+id/pv"  
  13.         android:layout_width="500dp"  
  14.         android:layout_height="800dp"  
  15.         android:background="#00FFFF"  
  16.         zk:centerImage="@drawable/center"  
  17.         zk:circleColor="#0000ff"  
  18.         zk:pointColor="#ff0000"  
  19.         zk:pointWidth="20dp" />  
  20.   
  21. </LinearLayout>  

猜你喜欢

转载自blog.csdn.net/shenggaofei/article/details/52442869