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 }

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

相关文章
|
18天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
20天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
30 5
|
27天前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
31 2
|
27天前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
27天前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
32 2
|
28天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
1月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
24 2
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
39 3
|
3月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
119 3
|
4月前
|
消息中间件 前端开发 Android开发
Android面试题自定义View之Window、ViewRootImpl和View的三大流程
Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
118 0