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

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

效果图:

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

实现代码:

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实现下载进度的显示的更多相关文章

  1. Android中实现自定义View组件并使其能跟随鼠标移动

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...

  2. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  3. Android中制作自定义dialog对话框的实例

    http://www.jb51.net/article/83319.htm   这篇文章主要介绍了Android中制作自定义dialog对话框的实例分享,安卓自带的Dialog显然不够用,因而我们要继 ...

  4. Android开发进阶——自定义View的使用及其原理探索

    在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...

  5. iOS开发小技巧--获取自定义的BarButtonItem中的自定义View的方法(customView)

    如果BarButtonItem是通过[[UIBarButtonItem alloc] initWithCustomView:(nonnull UIView *)]方法设置的.某些情况下需要修改BarB ...

  6. iOS 在UITableViewCell中加入自定义view时view的frame设定注意

    由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...

  7. 从一个简洁的进度刻度绘制中了解自定义View的思路流程

    先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: [项目github地址: https://github.com/zhangke3016/CircleLoading] 实现起来并不难, ...

  8. Android中使用AsyncTask实现文件下载以及进度更新提示

    Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...

  9. Android 中使用自定义字体的方法

    1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . <?xml version="1.0 ...

随机推荐

  1. mysql and 和 or 的 优先级和 查询问题

    1. select * from trade where id=1 and cid=1 or pid=2 ; 2. select * from trade where cid=1 or (pid=2 ...

  2. getContextPath、getServletPath、getRequestURI的区别

    假定你的web application 项目名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下 ...

  3. Jade之属性

    属性 所有的html(5)标签在jade中均支持如下写法.jade中省去了html<>和标签的关闭等写法,并将属性写在括号之中.若存在多个属性,可以将其分为多行. jade: a(href ...

  4. [转]正确使用SQLCipher来加密Android数据库 - 朝野布告

    参考文档:http://www.tuicool.com/articles/eYNFbuA Android本身自带有不加密的数据库SQLite,如果要保存密码之类的敏感数据在本地的话方法一是使用字段加密 ...

  5. 用c#开发微信 (9) 微渠道 - 推广渠道管理系统 4 部署测试 (最终效果图)

    我们可以使用微信的“生成带参数二维码接口”和 “用户管理接口”,来实现生成能标识不同推广渠道的二维码,记录分配给不同推广渠道二维码被扫描的信息.这样就可以统计和分析不同推广渠道的推广效果. 本文是微渠 ...

  6. 改进ConcurrentDictionary并行使用的性能

    上一篇文章“ConcurrentDictionary 对决 Dictionary+Locking”中,我们知道了 .NET 4.0 中提供了线程安全的 ConcurrentDictionary< ...

  7. [MSSQL]SQL疑难杂症实战记录-巧妙利用PARTITION分组排名递增特性解决合并连续相同数据行

    问题提出 先造一些测试数据以说明题目: DECLARE @TestData TABLE(ID INT,Col1 VARCHAR(20),Col2 VARCHAR(20)) INSERT INTO @T ...

  8. XML相关知识全接触(一)

    XML文件格式已经出来很久了.他的风头如今在JSON.YAML等新兴文件格式的冲击下已经显的不那么强劲.但是XML仍然是当今世界上使用最广泛的文件格式.围绕着它也有一大堆的概念和知识点.所以我们还是很 ...

  9. 【HTML5】Canvas 内部元素添加事件处理

    前言 canvas 没有提供为其内部元素添加事件监听的方法,因此如果要使 canvas 内的元素能够响应事件,需要自己动手实现.实现方法也很简单,首先获得鼠标在 canvas 上的坐标,计算当前坐标在 ...

  10. linux下ssh远程登录服务器入门操作

    使用用户名密码登录 在命令行中输入命令: ssh username@ip_address -p port 之后系统会提示输入密码,输入后即可登录 如果不添加-p选项,则默认是22端口 还可以使用-l选 ...