Android中使用自定义View实现下载进度的显示

  一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态。之前在公司的项目中写过一个,今天抽空来整理一下。

  一般下载都会有这么几种状态:未开始、等待、正在下载、下载结束,当然有时候会有下载出错的状态。等待状态是指用户点击开始下载,但是线程池中没有空闲的线程来处理该次下载,所以状态为等待。

效果图:

Android中使用自定义View实现下载进度的显示

  这里我只是演示了一下下载和暂停的状态,其他状态没有演示,在代码中设置就可以了。

实现代码:

1、自定义View

 public class DownloadPercentView extends View {

     public final static int STATUS_PEDDING = 1;
public final static int STATUS_WAITING = 2;
public final static int STATUS_DOWNLOADING = 3;
public final static int STATUS_PAUSED = 4;
public final static int STATUS_FINISHED = 5; // 画实心圆的画笔
private Paint mCirclePaint;
// 画圆环的画笔
private Paint mRingPaint;
// 绘制进度文字的画笔
private Paint mTxtPaint;
// 圆形颜色
private int mCircleColor;
// 圆环颜色
private int mRingColor;
// 半径
private int mRadius;
// 圆环宽度
private int mStrokeWidth = 2;
// 圆心x坐标
private int mXCenter;
// 圆心y坐标
private int mYCenter;
// 总进度
private int mTotalProgress = 100;
// 当前进度
private int mProgress;
//下载状态
private int mStatus = 1; //默认显示的图片
private Bitmap mNotBeginImg;
//暂停时中间显示的图片
private Bitmap mPausedImg;
//等待时显示的图片
private Bitmap mWatiImg;
//下载完成时显示的图片
private Bitmap finishedImg; public DownloadPercentView(Context context, AttributeSet attrs) {
super(context, attrs);
// 获取自定义的属性
initAttrs(context, attrs);
initVariable();
} private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.DownloadPercentView, 0, 0);
mRadius = (int)typeArray.getDimension(R.styleable.DownloadPercentView_radius, 100);
mNotBeginImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_notBeginImg)).getBitmap();
mPausedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_pausedImg)).getBitmap();
mWatiImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_waitImg)).getBitmap();
finishedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_finishedImg)).getBitmap(); mNotBeginImg = big(mNotBeginImg, mRadius * 2, mRadius * 2);
mPausedImg = big(mPausedImg, mRadius * 2, mRadius * 2);
mWatiImg = big(mWatiImg, mRadius * 2, mRadius * 2);
finishedImg = big(finishedImg, mRadius * 2, mRadius * 2); mStrokeWidth = (int)typeArray.getDimension(R.styleable.DownloadPercentView_strokeWidth, 2); // mRadius = Math.max(mNotBeginImg.getWidth()/2, mNotBeginImg.getHeight()/2) + mStrokeWidth;
mCircleColor = typeArray.getColor(R.styleable.DownloadPercentView_circleColor, 0xFFFFFFFF);
mRingColor = typeArray.getColor(R.styleable.DownloadPercentView_ringColor, 0xFFFFFFFF);
} private void initVariable() {
//初始化绘制灰色圆的画笔
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(mCircleColor);
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mStrokeWidth); //初始化绘制圆弧的画笔
mRingPaint = new Paint();
mRingPaint.setAntiAlias(true);
mRingPaint.setColor(mRingColor);
mRingPaint.setStyle(Paint.Style.STROKE);
mRingPaint.setStrokeWidth(mStrokeWidth); //初始化绘制文字的画笔
mTxtPaint = new Paint();
mTxtPaint.setAntiAlias(true);
mTxtPaint.setColor(Color.parseColor("#52ce90"));
mTxtPaint.setTextAlign(Paint.Align.CENTER);
mTxtPaint.setTextSize(24); } @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = (int)Math.ceil(mRadius) * 2;
setMeasuredDimension(width, width);
} @Override
protected void onDraw(Canvas canvas) {
mXCenter = getWidth() / 2;
mYCenter = getHeight() / 2;
switch (mStatus) {
case STATUS_PEDDING:
canvas.drawBitmap(mNotBeginImg, 0, 0, null);
break;
case STATUS_WAITING:
canvas.drawBitmap(mWatiImg, 0, 0, null);
break;
case STATUS_DOWNLOADING:
drawDownloadingView(canvas);
break;
case STATUS_PAUSED:
drawPausedView(canvas);
break;
case STATUS_FINISHED:
canvas.drawBitmap(finishedImg, 0, 0, null);
break;
} } /**
* 绘制下载中的view
* @param canvas
*/
private void drawDownloadingView(Canvas canvas) {
//绘制灰色圆环
canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint); //绘制进度扇形圆环
RectF oval = new RectF();
//设置椭圆上下左右的坐标
oval.left = mXCenter - mRadius + mStrokeWidth/2;
oval.top = mYCenter - mRadius + mStrokeWidth/2;
oval.right = mXCenter + mRadius - mStrokeWidth/2;
oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
canvas.drawArc(oval, -90, ((float)mProgress / mTotalProgress) * 360, false, mRingPaint); //绘制中间百分比文字
String percentTxt = String.valueOf(mProgress);
//计算文字垂直居中的baseline
Paint.FontMetricsInt fontMetrics = mTxtPaint.getFontMetricsInt();
float baseline = oval.top + (oval.bottom - oval.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
canvas.drawText(percentTxt, mXCenter, baseline, mTxtPaint); } /**
* 绘制暂停时的view
* @param canvas
*/
private void drawPausedView(Canvas canvas) {
//绘制灰色圆环
canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint); //绘制进度扇形圆环
RectF oval = new RectF();
//设置椭圆上下左右的坐标
oval.left = mXCenter - mRadius + mStrokeWidth/2;
oval.top = mYCenter - mRadius + mStrokeWidth/2;
oval.right = mXCenter + mRadius - mStrokeWidth/2;
oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
canvas.drawArc(oval, -90, ((float) mProgress / mTotalProgress) * 360, false, mRingPaint); //绘制中间暂停图标
canvas.drawBitmap(mPausedImg, 0, 0, null);
} /**
* 更新进度
* @param progress
*/
public void setProgress(int progress) {
mProgress = progress;
postInvalidate();
} /**
* 设置下载状态
* @param status
*/
public void setStatus(int status) {
this.mStatus = status;
postInvalidate();
} /**
* 获取下载状态
* @return
*/
public int getStatus() {
return mStatus;
} public static Bitmap big(Bitmap b,float x,float y)
{
int w=b.getWidth();
int h=b.getHeight();
float sx=(float)x/w;
float sy=(float)y/h;
Matrix matrix = new Matrix();
matrix.postScale(sx, sy); // 长和宽放大缩小的比例
Bitmap resizeBmp = Bitmap.createBitmap(b, 0, 0, w,
h, matrix, true);
return resizeBmp;
} }

2、自定义属性

 <?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="DownloadPercentView">
<attr name="radius" format="dimension"/>
<attr name="notBeginImg" format="string"/>
<attr name="waitImg" format="string"/>
<attr name="pausedImg" format="string"/>
<attr name="finishedImg" format="string"/>
<attr name="strokeWidth" format="dimension"/>
<attr name="circleColor" format="color"/>
<attr name="ringColor" format="color"/>
</declare-styleable> </resources>

3、使用自定义布局

  首先在布局文件中引用:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <com.bbk.lling.downloadpercentdemo.DownloadPercentView
android:id="@+id/downloadView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
custom:notBeginImg="@drawable/ic_no_download"
custom:waitImg="@drawable/ic_wait"
custom:pausedImg="@drawable/ic_pause"
custom:finishedImg="@drawable/ic_finished"
custom:strokeWidth="2dp"
custom:circleColor="#bdbdbd"
custom:radius="18dp"
custom:ringColor="#52ce90"/> </RelativeLayout>

  然后我这里在Activity使用一个线程来模拟下载过程来演示:

 package com.bbk.lling.downloadpercentdemo;

 import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View; public class MainActivity extends Activity { public final static int MSG_UPDATE = 1;
public final static int MSG_FINISHED = 2; private DownloadPercentView mDownloadPercentView;
private int mDownloadProgress = 0;
private Handler mHandler = new InnerHandler();
private boolean downloading = false; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDownloadPercentView = (DownloadPercentView) findViewById(R.id.downloadView);
mDownloadPercentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PEDDING
|| mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PAUSED) {
downloading = true;
mDownloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING);
//模拟下载
new Thread(new Runnable() {
@Override
public void run() {
while (downloading) {
if(mDownloadProgress == 100) {
mHandler.sendEmptyMessage(MSG_FINISHED);
return;
}
mDownloadProgress += 1;
mHandler.sendEmptyMessage(MSG_UPDATE);
try{
Thread.sleep(100);
} catch (Exception e) {
} }
}
}).start();
} else if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_DOWNLOADING){
downloading = false;
mDownloadPercentView.setStatus(DownloadPercentView.STATUS_PAUSED);
}
}
});
} class InnerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_FINISHED:
mDownloadPercentView.setStatus(DownloadPercentView.STATUS_FINISHED);
break;
case MSG_UPDATE:
mDownloadPercentView.setProgress(mDownloadProgress);
break;
}
super.handleMessage(msg);
}
} }

源码下载:https://github.com/liuling07/DownloadPercentDemo

上一篇:Android中的AsyncTask异步任务的简单实例


下一篇:Android中使用Thread线程与AsyncTask异步任务的区别