Android中使用自定义View实现下载进度的显示
一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态。之前在公司的项目中写过一个,今天抽空来整理一下。
一般下载都会有这么几种状态:未开始、等待、正在下载、下载结束,当然有时候会有下载出错的状态。等待状态是指用户点击开始下载,但是线程池中没有空闲的线程来处理该次下载,所以状态为等待。
效果图:
这里我只是演示了一下下载和暂停的状态,其他状态没有演示,在代码中设置就可以了。
实现代码:
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 @Override 104 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 * 绘制下载中的view 129 * @param canvas 130 */ 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 //计算文字垂直居中的baseline 147 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 * 绘制暂停时的view 155 * @param canvas 156 */ 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 progress 177 */ 178 public void setProgress(int progress) { 179 mProgress = progress; 180 postInvalidate(); 181 } 182 183 /** 184 * 设置下载状态 185 * @param status 186 */ 187 public void setStatus(int status) { 188 this.mStatus = status; 189 postInvalidate(); 190 } 191 192 /** 193 * 获取下载状态 194 * @return 195 */ 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.DownloadPercentView 11 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 @Override 21 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 @Override 27 public void onClick(View v) { 28 if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PEDDING 29 || mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PAUSED) { 30 downloading = true; 31 mDownloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING); 32 //模拟下载 33 new Thread(new Runnable() { 34 @Override 35 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 @Override 61 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 }
我喜欢,驾驭着代码在风驰电掣中创造完美!我喜欢,操纵着代码在随必所欲中体验生活!我喜欢,书写着代码在时代浪潮中完成经典!每一段新的代码在我手中诞生对我来说就象观看刹那花开的感动!