Oztaking

1.下载模块布局开发

【思路】下面的下载的布局是线性布局,与上面的详情页是分开的;与scroll是分开的;

【初始化下载模块】

【新建Hodler类】

【布局源码】/GooglePlay74/res/layout/layout_detail_download.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent" >
 5 
 6     <Button
 7         android:id="@+id/btn_fav"
 8         android:layout_width="wrap_content"
 9         android:layout_height="46dp"
10         android:layout_margin="2dp"
11         android:background="@drawable/detail_btn_normal"
12         android:text="收藏"
13         android:textColor="#fff" />
14 
15     <Button
16         android:id="@+id/btn_share"
17         android:layout_width="wrap_content"
18         android:layout_height="46dp"
19         android:layout_alignParentRight="true"
20         android:layout_margin="2dp"
21         android:background="@drawable/detail_btn_normal"
22         android:text="分享"
23         android:textColor="#fff" />
24 
25     <Button
26         android:id="@+id/btn_download"
27         android:layout_width="wrap_content"
28         android:layout_height="46dp"
29         android:layout_margin="2dp"
30         android:layout_toLeftOf="@id/btn_share"
31         android:layout_toRightOf="@id/btn_fav"
32         android:background="@drawable/progress_btn_normal"
33         android:text="下载"
34         android:textColor="#fff" />
35 
36     <FrameLayout
37         android:id="@+id/fl_progress"
38         android:layout_width="wrap_content"
39         android:layout_height="46dp"
40         android:layout_margin="2dp"
41         android:layout_toLeftOf="@id/btn_share"
42         android:layout_toRightOf="@id/btn_fav" />
43 
44 </RelativeLayout>

 

 【在Holder中加载布局】布局的加载

【初始化下载模块的完善】

【测试】下面的下载模块的布局和上面的scrollView是分开的;上面的移动不会造成下载模块的改变;

2.线程池介绍及使用

2.1 效果及分析

【说明】可以同时下载多个多个应用,同时在详情页会同步显示下载进度;

【线程池】如果同时开的线程多了的话,会造成手机的卡顿,内存的使用过多;

 

2.2  线程池管理器

【说明】线程池是单例的,需要考虑线程安全的问题;

【线程池的源码】/GooglePlay74/src/com/itheima/googleplay74/manager/ThreadManager.java

 1 package com.itheima.googleplay74.manager;
 2 
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.LinkedBlockingQueue;
 5 import java.util.concurrent.ThreadPoolExecutor;
 6 import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
 7 import java.util.concurrent.TimeUnit;
 8 
 9 /**
10  * 线程管理器
11  * 
12  * @author Kevin
13  * @date 2015-11-4
14  */
15 public class ThreadManager {
16 
17     private static ThreadPool mThreadPool;
18 
19     public static ThreadPool getThreadPool() {
20         if (mThreadPool == null) {
21             synchronized (ThreadManager.class) {
22                 if (mThreadPool == null) {
23                     int cpuCount = Runtime.getRuntime().availableProcessors();// 获取cpu数量
24                     System.out.println("cup个数:" + cpuCount);
25 
26                     // int threadCount = cpuCount * 2 + 1;//线程个数
27                     int threadCount = 10;
28                     mThreadPool = new ThreadPool(threadCount, threadCount, 1L);
29                 }
30             }
31         }
32 
33         return mThreadPool;
34     }
35 
36     // 线程池
37     public static class ThreadPool {
38 
39         private int corePoolSize;// 核心线程数
40         private int maximumPoolSize;// 最大线程数
41         private long keepAliveTime;// 休息时间
42 
43         private ThreadPoolExecutor executor;
44 
45         private ThreadPool(int corePoolSize, int maximumPoolSize,
46                 long keepAliveTime) {
47             this.corePoolSize = corePoolSize;
48             this.maximumPoolSize = maximumPoolSize;
49             this.keepAliveTime = keepAliveTime;
50         }
51 
52         // 线程池几个参数的理解:
53         // 比如去火车站买票, 有10个售票窗口, 但只有5个窗口对外开放. 那么对外开放的5个窗口称为核心线程数,
54         // 而最大线程数是10个窗口.
55         // 如果5个窗口都被占用, 那么后来的人就必须在后面排队, 但后来售票厅人越来越多, 已经人满为患, 就类似于线程队列已满.
56         // 这时候火车站站长下令, 把剩下的5个窗口也打开, 也就是目前已经有10个窗口同时运行. 后来又来了一批人,
57         // 10个窗口也处理不过来了, 而且售票厅人已经满了, 这时候站长就下令*入口,不允许其他人再进来, 这就是线程异常处理策略.
58         // 而线程存活时间指的是, 允许售票员休息的最长时间, 以此限制售票员偷懒的行为.
59         public void execute(Runnable r) {
60             if (executor == null) {
61                 executor = new ThreadPoolExecutor(corePoolSize,
62                         maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
63                         new LinkedBlockingQueue<Runnable>(),
64                         Executors.defaultThreadFactory(), new AbortPolicy());
65                 // 参1:核心线程数;参2:最大线程数;参3:线程休眠时间;参4:时间单位;参5:线程队列;参6:生产线程的工厂;参7:线程异常处理策略
66             }
67 
68             // 线程池执行一个Runnable对象, 具体运行时机线程池说了算
69             executor.execute(r);
70         }
71 
72         // 取消任务
73         public void cancel(Runnable r) {
74             if (executor != null) {
75                 // 从线程队列中移除对象
76                 executor.getQueue().remove(r);
77             }
78         }
79 
80     }
81 }

 【线程池的使用】将在子线程中执行的任务放到线程池的run方法中执行;

【测试】使用线程池的执行效果和使用子线程的效果一样,可以验证完全没有问题;

3.观察者设计模式-下载功能比较复杂

 3.1 下载

【说明】下载状态包含以下六种;

【创建下载管理者】全局的管理者一般都写为单例模式,自己写的可以写饿汉模式,面试的时候演示用懒汉模式;

【下载的进度保持一致】使用到了观察者模式;

 

3.2观察者接口的创建

【观察者接口的创建】需要观察下载状态的变化,观察下载进度的变化;

【注册/注销观察者】

 

【DownLoadManager通知状态和进度的变化】DownLoadManager是被观察者;本质就是回调

4.总结:只要是既有注册又有注销必定为观察者模式,即插即用;

5.DownloadInfo对象封装

【下载应该存在的字段信息和获取下载进度的信息】

【文件夹下载路径的拼接】

【创建文件夹】

【判断文件夹路径】

 【增加到本地文件的路径字段】

【方法-拷贝信息】在下载的Info中存在很多相同的字段与AppInfo一致,因此需要信息的拷贝;

【增加权限】

6.下载-暂停-安装

6.1 下载

【说明】可能同时下载的应用会有很多个,需要使用集合维护起来;

【断点续传】如果已经存在一个下载的对象,则无需再次创建下载的对象,如果不存在,再创建;

【通知状态观察者状态发生变化了】

[修改观察者的参数]发生的变化的信息需要传递给观察者,因此要传递参数

【执行下载的任务】将要下载的任务抽取出来;

[下载任务传递参数]抽取的构造方法

【创建下载任务的集合】集合可能会存在很多个;

【下载的任务放入到集合中进行维护】

6.2 暂停

【暂停的思路】本身下载的任务已经加载到了线程池中,取消任务就是将该任务从下载的队列中取消删除即可;

【增加线程池管理者的队列取消方法】

6.3. 安装

7. 下载任务开发

7.1 下载的框架

【说明】文件下载需要考虑【1】从0开始下载;【2】断点续传;

7.2 从0开始下载

【需要考虑的情况】【1】文件不存在【2】已经下载的文件的长度与 要下载的文件的长度不一致;【3】文件下载的位置为0;

7.3 断点续传

【下载的位置】文件当前的位置,主义需要传递参数:range

7.4 网路正常时的数据的下载

【说明】需要考虑数据刷到本地;

8.DownloadManager开发完成

8.1 当前文件的下载中的暂停

【说明】之前的暂停是清楚了排在队列中的任务取消掉;现在需要的是在正在下载的任务暂停掉

两者是有区别的。

【说明】移除的是队列中的任务,并非是正在执行的任务。

【源码修改】

【调用的流程】按下暂停按键,状态currentState设置为暂停,在下载中while循环中的判断状态不是下载,直接会跳过。

 8.2 网络异常的处理

8.3 文件下载结束的处理

【移除当前的下载任务】不管是否成功都会移除;

8.4 并发的处理

【说明】添加关键字Synchronized防止同一文件并发下载;

8.5 使用线程安全的HashMap

【说明】HashMap的操作比较频繁,此处使用线程安全的类;

【源码】考虑了断点续传功能;/GooglePlay74/src/com/itheima/googleplay74/manager/DownloadManager.java

  1 package com.itheima.googleplay74.manager;
  2 
  3 import java.io.File;
  4 import java.io.FileOutputStream;
  5 import java.io.InputStream;
  6 import java.util.ArrayList;
  7 import java.util.concurrent.ConcurrentHashMap;
  8 
  9 import android.content.Intent;
 10 import android.net.Uri;
 11 
 12 import com.itheima.googleplay74.domain.AppInfo;
 13 import com.itheima.googleplay74.domain.DownloadInfo;
 14 import com.itheima.googleplay74.http.HttpHelper;
 15 import com.itheima.googleplay74.http.HttpHelper.HttpResult;
 16 import com.itheima.googleplay74.utils.IOUtils;
 17 import com.itheima.googleplay74.utils.UIUtils;
 18 
 19 /**
 20  * 下载管理器
 21  * 
 22  * - 未下载 - 等待下载 - 正在下载 - 暂停下载 - 下载失败 - 下载成功
 23  * 
 24  * DownloadManager: 被观察者, 有责任通知所有观察者状态和进度发生变化
 25  * 
 26  * @author Kevin
 27  * @date 2015-11-4
 28  */
 29 public class DownloadManager {
 30 
 31     public static final int STATE_UNDO = 1;
 32     public static final int STATE_WAITING = 2;
 33     public static final int STATE_DOWNLOADING = 3;
 34     public static final int STATE_PAUSE = 4;
 35     public static final int STATE_ERROR = 5;
 36     public static final int STATE_SUCCESS = 6;
 37 
 38     private static DownloadManager mDM = new DownloadManager();
 39 
 40     // 4. 观察者集合
 41     private ArrayList<DownloadObserver> mObservers = new ArrayList<DownloadManager.DownloadObserver>();
 42 
 43     // 下载对象的集合, 使用线程安全的HashMap
 44     // private HashMap<String, DownloadInfo> mDownloadInfoMap = new
 45     // HashMap<String, DownloadInfo>();
 46     private ConcurrentHashMap<String, DownloadInfo> mDownloadInfoMap = new ConcurrentHashMap<String, DownloadInfo>();
 47 
 48     // 下载任务的集合
 49     private ConcurrentHashMap<String, DownloadTask> mDownloadTaskMap = new ConcurrentHashMap<String, DownloadManager.DownloadTask>();
 50 
 51     private DownloadManager() {
 52     };
 53 
 54     public static DownloadManager getInstance() {
 55         return mDM;
 56     }
 57 
 58     // 2. 注册观察者
 59     public synchronized void registerObserver(DownloadObserver observer) {
 60         if (observer != null && !mObservers.contains(observer)) {
 61             mObservers.add(observer);
 62         }
 63     }
 64 
 65     // 3. 注销观察者
 66     public synchronized void unregisterObserver(DownloadObserver observer) {
 67         if (observer != null && mObservers.contains(observer)) {
 68             mObservers.remove(observer);
 69         }
 70     }
 71 
 72     // 5.通知下载状态发生变化
 73     public synchronized void notifyDownloadStateChanged(DownloadInfo info) {
 74         for (DownloadObserver observer : mObservers) {
 75             observer.onDownloadStateChanged(info);
 76         }
 77     }
 78 
 79     // 6.通知下载进度发生变化
 80     public synchronized void notifyDownloadProgressChanged(DownloadInfo info) {
 81         for (DownloadObserver observer : mObservers) {
 82             observer.onDownloadProgressChanged(info);
 83         }
 84     }
 85 
 86     // 开始下载
 87     public synchronized void download(AppInfo info) {
 88         // 如果对象是第一次下载, 需要创建一个新的DownloadInfo对象,从头下载
 89         // 如果之前下载过, 要接着下载,实现断点续传
 90         DownloadInfo downloadInfo = mDownloadInfoMap.get(info.id);
 91         if (downloadInfo == null) {
 92             downloadInfo = DownloadInfo.copy(info);// 生成一个下载的对象
 93         }
 94 
 95         downloadInfo.currentState = STATE_WAITING;// 状态切换为等待下载
 96         notifyDownloadStateChanged(downloadInfo);// 通知所有的观察者, 状态发生变化了
 97 
 98         System.out.println(downloadInfo.name + "等待下载啦");
 99 
100         // 将下载对象放入集合中
101         mDownloadInfoMap.put(downloadInfo.id, downloadInfo);
102 
103         // 初始化下载任务, 并放入线程池中运行
104         DownloadTask task = new DownloadTask(downloadInfo);
105         ThreadManager.getThreadPool().execute(task);
106 
107         // 将下载任务放入集合中
108         mDownloadTaskMap.put(downloadInfo.id, task);
109     }
110 
111     // 下载任务对象
112     class DownloadTask implements Runnable {
113 
114         private DownloadInfo downloadInfo;
115 
116         public DownloadTask(DownloadInfo downloadInfo) {
117             this.downloadInfo = downloadInfo;
118         }
119 
120         @Override
121         public void run() {
122             System.out.println(downloadInfo.name + "开始下载啦");
123 
124             // 状态切换为正在下载
125             downloadInfo.currentState = STATE_DOWNLOADING;
126             notifyDownloadStateChanged(downloadInfo);
127 
128             File file = new File(downloadInfo.path);
129 
130             HttpResult httpResult;
131 
132             if (!file.exists() || file.length() != downloadInfo.currentPos
133                     || downloadInfo.currentPos == 0) {
134                 // 从头开始下载
135                 // 删除无效文件
136                 file.delete();// 文件如果不存在也是可以删除的, 只不过没有效果而已
137                 downloadInfo.currentPos = 0;// 当前下载位置置为0
138 
139                 // 从头开始下载
140                 httpResult = HttpHelper.download(HttpHelper.URL
141                         + "download?name=" + downloadInfo.downloadUrl);
142             } else {
143                 // 断点续传
144                 // range 表示请求服务器从文件的哪个位置开始返回数据
145                 httpResult = HttpHelper.download(HttpHelper.URL
146                         + "download?name=" + downloadInfo.downloadUrl
147                         + "&range=" + file.length());
148             }
149 
150             if (httpResult != null && httpResult.getInputStream() != null) {
151 
152                 InputStream in = httpResult.getInputStream();
153                 FileOutputStream out = null;
154                 try {
155                     out = new FileOutputStream(file, true);// 要在原有文件基础上追加数据
156 
157                     int len = 0;
158                     byte[] buffer = new byte[1024];
159 
160                     // 只有状态是正在下载, 才继续轮询. 解决下载过程中中途暂停的问题
161                     while ((len = in.read(buffer)) != -1
162                             && downloadInfo.currentState == STATE_DOWNLOADING) {
163                         out.write(buffer, 0, len);
164                         out.flush();// 把剩余数据刷入本地
165 
166                         // 更新下载进度
167                         downloadInfo.currentPos += len;
168                         notifyDownloadProgressChanged(downloadInfo);
169                     }
170 
171                 } catch (Exception e) {
172                     e.printStackTrace();
173                 } finally {
174                     IOUtils.close(in);
175                     IOUtils.close(out);
176                 }
177 
178                 // 文件下载结束
179                 if (file.length() == downloadInfo.size) {
180                     // 文件完整, 表示下载成功
181                     downloadInfo.currentState = STATE_SUCCESS;
182                     notifyDownloadStateChanged(downloadInfo);
183                 } else if (downloadInfo.currentState == STATE_PAUSE) {
184                     // 中途暂停
185                     notifyDownloadStateChanged(downloadInfo);
186                 } else {
187                     // 下载失败
188                     file.delete();// 删除无效文件
189                     downloadInfo.currentState = STATE_ERROR;
190                     downloadInfo.currentPos = 0;
191                     notifyDownloadStateChanged(downloadInfo);
192                 }
193             } else {
194                 // 网络异常
195                 file.delete();// 删除无效文件
196                 downloadInfo.currentState = STATE_ERROR;
197                 downloadInfo.currentPos = 0;
198                 notifyDownloadStateChanged(downloadInfo);
199             }
200 
201             // 从集合中移除下载任务
202             mDownloadTaskMap.remove(downloadInfo.id);
203         }
204 
205     }
206 
207     // 下载暂停
208     public synchronized void pause(AppInfo info) {
209         // 取出下载对象
210         DownloadInfo downloadInfo = mDownloadInfoMap.get(info.id);
211 
212         if (downloadInfo != null) {
213             // 只有在正在下载和等待下载时才需要暂停
214             if (downloadInfo.currentState == STATE_DOWNLOADING
215                     || downloadInfo.currentState == STATE_WAITING) {
216 
217                 DownloadTask task = mDownloadTaskMap.get(downloadInfo.id);
218 
219                 if (task != null) {
220                     // 移除下载任务, 如果任务还没开始,正在等待, 可以通过此方法移除
221                     // 如果任务已经开始运行, 需要在run方法里面进行中断
222                     ThreadManager.getThreadPool().cancel(task);
223                 }
224                 
225                 // 将下载状态切换为暂停
226                 downloadInfo.currentState = STATE_PAUSE;
227                 notifyDownloadStateChanged(downloadInfo);
228             }
229         }
230     }
231 
232     // 开始安装
233     public synchronized void install(AppInfo info) {
234         DownloadInfo downloadInfo = mDownloadInfoMap.get(info.id);
235         if (downloadInfo != null) {
236             // 跳到系统的安装页面进行安装
237             Intent intent = new Intent(Intent.ACTION_VIEW);
238             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
239             intent.setDataAndType(Uri.parse("file://" + downloadInfo.path),
240                     "application/vnd.android.package-archive");
241             UIUtils.getContext().startActivity(intent);
242         }
243     }
244 
245     /**
246      * 1. 声明观察者的接口
247      */
248     public interface DownloadObserver {
249 
250         // 下载状态发生变化
251         public void onDownloadStateChanged(DownloadInfo info);
252 
253         // 下载进度发生变化
254         public void onDownloadProgressChanged(DownloadInfo info);
255     }
256 
257     // 根据应用信息返回下载对象
258     public DownloadInfo getDownloadInfo(AppInfo info) {
259         return mDownloadInfoMap.get(info.id);
260     }
261 
262 }

 【源码】/GooglePlay74/src/com/itheima/googleplay74/domain/DownloadInfo.java

 1 package com.itheima.googleplay74.domain;
 2 
 3 import java.io.File;
 4 
 5 import com.itheima.googleplay74.manager.DownloadManager;
 6 
 7 import android.os.Environment;
 8 
 9 /**
10  * 下载对象
11  * 
12  * 注意: 一定要有读写sdcard的权限!!!!
13  * 
14  *  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
15     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
16  * 
17  * @author Kevin
18  * @date 2015-11-4
19  */
20 public class DownloadInfo {
21 
22     public String id;
23     public String name;
24     public String downloadUrl;
25     public long size;
26     public String packageName;
27 
28     public long currentPos;// 当前下载位置
29     public int currentState;// 当前下载状态
30     public String path;// 下载到本地文件的路径
31 
32     public static final String GOOGLE_MARKET = "GOOGLE_MARKET";// sdcard根目录文件夹名称
33     public static final String DONWLOAD = "download";// 子文件夹名称, 存放下载的文件
34 
35     // 获取下载进度(0-1)
36     public float getProgress() {
37         if (size == 0) {
38             return 0;
39         }
40 
41         float progress = currentPos / (float) size;
42         return progress;
43     }
44 
45     // 拷贝对象, 从AppInfo中拷贝出一个DownloadInfo
46     public static DownloadInfo copy(AppInfo info) {
47         DownloadInfo downloadInfo = new DownloadInfo();
48 
49         downloadInfo.id = info.id;
50         downloadInfo.name = info.name;
51         downloadInfo.downloadUrl = info.downloadUrl;
52         downloadInfo.packageName = info.packageName;
53         downloadInfo.size = info.size;
54 
55         downloadInfo.currentPos = 0;
56         downloadInfo.currentState = DownloadManager.STATE_UNDO;// 默认状态是未下载
57         downloadInfo.path = downloadInfo.getFilePath();
58 
59         return downloadInfo;
60     }
61 
62     // 获取文件下载路径
63     public String getFilePath() {
64         StringBuffer sb = new StringBuffer();
65         String sdcard = Environment.getExternalStorageDirectory()
66                 .getAbsolutePath();
67         sb.append(sdcard);
68         // sb.append("/");
69         sb.append(File.separator);
70         sb.append(GOOGLE_MARKET);
71         sb.append(File.separator);
72         sb.append(DONWLOAD);
73 
74         if (createDir(sb.toString())) {
75             // 文件夹存在或者已经创建完成
76             return sb.toString() + File.separator + name + ".apk";// 返回文件路径
77         }
78 
79         return null;
80     }
81 
82     private boolean createDir(String dir) {
83         File dirFile = new File(dir);
84 
85         // 文件夹不存在或者不是一个文件夹
86         if (!dirFile.exists() || !dirFile.isDirectory()) {
87             return dirFile.mkdirs();
88         }
89 
90         return true;// 文件夹存在
91     }
92 
93 }

9.应用详情页下载逻辑

9.1 下载更新的管理类的调用

【说明】应用详情页不止是实时显示下载的效果,在首页还会监听下载的进度。

【说明】生成下载的对象,注册接口,实现观察者的接口。

9.2 进度条的布局和初始化

【进度条说明】点击之后变为了进度条;使用一个自定义控件;

【使用自定义控件】/GooglePlay74/src/com/itheima/googleplay74/ui/view/ProgressHorizontal.java,没有自己写,直接移植的;

  1 package com.itheima.googleplay74.ui.view;
  2 
  3 import android.content.Context;
  4 import android.content.res.ColorStateList;
  5 import android.graphics.Canvas;
  6 import android.graphics.Paint;
  7 import android.graphics.Paint.Align;
  8 import android.graphics.Paint.FontMetrics;
  9 import android.graphics.Rect;
 10 import android.graphics.Typeface;
 11 import android.graphics.drawable.Drawable;
 12 import android.graphics.drawable.DrawableContainer;
 13 import android.graphics.drawable.NinePatchDrawable;
 14 import android.os.Process;
 15 import android.view.View;
 16 import android.widget.RemoteViews.RemoteView;
 17 
 18 import com.itheima.googleplay74.utils.StringUtils;
 19 import com.itheima.googleplay74.utils.UIUtils;
 20 
 21 @RemoteView
 22 public class ProgressHorizontal extends View {
 23     private static final int MAX_SMOOTH_ANIM_DURATION = 2000;
 24     private long mThreadId;
 25     private int mResBackground;
 26     private Drawable mDrbBackground;
 27     private int mResProgress;
 28     private Drawable mDrbProgress;
 29     private int mProgressDrbMinWidth;
 30 
 31     private int mProgressTextSize;
 32 
 33     private ColorStateList mProgressTextColorStateList;
 34 
 35     private int mProgressTextColor;
 36 
 37     private Typeface mTypeface = Typeface.DEFAULT;
 38 
 39     private Paint mTextPaint;
 40 
 41     private boolean mProgressTextVisible = true;
 42 
 43     private int mMaxProgress = 100;
 44 
 45     private float mProgress;
 46 
 47     private float mSmoothProgress = 0;
 48 
 49     private float mStartProgress = 0;
 50 
 51     private long mProgressSetTime;
 52 
 53     private int mSmoothAnimDuration;
 54 
 55     private Rect mRawProgressBounds = new Rect();
 56 
 57     private StringBuilder mSb = new StringBuilder(4);
 58 
 59     private String mText;
 60 
 61     private OnProgressChangeListener mOnProgressChangeListener;
 62 
 63     public ProgressHorizontal(Context context) {
 64         super(context);
 65         this.setFocusable(false);
 66         this.setClickable(false);
 67         this.setFocusableInTouchMode(false);
 68         mThreadId = Process.myTid();
 69         mTextPaint = new Paint();
 70     }
 71 
 72     public synchronized float getProgress() {
 73         return mProgress;
 74     }
 75 
 76     public void setProgressBackgroundResource(int resId) {
 77         if (mResBackground == resId) {
 78             return;
 79         }
 80         mResBackground = resId;
 81         try {
 82             mDrbBackground = UIUtils.getDrawable(resId);
 83             if (null != mDrbBackground) {
 84                 mDrbBackground.setBounds(0, 0, getWidth(), getHeight());
 85             }
 86         } catch (Exception e) {
 87             mDrbBackground = null;
 88             mResBackground = -1;
 89         }
 90         invalidate();
 91     }
 92 
 93     public void setProgressDrawble(Drawable drawable) {
 94         if (mDrbProgress == drawable) {
 95             return;
 96         }
 97         mDrbProgress = drawable;
 98         invalidate();
 99     }
100 
101     public void setProgressResource(int resId) {
102         if (mResProgress == resId) {
103             return;
104         }
105         mDrbProgress = UIUtils.getDrawable(resId);
106         invalidate();
107     }
108 
109     public void setMinProgressWidth(int minWidth) {
110         mProgressDrbMinWidth = minWidth;
111         invalidateSafe();
112     }
113 
114     public void setMax(int max) {
115         if (max > 0) {
116             mMaxProgress = max;
117         }
118     }
119 
120     public void setProgress(int progress) {
121         setProgress(progress, false);
122     }
123 
124     public void setProgress(int progress, boolean smooth) {
125         setProgress(progress / (float) mMaxProgress, smooth);
126     }
127 
128     public void setProgress(float progress) {
129         setProgress(progress, false);
130     }
131 
132     public synchronized void setProgress(float progress, boolean smooth) {
133         if (progress < 0) {
134             progress = 0;
135         }
136         if (progress > 1) {
137             progress = 1;
138         }
139         mProgress = progress;
140         mProgressSetTime = System.currentTimeMillis();
141         if (smooth) {
142             mSmoothAnimDuration = (int) (MAX_SMOOTH_ANIM_DURATION * (1 - mProgress));
143         } else {
144             mSmoothAnimDuration = 0;
145             mSmoothProgress = mProgress;
146         }
147         mStartProgress = mSmoothProgress;
148         invalidateSafe();
149     }
150 
151     public void setProgressTextSize(int px) {
152         mProgressTextSize = px;
153     }
154 
155     public void setProgressTextColor(ColorStateList color) {
156         mProgressTextColorStateList = color;
157         mProgressTextColor = mProgressTextColorStateList.getColorForState(getDrawableState(), mProgressTextColor);
158     }
159 
160     public void setProgressTextColor(int color) {
161         mProgressTextColorStateList = null;
162         mProgressTextColor = color;
163     }
164 
165     public void setProgressTextVisible(boolean visible) {
166         mProgressTextVisible = visible;
167     }
168 
169     public void setCenterText(String text) {
170         mText = text;
171         invalidate();
172     }
173 
174     public void setOnProgressChangeListener(OnProgressChangeListener l) {
175         mOnProgressChangeListener = l;
176     }
177 
178     private void invalidateSafe() {
179         if (mThreadId == Process.myTid()) {
180             invalidate();
181         } else {
182             postInvalidate();
183         }
184     }
185 
186     private void notifyProgressChange(float smoothProgress, float targetProgress) {
187         if (null != mOnProgressChangeListener) {
188             mOnProgressChangeListener.onProgressChange(smoothProgress, targetProgress);
189         }
190     }
191 
192     @Override
193     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
194         int width = 0;
195         int height = 0;
196 
197         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
198         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
199         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
200         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
201 
202         if (widthMode == MeasureSpec.EXACTLY) {
203             width = widthSize;
204         } else {
205             width = mDrbBackground == null ? 0 : mDrbBackground.getIntrinsicWidth();
206             if (widthMode == MeasureSpec.AT_MOST) {
207                 width = Math.min(width, widthSize);
208             }
209         }
210 
211         if (heightMode == MeasureSpec.EXACTLY) {
212             height = heightSize;
213         } else {
214             height = mDrbBackground == null ? 0 : mDrbBackground.getIntrinsicHeight();
215             if (heightMode == MeasureSpec.AT_MOST) {
216                 height = Math.min(height, heightSize);
217             }
218         }
219 
220         if (null != mDrbBackground) {
221             mDrbBackground.setBounds(0, 0, width, height);
222         }
223         mRawProgressBounds.set(getPaddingLeft(), getPaddingTop(), width - getPaddingRight(), height
224                 - getPaddingBottom());
225         setMeasuredDimension(width, height);
226     }
227 
228     @Override
229     protected void onDraw(Canvas canvas) {
230         float factor;
231         if (mProgress == 0 || mProgress == 1) {
232             factor = 1;
233         } else {
234             long elapsed = System.currentTimeMillis() - mProgressSetTime;
235             if (elapsed < 0) {
236                 factor = 0;
237             } else if (elapsed > mSmoothAnimDuration) {
238                 factor = 1;
239             } else {
240                 factor = elapsed / (float) mSmoothAnimDuration;
241             }
242         }
243         mSmoothProgress = mStartProgress + factor * (mProgress - mStartProgress);
244 
245         // Draw background
246         if (null != mDrbBackground) {
247             mDrbBackground.draw(canvas);
248         }
249 
250         // Draw progress
251         if (null != mDrbProgress) {
252             if (mDrbProgress instanceof NinePatchDrawable
253                     || (mDrbProgress instanceof DrawableContainer && ((DrawableContainer) mDrbProgress).getCurrent() instanceof NinePatchDrawable)) {
254                 if (mSmoothProgress == 0) {
255                     mDrbProgress.setBounds(0, 0, 0, 0);
256                 } else {
257                     mDrbProgress.setBounds(0, mRawProgressBounds.top,
258                             (int) (mRawProgressBounds.left + (mRawProgressBounds.width() - mProgressDrbMinWidth)
259                                     * mSmoothProgress)
260                                     + mProgressDrbMinWidth, mRawProgressBounds.bottom);
261                 }
262             }
263             mDrbProgress.draw(canvas);
264         }
265 
266         // Draw progress text
267         if (mProgressTextVisible) {
268             mSb.delete(0, mSb.length());
269             if (StringUtils.isEmpty(mText)) {
270                 mSb.append((int) (mSmoothProgress * 100));
271                 mSb.append(\'%\');
272             } else {
273                 mSb.append(mText);
274             }
275             String text = mSb.toString();
276 
277             mTextPaint.setAntiAlias(true);
278             mTextPaint.setColor(mProgressTextColor);
279             mTextPaint.setTextSize(mProgressTextSize);
280             mTextPaint.setTypeface(mTypeface);
281             mTextPaint.setTextAlign(Align.CENTER);
282             FontMetrics fm = mTextPaint.getFontMetrics();
283             int fontH = (int) (Math.abs(fm.descent - fm.ascent));
284             canvas.drawText(text, getWidth() >> 1, ((getHeight() - getPaddingTop() - getPaddingBottom()) >> 1) + (fontH >> 1), mTextPaint);
285             
286         }
287 
288         if (factor != 1) {
289             invalidate();
290         }
291         notifyProgressChange(mSmoothProgress, mProgress);
292     }
293 
294     @Override
295     protected void drawableStateChanged() {
296         int[] drawableState = getDrawableState();
297         if (mDrbBackground != null && mDrbBackground.isStateful()) {
298             mDrbBackground.setState(drawableState);
299         }
300         if (mDrbProgress != null && mDrbProgress.isStateful()) {
301             mDrbProgress.setState(drawableState);
302         }
303         if (mProgressTextColorStateList != null) {
304             mProgressTextColor = mProgressTextColorStateList.getColorForState(drawableState, mProgressTextColor);
305         }
306         invalidate();
307     }
308 
309     public static interface OnProgressChangeListener {
310 
311         public void onProgressChange(float smoothProgress, float targetProgress);
312 
313     }
314 
315 }

 【思路】照着下载按钮的布局直接复制修改为一个帧布局,与下载的按钮重叠在一起,根据不同的状态,然后切换,显示帧布局,还是显示按钮。

【进度条的帧布局】

【初始化进度条并添加到帧布局当中】添加了进度条的背景,文字的颜色,大小等;

 

9.3 进度条的数据的更新

【下载管理者暴露数据接口】数据更新时候需要判断是否已经存在下载的列表中了

【更新UI】根据下载的信息是否存在,判断是否下载过;分别进行处理;

【更新UI】根据不同的状态显示不同的效果;

 1 // 根据当前的下载进度和状态来更新界面
 2     private void refreshUI(int currentState, float progress) {
 3 
 4         //System.out.println("刷新ui了:" + currentState);
 5 
 6         mCurrentState = currentState;
 7         mProgress = progress;
 8 
 9         switch (currentState) {
10         case DownloadManager.STATE_UNDO:// 未下载
11             flProgress.setVisibility(View.GONE);
12             btnDownload.setVisibility(View.VISIBLE);
13             btnDownload.setText("下载");
14             break;
15 
16         case DownloadManager.STATE_WAITING:// 等待下载
17             flProgress.setVisibility(View.GONE);
18             btnDownload.setVisibility(View.VISIBLE);
19             btnDownload.setText("等待中..");
20             break;
21 
22         case DownloadManager.STATE_DOWNLOADING:// 正在下载
23             flProgress.setVisibility(View.VISIBLE);
24             btnDownload.setVisibility(View.GONE);
25             pbProgress.setCenterText("");
26             pbProgress.setProgress(mProgress);// 设置下载进度
27             break;
28 
29         case DownloadManager.STATE_PAUSE:// 下载暂停
30             flProgress.setVisibility(View.VISIBLE);
31             btnDownload.setVisibility(View.GONE);
32             pbProgress.setCenterText("暂停");
33             pbProgress.setProgress(mProgress);
34 
35             System.out.println("暂停界面更新:" + mCurrentState);
36             break;
37 
38         case DownloadManager.STATE_ERROR:// 下载失败
39             flProgress.setVisibility(View.GONE);
40             btnDownload.setVisibility(View.VISIBLE);
41             btnDownload.setText("下载失败");
42             break;
43 
44         case DownloadManager.STATE_SUCCESS:// 下载成功
45             flProgress.setVisibility(View.GONE);
46             btnDownload.setVisibility(View.VISIBLE);
47             btnDownload.setText("安装");
48             break;
49 
50         default:
51             break;
52         }
53 
54     }

 【实时显示下载的状态】因为是更新UI,所以需要运行在主线程中;

【信息的过滤】每次运行下载管理者都会通知观察者,但是更新的信息并非是正在操作的应用,因此需要将非 本应用的信息过滤掉;

9.5.启动下载

【实现点击事件的监听】

【添加控件的点击】进度条也需要点击事件的响应;

【点击之后的执行的逻辑】

【测试】

【bug1】出现bug,在点击暂停之后的再次点击开始下载,虽然进度条在改变,但是文字仍然显示暂停;

【bug1修改】在正在下载的逻辑中添加文字为空的源码;

【高端bug】

【bug2】暂停之后的状态的更新不够及时,有时候会失效;

【bug2原因】界面的更新不够及时,存在延时;

【bug2修改】传递数据的时候作为一个整体进行传递;增加了关键字final可以保证中间的字符串的文字是实时更新的;

【测试】中间暂停一次,可以继续下载,然后可以安装;

 

10.首页下载逻辑处理

【说明】首页的下载的进度的圆圈同步更新数据;需要使用的也是自定义布局;

【自定义控件的源码】/GooglePlay74/src/com/itheima/googleplay74/ui/view/ProgressArc.java

  1 /*
  2  * File Name: MarketProgressArc.java 
  3  * History:
  4  * Created by wangyl on 2013-11-1
  5  */
  6 package com.itheima.googleplay74.ui.view;
  7 
  8 import android.content.Context;
  9 import android.graphics.Canvas;
 10 import android.graphics.Paint;
 11 import android.graphics.RectF;
 12 import android.graphics.drawable.Drawable;
 13 import android.view.View;
 14 
 15 import com.itheima.googleplay74.utils.UIUtils;
 16 
 17 /**
 18  * 圆形进度条
 19  * 
 20  * @author Kevin
 21  */
 22 public class ProgressArc extends View {
 23     // ==========================================================================
 24     // Constants
 25     // ==========================================================================
 26     private final static int START_PROGRESS = -90;
 27     private static final int SET_PROGRESS_END_TIME = 1000;
 28     private static final float RATIO = 360f;
 29     public final static int PROGRESS_STYLE_NO_PROGRESS = -1;
 30     public final static int PROGRESS_STYLE_DOWNLOADING = 0;
 31     public final static int PROGRESS_STYLE_WAITING = 1;
 32 
 33     private int mDrawableForegroudResId;
 34     private Drawable mDrawableForegroud;// 图片
 35     private int mProgressColor;// 进度条的颜色
 36     private RectF mArcRect;// 用于画圆形的区域
 37     private Paint mPaint;// 用户画进度条的画笔
 38     private boolean mUserCenter = false;
 39     private OnProgressChangeListener mProgressChangeListener;// 进度改变的监听
 40     private float mStartProgress;// 动画的起始进度
 41     private float mCurrentProgress;// 当前进度
 42     private float mProgress;// 目标进度
 43     private float mSweep;
 44     private long mStartTime, mEndTime;
 45     private int mStyle = PROGRESS_STYLE_NO_PROGRESS;
 46     private int mArcDiameter;
 47 
 48     public ProgressArc(Context context) {
 49         super(context);
 50         int strokeWidth = UIUtils.dip2px(1);
 51 
 52         mPaint = new Paint();
 53         mPaint.setAntiAlias(true);
 54         mPaint.setStyle(Paint.Style.STROKE);
 55         mPaint.setStrokeWidth(strokeWidth);
 56 
 57         mUserCenter = false;
 58 
 59         mArcRect = new RectF();
 60     }
 61 
 62     public void setProgressChangeListener(OnProgressChangeListener listener) {
 63         mProgressChangeListener = listener;
 64     }
 65 
 66     public void seForegroundResource(int resId) {
 67         if (mDrawableForegroudResId == resId) {
 68             return;
 69         }
 70         mDrawableForegroudResId = resId;
 71         mDrawableForegroud = UIUtils.getDrawable(mDrawableForegroudResId);
 72         invalidateSafe();
 73     }
 74 
 75     /** 设置直径 */
 76     public void setArcDiameter(int diameter) {
 77         mArcDiameter = diameter;
 78     }
 79 
 80     /** 设置进度条的颜色 */
 81     public void setProgressColor(int progressColor) {
 82         mProgressColor = progressColor;
 83         mPaint.setColor(progressColor);
 84     }
 85 
 86     public void setStyle(int style) {
 87         this.mStyle = style;
 88         if (mStyle == PROGRESS_STYLE_WAITING) {
 89             invalidateSafe();
 90         } else {
 91         }
 92     }
 93 
 94     /** 设置进度,第二个参数是否采用平滑进度 */
 95     public void setProgress(float progress, boolean smooth) {
 96         mProgress = progress;
 97         if (mProgress == 0) {
 98             mCurrentProgress = 0;
 99         }
100         mStartProgress = mCurrentProgress;
101         mStartTime = System.currentTimeMillis();
102         if (smooth) {
103             mEndTime = SET_PROGRESS_END_TIME;
104         } else {
105             mEndTime = 0;
106         }
107         invalidateSafe();
108     }
109 
110     private void invalidateSafe() {
111         if (UIUtils.isRunOnUIThread()) {
112             postInvalidate();
113         } else {
114             invalidate();
115         }
116     }
117 
118     /** 测量 */
119     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
120         int width = 0;
121         int height = 0;
122 
123         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
124         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
125         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
126         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
127 
128         if (widthMode == MeasureSpec.EXACTLY) {// 如果是精确的
129             width = widthSize;
130         } else {// 采用图片的大小
131             width = mDrawableForegroud == null ? 0 : mDrawableForegroud
132                     .getIntrinsicWidth();
133             if (widthMode == MeasureSpec.AT_MOST) {
134                 width = Math.min(width, widthSize);
135             }
136         }
137 
138         if (heightMode == MeasureSpec.EXACTLY) {// 如果是精确的
139             height = heightSize;
140         } else {// 采用图片的大小
141             height = mDrawableForegroud == null ? 0 : mDrawableForegroud
142                     .getIntrinsicHeight();
143             if (heightMode == MeasureSpec.AT_MOST) {
144                 height = Math.min(height, heightSize);
145             }
146         }
147         // 计算出进度条的区域
148         mArcRect.left = (width - mArcDiameter) * 0.5f;
149         mArcRect.top = (height - mArcDiameter) * 0.5f;
150         mArcRect.right = (width + mArcDiameter) * 0.5f;
151         mArcRect.bottom = (height + mArcDiameter) * 0.5f;
152 
153         setMeasuredDimension(width, height);
154     }
155 
156     @Override
157     protected void onDraw(Canvas canvas) {
158         if (mDrawableForegroud != null) {// 先把图片画出来
159             mDrawableForegroud.setBounds(0, 0, getMeasuredWidth(),
160                     getMeasuredHeight());
161             mDrawableForegroud.draw(canvas);
162         }
163         // 再画进度
164         drawArc(canvas);
165     }
166 
167     protected void drawArc(Canvas canvas) {
168         if (mStyle == PROGRESS_STYLE_DOWNLOADING
169                 || mStyle == PROGRESS_STYLE_WAITING) {
170             float factor;
171             long currentTime = System.currentTimeMillis();
172             if (mProgress == 100) {
173                 factor = 1;
174             } else {
175                 if (currentTime - mStartTime < 0) {
176                     factor = 0;
177                 } else if (currentTime - mStartTime > mEndTime) {
178                     factor = 1;
179                 } else {
180                     factor = (currentTime - mStartTime) / (float) mEndTime;
181                 }
182             }
183             mPaint.setColor(mProgressColor);
184             mCurrentProgress = mStartProgress + factor
185                     * (mProgress - mStartProgress);
186             mSweep = mCurrentProgress * RATIO;
187             canvas.drawArc(mArcRect, START_PROGRESS, mSweep, mUserCenter,
188                     mPaint);
189             if (factor != 1 && mStyle == PROGRESS_STYLE_DOWNLOADING) {
190                 invalidate();
191             }
192             if (mCurrentProgress > 0) {
193                 notifyProgressChanged(mCurrentProgress);
194             }
195         }
196     }
197 
198     private void notifyProgressChanged(float currentProgress) {
199         if (mProgressChangeListener != null) {
200             mProgressChangeListener.onProgressChange(currentProgress);
201         }
202     }
203 
204     public static interface OnProgressChangeListener {
205 
206         public void onProgressChange(float smoothProgress);
207     }
208 }

【布局的修改】将ImageView替换为帧布局;

【初始化进度条】

【注册监听事件】

 

【bug】有缘网点击了下载,但是在下一个页面的笑霸来了也偶尔会显示正在下载;

【原因】listView的重用机制出现的问题,需要判断条件;

【bug修改】刷新的时候保证是同一个应用;

【源码】/GooglePlay74/src/com/itheima/googleplay74/ui/holder/HomeHolder.java

  1 package com.itheima.googleplay74.ui.holder;
  2 
  3 import android.text.format.Formatter;
  4 import android.view.View;
  5 import android.view.View.OnClickListener;
  6 import android.widget.FrameLayout;
  7 import android.widget.ImageView;
  8 import android.widget.RatingBar;
  9 import android.widget.TextView;
 10 
 11 import com.itheima.googleplay74.R;
 12 import com.itheima.googleplay74.domain.AppInfo;
 13 import com.itheima.googleplay74.domain.DownloadInfo;
 14 import com.itheima.googleplay74.http.HttpHelper;
 15 import com.itheima.googleplay74.manager.DownloadManager;
 16 import com.itheima.googleplay74.manager.DownloadManager.DownloadObserver;
 17 import com.itheima.googleplay74.ui.view.ProgressArc;
 18 import com.itheima.googleplay74.utils.BitmapHelper;
 19 import com.itheima.googleplay74.utils.UIUtils;
 20 import com.lidroid.xutils.BitmapUtils;
 21 
 22 /**
 23  * 首页holder
 24  * 
 25  * @author Kevin
 26  * @date 2015-10-28
 27  */
 28 public class HomeHolder extends BaseHolder<AppInfo> implements
 29         DownloadObserver, OnClickListener {
 30 
 31     private DownloadManager mDM;
 32 
 33     private TextView tvName, tvSize, tvDes;
 34     private ImageView ivIcon;
 35     private RatingBar rbStar;
 36 
 37     private BitmapUtils mBitmapUtils;
 38 
 39     private ProgressArc pbProgress;
 40 
 41     private int mCurrentState;
 42     private float mProgress;
 43 
 44     private TextView tvDownload;
 45 
 46     @Override
 47     public View initView() {
 48         // 1. 加载布局
 49         View view = UIUtils.inflate(R.layout.list_item_home);
 50         // 2. 初始化控件
 51         tvName = (TextView) view.findViewById(R.id.tv_name);
 52         tvSize = (TextView) view.findViewById(R.id.tv_size);
 53         tvDes = (TextView) view.findViewById(R.id.tv_des);
 54         ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
 55         rbStar = (RatingBar) view.findViewById(R.id.rb_star);
 56 
 57         tvDownload = (TextView) view.findViewById(R.id.tv_download);
 58 
 59         // mBitmapUtils = new BitmapUtils(UIUtils.getContext());
 60         mBitmapUtils = BitmapHelper.getBitmapUtils();
 61 
 62         // 初始化进度条
 63         FrameLayout flProgress = (FrameLayout) view
 64                 .findViewById(R.id.fl_progress);
 65         flProgress.setOnClickListener(this);
 66 
 67         pbProgress = new ProgressArc(UIUtils.getContext());
 68         // 设置圆形进度条直径
 69         pbProgress.setArcDiameter(UIUtils.dip2px(26));
 70         // 设置进度条颜色
 71         pbProgress.setProgressColor(UIUtils.getColor(R.color.progress));
 72         // 设置进度条宽高布局参数
 73         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
 74                 UIUtils.dip2px(27), UIUtils.dip2px(27));
 75         flProgress.addView(pbProgress, params);
 76 
 77         // pbProgress.setOnClickListener(this);
 78 
 79         mDM = DownloadManager.getInstance();
 80         mDM.registerObserver(this);// 注册观察者, 监听状态和进度变化
 81 
 82         return view;
 83     }
 84 
 85     @Override
 86     public void refreshView(AppInfo data) {
 87         tvName.setText(data.name);
 88         tvSize.setText(Formatter.formatFileSize(UIUtils.getContext(), data.size));
 89         tvDes.setText(data.des);
 90         rbStar.setRating(data.stars);
 91 
 92         mBitmapUtils.display(ivIcon, HttpHelper.URL + "image?name="
 93                 + data.iconUrl);
 94 
 95         // 判断当前应用是否下载过
 96         DownloadInfo downloadInfo = mDM.getDownloadInfo(data);
 97         if (downloadInfo != null) {
 98             // 之前下载过
 99             mCurrentState = downloadInfo.currentState;
100             mProgress = downloadInfo.getProgress();
101         } else {
102             // 没有下载过
103             mCurrentState = DownloadManager.STATE_UNDO;
104             mProgress = 0;
105         }
106 
107         refreshUI(mCurrentState, mProgress, data.id);
108     }
109 
110     /**
111      * 刷新界面
112      * 
113      * @param progress
114      * @param state
115      */
116     private void refreshUI(int state, float progress, String id) {
117         // 由于listview重用机制, 要确保刷新之前, 确实是同一个应用
118         if (!getData().id.equals(id)) {
119             return;
120         }
121 
122         mCurrentState = state;
123         mProgress = progress;
124         switch (state) {
125         case DownloadManager.STATE_UNDO:
126             // 自定义进度条背景
127             pbProgress.setBackgroundResource(R.drawable.ic_download);
128             // 没有进度
129             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_NO_PROGRESS);
130             tvDownload.setText("下载");
131             break;
132         case DownloadManager.STATE_WAITING:
133             pbProgress.setBackgroundResource(R.drawable.ic_download);
134             // 等待模式
135             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_WAITING);
136             tvDownload.setText("等待");
137             break;
138         case DownloadManager.STATE_DOWNLOADING:
139             pbProgress.setBackgroundResource(R.drawable.ic_pause);
140             // 下载中模式
141             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_DOWNLOADING);
142             pbProgress.setProgress(progress, true);
143             tvDownload.setText((int) (progress * 100) + "%");
144             break;
145         case DownloadManager.STATE_PAUSE:
146             pbProgress.setBackgroundResource(R.drawable.ic_resume);
147             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_NO_PROGRESS);
148             break;
149         case DownloadManager.STATE_ERROR:
150             pbProgress.setBackgroundResource(R.drawable.ic_redownload);
151             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_NO_PROGRESS);
152             tvDownload.setText("下载失败");
153             break;
154         case DownloadManager.STATE_SUCCESS:
155             pbProgress.setBackgroundResource(R.drawable.ic_install);
156             pbProgress.setStyle(ProgressArc.PROGRESS_STYLE_NO_PROGRESS);
157             tvDownload.setText("安装");
158             break;
159 
160         default:
161             break;
162         }
163     }
164 
165     // 主线程更新ui 3-4
166     private void refreshUIOnMainThread(final DownloadInfo info) {
167         // 判断下载对象是否是当前应用
168         AppInfo appInfo = getData();
169         if (appInfo.id.equals(info.id)) {
170             UIUtils.runOnUIThread(new Runnable() {
171 
172                 @Override
173                 public void run() {
174                     refreshUI(info.currentState, info.getProgress(), info.id);
175                 }
176             });
177         }
178     }
179 
180     @Override
181     public void onDownloadStateChanged(DownloadInfo info) {
182         refreshUIOnMainThread(info);
183     }
184 
185     @Override
186     public void onDownloadProgressChanged(DownloadInfo info) {
187         refreshUIOnMainThread(info);
188     }
189 
190     @Override
191     public void onClick(View v) {
192         switch (v.getId()) {
193         case R.id.fl_progress:
194             // 根据当前状态来决定下一步操作
195             if (mCurrentState == DownloadManager.STATE_UNDO
196                     || mCurrentState == DownloadManager.STATE_ERROR
197                     || mCurrentState == DownloadManager.STATE_PAUSE) {
198                 mDM.download(getData());// 开始下载
199             } else if (mCurrentState == DownloadManager.STATE_DOWNLOADING
200                     || mCurrentState == DownloadManager.STATE_WAITING) {
201                 mDM.pause(getData());// 暂停下载
202             } else if (mCurrentState == DownloadManager.STATE_SUCCESS) {
203                 mDM.install(getData());// 开始安装
204             }
205 
206             break;
207 
208         default:
209             break;
210         }
211     }
212 
213 }

分类:

技术点:

相关文章: