一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态。之前在公司的项目中写过一个,今天抽空来整理一下。
一般下载都会有这么几种状态:未开始、等待、正在下载、下载结束,当然有时候会有下载出错的状态。等待状态是指用户点击开始下载,但是线程池中没有空闲的线程来处理该次下载,所以状态为等待。
效果图:
这里我只是演示了一下下载和暂停的状态,其他状态没有演示,在代码中设置就可以了。
实现代码:
1、自定义View
1 public class DownloadPercentView extends View { 2 3 public final static int STATUS_PEDDING = 1; 4 public final static int STATUS_WAITING = 2; 5 public final static int STATUS_DOWNLOADING = 3; 6 public final static int STATUS_PAUSED = 4; 7 public final static int STATUS_FINISHED = 5; 8 9 // 画实心圆的画笔 10 private Paint mCirclePaint; 11 // 画圆环的画笔 12 private Paint mRingPaint; 13 // 绘制进度文字的画笔 14 private Paint mTxtPaint; 15 // 圆形颜色 16 private int mCircleColor; 17 // 圆环颜色 18 private int mRingColor; 19 // 半径 20 private int mRadius; 21 // 圆环宽度 22 private int mStrokeWidth = 2; 23 // 圆心x坐标 24 private int mXCenter; 25 // 圆心y坐标 26 private int mYCenter; 27 // 总进度 28 private int mTotalProgress = 100; 29 // 当前进度 30 private int mProgress; 31 //下载状态 32 private int mStatus = 1; 33 34 //默认显示的图片 35 private Bitmap mNotBeginImg; 36 //暂停时中间显示的图片 37 private Bitmap mPausedImg; 38 //等待时显示的图片 39 private Bitmap mWatiImg; 40 //下载完成时显示的图片 41 private Bitmap finishedImg; 42 43 44 45 public DownloadPercentView(Context context, AttributeSet attrs) { 46 super(context, attrs); 47 // 获取自定义的属性 48 initAttrs(context, attrs); 49 initVariable(); 50 } 51 52 private void initAttrs(Context context, AttributeSet attrs) { 53 TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs, 54 R.styleable.DownloadPercentView, 0, 0); 55 mRadius = (int)typeArray.getDimension(R.styleable.DownloadPercentView_radius, 100); 56 mNotBeginImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_notBeginImg)).getBitmap(); 57 mPausedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_pausedImg)).getBitmap(); 58 mWatiImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_waitImg)).getBitmap(); 59 finishedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_finishedImg)).getBitmap(); 60 61 mNotBeginImg = big(mNotBeginImg, mRadius * 2, mRadius * 2); 62 mPausedImg = big(mPausedImg, mRadius * 2, mRadius * 2); 63 mWatiImg = big(mWatiImg, mRadius * 2, mRadius * 2); 64 finishedImg = big(finishedImg, mRadius * 2, mRadius * 2); 65 66 mStrokeWidth = (int)typeArray.getDimension(R.styleable.DownloadPercentView_strokeWidth, 2); 67 68 // mRadius = Math.max(mNotBeginImg.getWidth()/2, mNotBeginImg.getHeight()/2) + mStrokeWidth; 69 mCircleColor = typeArray.getColor(R.styleable.DownloadPercentView_circleColor, 0xFFFFFFFF); 70 mRingColor = typeArray.getColor(R.styleable.DownloadPercentView_ringColor, 0xFFFFFFFF); 71 } 72 73 private void initVariable() { 74 //初始化绘制灰色圆的画笔 75 mCirclePaint = new Paint(); 76 mCirclePaint.setAntiAlias(true); 77 mCirclePaint.setColor(mCircleColor); 78 mCirclePaint.setStyle(Paint.Style.STROKE); 79 mCirclePaint.setStrokeWidth(mStrokeWidth); 80 81 //初始化绘制圆弧的画笔 82 mRingPaint = new Paint(); 83 mRingPaint.setAntiAlias(true); 84 mRingPaint.setColor(mRingColor); 85 mRingPaint.setStyle(Paint.Style.STROKE); 86 mRingPaint.setStrokeWidth(mStrokeWidth); 87 88 //初始化绘制文字的画笔 89 mTxtPaint = new Paint(); 90 mTxtPaint.setAntiAlias(true); 91 mTxtPaint.setColor(Color.parseColor("#52ce90")); 92 mTxtPaint.setTextAlign(Paint.Align.CENTER); 93 mTxtPaint.setTextSize(24); 94 95 } 96 97 @Override 98 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 99 int width = (int)Math.ceil(mRadius) * 2;100 setMeasuredDimension(width, width);101 }102 103 @Override104 protected void onDraw(Canvas canvas) {105 mXCenter = getWidth() / 2;106 mYCenter = getHeight() / 2;107 switch (mStatus) {108 case STATUS_PEDDING:109 canvas.drawBitmap(mNotBeginImg, 0, 0, null);110 break;111 case STATUS_WAITING:112 canvas.drawBitmap(mWatiImg, 0, 0, null);113 break;114 case STATUS_DOWNLOADING:115 drawDownloadingView(canvas);116 break;117 case STATUS_PAUSED:118 drawPausedView(canvas);119 break;120 case STATUS_FINISHED:121 canvas.drawBitmap(finishedImg, 0, 0, null);122 break;123 }124 125 }126 127 /**128 * 绘制下载中的view129 * @param canvas130 */131 private void drawDownloadingView(Canvas canvas) {132 //绘制灰色圆环133 canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);134 135 //绘制进度扇形圆环136 RectF oval = new RectF();137 //设置椭圆上下左右的坐标138 oval.left = mXCenter - mRadius + mStrokeWidth/2;139 oval.top = mYCenter - mRadius + mStrokeWidth/2;140 oval.right = mXCenter + mRadius - mStrokeWidth/2;141 oval.bottom = mYCenter + mRadius - mStrokeWidth/2;142 canvas.drawArc(oval, -90, ((float)mProgress / mTotalProgress) * 360, false, mRingPaint);143 144 //绘制中间百分比文字145 String percentTxt = String.valueOf(mProgress);146 //计算文字垂直居中的baseline147 Paint.FontMetricsInt fontMetrics = mTxtPaint.getFontMetricsInt();148 float baseline = oval.top + (oval.bottom - oval.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;149 canvas.drawText(percentTxt, mXCenter, baseline, mTxtPaint);150 151 }152 153 /**154 * 绘制暂停时的view155 * @param canvas156 */157 private void drawPausedView(Canvas canvas) {158 //绘制灰色圆环159 canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);160 161 //绘制进度扇形圆环162 RectF oval = new RectF();163 //设置椭圆上下左右的坐标164 oval.left = mXCenter - mRadius + mStrokeWidth/2;165 oval.top = mYCenter - mRadius + mStrokeWidth/2;166 oval.right = mXCenter + mRadius - mStrokeWidth/2;167 oval.bottom = mYCenter + mRadius - mStrokeWidth/2;168 canvas.drawArc(oval, -90, ((float) mProgress / mTotalProgress) * 360, false, mRingPaint);169 170 //绘制中间暂停图标171 canvas.drawBitmap(mPausedImg, 0, 0, null);172 }173 174 /**175 * 更新进度176 * @param progress177 */178 public void setProgress(int progress) {179 mProgress = progress;180 postInvalidate();181 }182 183 /**184 * 设置下载状态185 * @param status186 */187 public void setStatus(int status) {188 this.mStatus = status;189 postInvalidate();190 }191 192 /**193 * 获取下载状态194 * @return195 */196 public int getStatus() {197 return mStatus;198 }199 200 public static Bitmap big(Bitmap b,float x,float y)201 {202 int w=b.getWidth();203 int h=b.getHeight();204 float sx=(float)x/w;205 float sy=(float)y/h;206 Matrix matrix = new Matrix();207 matrix.postScale(sx, sy); // 长和宽放大缩小的比例208 Bitmap resizeBmp = Bitmap.createBitmap(b, 0, 0, w,209 h, matrix, true);210 return resizeBmp;211 }212 213 }
2、自定义属性
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <declare-styleable name="DownloadPercentView"> 5 <attr name="radius" format="dimension"/> 6 <attr name="notBeginImg" format="string"/> 7 <attr name="waitImg" format="string"/> 8 <attr name="pausedImg" format="string"/> 9 <attr name="finishedImg" format="string"/>10 <attr name="strokeWidth" format="dimension"/>11 <attr name="circleColor" format="color"/>12 <attr name="ringColor" format="color"/>13 </declare-styleable>14 15 </resources>
3、使用自定义布局
首先在布局文件中引用:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:custom="http://schemas.android.com/apk/res-auto" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" 6 android:paddingRight="@dimen/activity_horizontal_margin" 7 android:paddingTop="@dimen/activity_vertical_margin" 8 android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> 9 10 <com.bbk.lling.downloadpercentdemo.DownloadPercentView11 android:id="@+id/downloadView"12 android:layout_width="wrap_content"13 android:layout_height="wrap_content"14 android:layout_centerInParent="true"15 custom:notBeginImg="@drawable/ic_no_download"16 custom:waitImg="@drawable/ic_wait"17 custom:pausedImg="@drawable/ic_pause"18 custom:finishedImg="@drawable/ic_finished"19 custom:strokeWidth="2dp"20 custom:circleColor="#bdbdbd"21 custom:radius="18dp"22 custom:ringColor="#52ce90"/>23 24 </RelativeLayout>
然后我这里在Activity使用一个线程来模拟下载过程来演示:
1 package com.bbk.lling.downloadpercentdemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.view.View; 8 9 10 public class MainActivity extends Activity {11 12 public final static int MSG_UPDATE = 1;13 public final static int MSG_FINISHED = 2;14 15 private DownloadPercentView mDownloadPercentView;16 private int mDownloadProgress = 0;17 private Handler mHandler = new InnerHandler();18 private boolean downloading = false;19 20 @Override21 protected void onCreate(Bundle savedInstanceState) {22 super.onCreate(savedInstanceState);23 setContentView(R.layout.activity_main);24 mDownloadPercentView = (DownloadPercentView) findViewById(R.id.downloadView);25 mDownloadPercentView.setOnClickListener(new View.OnClickListener() {26 @Override27 public void onClick(View v) {28 if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PEDDING29 || mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PAUSED) {30 downloading = true;31 mDownloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING);32 //模拟下载33 new Thread(new Runnable() {34 @Override35 public void run() {36 while (downloading) {37 if(mDownloadProgress == 100) {38 mHandler.sendEmptyMessage(MSG_FINISHED);39 return;40 }41 mDownloadProgress += 1;42 mHandler.sendEmptyMessage(MSG_UPDATE);43 try{44 Thread.sleep(100);45 } catch (Exception e) {46 }47 48 }49 }50 }).start();51 } else if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_DOWNLOADING){52 downloading = false;53 mDownloadPercentView.setStatus(DownloadPercentView.STATUS_PAUSED);54 }55 }56 });57 }58 59 class InnerHandler extends Handler {60 @Override61 public void handleMessage(Message msg) {62 switch (msg.what) {63 case MSG_FINISHED:64 mDownloadPercentView.setStatus(DownloadPercentView.STATUS_FINISHED);65 break;66 case MSG_UPDATE:67 mDownloadPercentView.setProgress(mDownloadProgress);68 break;69 }70 super.handleMessage(msg);71 }72 }73 74 75 }