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 ...
随机推荐
- Linux2
linux开源软件 :apache软件 nginx支持更高的并发访问 MySQL PHP samba mongoDB python 应用领域: 一:服务器 二:嵌入式
- Nginx虚拟目录支持PHP配置
感谢作者:http://blog.csdn.net/fangaoxin/article/details/7030139 location ~ ^/test/.+\.php$ { alias /var/ ...
- JS延时提示框
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 31.0px Consolas; color: #2b7ec3 } p.p2 { margin: 0.0px ...
- Apache禁止目录访问的方法
在学习ThinkPHP(3.2.3)的时候,公共文件夹.应用目录文件夹等都自带或者自动生成index.html的安全文件.但是ThinkPHP文件夹(核心包)却没有这样的设置.那么ThinkPHP核心 ...
- Log4net日志GUI配置工具
关于log4net的配置文章在园子里真的很多,但是有关GUI界面配置的文章确定太少,改写了一个以前很早的工具 以前的那个有很多的问题,这个基本的大的问题没有,可能一个小问题还是需要修改下,基本功能肯定 ...
- Windows 8.1 应用再出发 - 磁贴的更新
本篇和大家一起了解一下Windows 8.1 中磁贴的更新,我们来看看如何利用它做出更好的应用磁贴. 首先我们从展现形式上来对比一下Windows 8 与 Windows 8.1 中的磁贴: Wind ...
- 从一个Fragment跳转到另一个Fragment
我们知道Activity之间的跳转可以使用 startActivity(intent).但Fragment之间的跳转却不能使用该方法,那该怎么办呢? 直接上代码: 核心代码 @Override//核心 ...
- 居于集成了adt的Android 开发环境配置
一.先说一下环境 Windows 8.1 64 位 注:win7 Ultimate 64 配置会出现 Android SDK manger 不能启动的问题,是因为android.bat 里调用了fi ...
- Spring <context:annotation-config/> 解说
在基于主机方式配置Spring的配置文件中,你可能会见到<context:annotation-config/>这样一条配置,他的作用是式地向 Spring 容器注册 AutowiredA ...
- haskell中的let和where
haskell中有两种定义局部变量的方法let和where,方法分别如下 roots a b c = ((-b + det) / (a2), (-b - det) / (a2)) *a*c) a2 = ...