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中使用自定义View实现下载进度的显示的更多相关文章
- Android中实现自定义View组件并使其能跟随鼠标移动
场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...
- Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题
这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...
- Android中制作自定义dialog对话框的实例
http://www.jb51.net/article/83319.htm 这篇文章主要介绍了Android中制作自定义dialog对话框的实例分享,安卓自带的Dialog显然不够用,因而我们要继 ...
- Android开发进阶——自定义View的使用及其原理探索
在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...
- iOS开发小技巧--获取自定义的BarButtonItem中的自定义View的方法(customView)
如果BarButtonItem是通过[[UIBarButtonItem alloc] initWithCustomView:(nonnull UIView *)]方法设置的.某些情况下需要修改BarB ...
- iOS 在UITableViewCell中加入自定义view时view的frame设定注意
由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...
- 从一个简洁的进度刻度绘制中了解自定义View的思路流程
先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: [项目github地址: https://github.com/zhangke3016/CircleLoading] 实现起来并不难, ...
- Android中使用AsyncTask实现文件下载以及进度更新提示
Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...
- Android 中使用自定义字体的方法
1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . <?xml version="1.0 ...
随机推荐
- update select
update中加入select 最常用的update语法是:UPDATE <table_name>SET <column_name1> = <value>, SET ...
- 【Maven】使用Maven构建多模块项目
Maven多模块项目 Maven多模块项目,适用于一些比较大的项目,通过合理的模块拆分,实现代码的复用,便于维护和管理.尤其是一些开源框架,也是采用多模块的方式,提供插件集成,用户可以根据需要配置指定 ...
- JS学习之DOM节点的关系属性封装、克隆节点、Dom中Style常用的一些属性等小结
JS DOM节点: 在JS DOM中节点的关系被定义为节点的属性: 通常有以下几种节点之间的关系: (这里的关系是所有浏览器都支持的) parentNode 父节点 childNodes ...
- Mysql --分区(3)range分区
3.分区类型 RANGE分区 按照range分区的表是利用取值范围将数据分成分区,区间要连续并且不能互相重叠,使用values less than操作符进行分区定义 CREATE TABLE tnp ...
- Java EE 参考文档及sample
http://docs.oracle.com/javaee/6/tutorial/doc/ https://svn.java.net/svn/javaeetutorial~svn/ 检索: site: ...
- Spring MVC 3 深入总结
一.前言: 大家好,Spring3 MVC是非常优秀的MVC框架,由其是在3.0版本发布后,现在有越来越多的团队选择了Spring3 MVC了.Spring3 MVC结构简单,应了那句话简单就是美 ...
- Nhibernate的介绍
1.介绍的内容 1.感谢园友的文章支持 by 李永京 by wolfy 2.Nhibernate的框架介绍 3.Nhibernate的架构介绍 4.Nhibernate映射方法介绍(该点自己也存在一定 ...
- Magicodes.WeiChat——媒体资源选择组件之media-choice(开源)
media-choice为媒体资源选择组件,基于KnockoutJs.支持图片.语音.视频.图文的选择以及预览,支持默认选择类型,支持是否禁用选择类型的更改. 使用示例: <script id= ...
- Heroku空项目 Git本地Push代码错误 以及 Heroku Web启动错误
在Eclipse下建了一个"Blank Heroku App", 然后将自己写好的JS Web练习代码直接复制放到了这个空项目下, 由于Eclipse下的git工具不太会用, 导致 ...
- Xaml/Xml 实现对象与存储分离
刚开始用xml存储东西的时候都是不断的在xml文件里面添加或者修改xml的节点,这个是很常见的做法,这方面的博客也很多我也就不介绍了.但其实在小批量存储的时候我们可以直接将对象存进xml/xaml,使 ...