【0094】【项目实战】-【谷歌电子市场】-【06】【下载模块布局开发】【线程池介绍及使用】【观察者设计模式-下载功能比较复杂】【DownloadInfo对象封装】【下载-暂停-安装】【下载任务开发】【DownloadManager开发完成】【应用详情页下载逻辑】【首页下载逻辑处理】
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 }