博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android加载网络GIF完整解决方案
阅读量:3984 次
发布时间:2019-05-24

本文共 17345 字,大约阅读时间需要 57 分钟。

转自:http://blog.csdn.net/lvshaorong/article/details/51732520

前言:

加载并显示gif是App常见的一个功能,像加载普通图片一样,大体应该包含以下几项功能:

1、自动下载GIF到本地文件作为缓存,第二次加载同一个url的图片不需要下载第二遍

2、由于GIF往往较大,要显示圆形的进度条提示下载进度

3、在GIF完全下载完之前,先显示GIF的第一帧图像进行占位,完全下载完毕之后自动播放动画。

4、两个不同的页面加载同一张GIF,两个页面的加载进度应该一致

5、支持ViewPager同时加载多个GIF动图

效果演示:

          

项目github地址:

github上放了一个“demo效果.apk”可以下载装上看看效果,注意在网络较差的时候才能看清进度条的效果

实现思路:

1、关于下载和磁盘缓存:

我这里使用HttpConnection根据url进行下载,在下载之前先将url字符串使用16位MD5进行转换,让下载的文件名为url的MD5码,然后以4096字节为单位,使用ByteStremBuffer进行边读边写,防止下载过程中内存溢出,而且不时的向磁盘写入还可以帮助实现GIF第一帧占位的效果。

2、关于进度指示:

我这里使用了一个圆形的第三方Progress Bar和一个TextView实现,由于在下载过程中以4096为缓冲,所以每下载4096字节就会更新一次进度UI。文件总大小由http返回报文的头部的Content-length返回,通过已下载大小除以这个length得出下载百分比。

3、关于不同页面的下载同步:

用户在首页会看到一个gif,这时候点击图片可以跳进大图页继续这个gif的下载,用户在首页的下载进度到带到大图页来,不能让用户下载两遍,也不能在大图页打开一个才下载了一半的图像。

首先在下载开始之前,建立一个MD5.tmp的文件用来存储下载内容,在下载完毕之后将.tmp文件名后缀去掉,这样通过文件系统检索一个GIF是否已被下载的时候,没有下载完成的图片就不会被检索出来。

如果有一个url已经开始了一次下载,这时候又有一个下载请求同一个url,此时会将请求的imageView,textView和progressBar使用一个WeakReference引用起来,防止内存泄漏,然后把这三个空间添加到一个HashMap里去,这个HashMap的key是url,value就是这些控件的弱引用组成的list。当下载线程更新进度或完成的时候,会从这个HashMap中根据url取出所有和这张gif有关的控件,然后把这些控件统一的更新状态,这样就可以保证不同页面的控件的进度相同,也避免了一个文件下载多次的情况。

4、关于使用GIF的第一帧进行下载占位:

GIF的显示使用了github上的开源项目:-gif-drawable,地址:https://github.com/koral--/android-gif-drawable。是一个非常优秀的框架,其内部使用编写了一些效率非常高的执行代码。

这个框架的可以直接根据输入流进行加载,也就是说不用等gif文件完全下载完毕就可以显示已经下载完毕的内容,甚至可以向浏览器那样一行像素一行像素的进行加载,十分好用。

根据框架的这个特性,只需要将还没有下载好的文件直接传到Drawable里,让道gifImageView中显示即可,并且在这之前要判断能否拿到第一帧,然后设置播放选项为暂停。

5、关于VIewPager的使用

在ViewPager的Adapter使用的时候遇到了很多麻烦,主要是由于ViewPager的缓存机制引起的,会引起显示重复,无控件显示等等问题,要解决在ViewPager中的使用,并让GifImageView和普通ImageView一起在ViewPager中和平共处,需要先研究好ViewPager的缓存机制。在这里我是先根据所有图片数量生成同等多的imageView放在一个数组里,然后ViewPager切换到哪张就从数组里拿出哪张放到ViewPager的里。GIfImageVIew也是这样,不过是放在另一个数组里,根据position取得相应的GIFImageView,然后用container来add,这里对于add过一遍的GIfImageView会报异常,通过catch解决。

关于如何在项目中引入android-gif-drawable这个库,请看我的另一篇博文《》

具体代码:

加载工具类:

[java]   
 
  1. import android.os.Handler;  
  2. import android.util.Log;  
  3. import android.view.View;  
  4. import android.view.ViewGroup;  
  5. import android.widget.ImageView;  
  6. import android.widget.TextView;  
  7.   
  8. import java.io.BufferedInputStream;  
  9. import java.io.BufferedOutputStream;  
  10. import java.io.FileOutputStream;  
  11. import java.io.OutputStream;  
  12. import java.lang.ref.WeakReference;  
  13. import java.net.HttpURLConnection;  
  14. import java.net.URL;  
  15. import java.security.MessageDigest;  
  16.   
  17. import com.imaginato.qravedconsumer.task.AlxMultiTask;  
  18. import com.lidroid.xutils.HttpUtils;  
  19. import com.pnikosis.materialishprogress.ProgressWheel;  
  20. import com.qraved.app.R;  
  21.   
  22. import java.io.File;  
  23. import java.io.IOException;  
  24. import java.security.NoSuchAlgorithmException;  
  25. import java.util.ArrayList;  
  26. import java.util.concurrent.ConcurrentHashMap;  
  27.   
  28. import pl.droidsonroids.gif.GifDrawable;  
  29. import pl.droidsonroids.gif.GifImageView;  
  30.   
  31. /** 
  32.  * Created by Alex on 2016/6/16. 
  33.  */  
  34. public class AlxGifHelper {  
  35.   
  36.   
  37.     public static class ProgressViews{  
  38.         public ProgressViews(WeakReference<GifImageView> gifImageViewWeakReference, WeakReference<ProgressWheel> progressWheelWeakReference, WeakReference<TextView> textViewWeakReference,int displayWidth) {  
  39.             this.gifImageViewWeakReference = gifImageViewWeakReference;  
  40.             this.progressWheelWeakReference = progressWheelWeakReference;  
  41.             this.textViewWeakReference = textViewWeakReference;  
  42.             this.displayWidth = displayWidth;  
  43.         }  
  44.   
  45.         public WeakReference<GifImageView> gifImageViewWeakReference;//gif显示控件  
  46.         public WeakReference<ProgressWheel> progressWheelWeakReference;//用来装饰的圆形进度条  
  47.         public WeakReference<TextView> textViewWeakReference;//用来显示当前进度的文本框  
  48.         public int displayWidth;//imageView的控件宽度  
  49.     }  
  50.   
  51.     public static ConcurrentHashMap<String,ArrayList<ProgressViews>> memoryCache;//防止同一个gif文件建立多个下载线程,url和imageView是一对多的关系,如果一个imageView建立了一次下载,那么其他请求这个url的imageView不需要重新开启一次新的下载,这几个imageView同时回调  
  52.     //为了防止内存泄漏,这个一对多的关系均使用LRU缓存  
  53.   
  54.     /** 
  55.      * 通过本地缓存或联网加载一张GIF图片 
  56.      * @param url 
  57.      * @param gifView 
  58.      */  
  59.     public static void displayImage(final String url, GifImageView gifView, ProgressWheel progressBar , TextView tvProgress, int displayWidth){  
  60.         //首先查询一下这个gif是否已被缓存  
  61.         String md5Url = getMd5(url);  
  62.         String path = gifView.getContext().getCacheDir().getAbsolutePath()+"/"+md5Url;//带.tmp后缀的是没有下载完成的,用于加载第一帧,不带tmp后缀是下载完成的,  
  63.         //这样做的目的是为了防止一个图片正在下载的时候,另一个请求相同url的imageView使用未下载完毕的文件显示一半图像  
  64.         JLogUtils.i("AlexGIF","gif图片的缓存路径是"+path);  
  65.         final File cacheFile = new File(path);  
  66.         if(cacheFile.exists()){
    //如果本地已经有了这个gif的缓存  
  67.             JLogUtils.i("AlexGIF","本图片有缓存");  
  68.             if(displayImage(cacheFile,gifView,displayWidth)) {
    //如果本地缓存读取失败就重新联网下载  
  69.                 if (progressBar != null) progressBar.setVisibility(View.GONE);  
  70.                 if (tvProgress!=null)tvProgress.setVisibility(View.GONE);  
  71.                 return;  
  72.             }  
  73.         }  
  74.         //为了防止activity被finish了但是还有很多gif还没有加载完成,导致activity没有及时被内存回收导致内存泄漏,这里使用弱引用  
  75.         final WeakReference<GifImageView> imageViewWait= new WeakReference<GifImageView>(gifView);  
  76.         final WeakReference<ProgressWheel> progressBarWait= new WeakReference<ProgressWheel>(progressBar);  
  77.         final WeakReference<TextView> textViewWait= new WeakReference<TextView>(tvProgress);  
  78.         if(gifView.getId()!= R.id.gif_photo_view)gifView.setImageResource(R.drawable.qraved_bg_default);//设置没有下载完成前的默认图片  
  79.         if(memoryCache!=null && memoryCache.get(url)!=null){
    //如果以前有别的imageView加载过  
  80.             JLogUtils.i("AlexGIF","以前有别的ImageView申请加载过该gif"+url);  
  81.             //可以借用以前的下载进度,不需要新建一个下载线程了  
  82.             memoryCache.get(url).add(new ProgressViews(imageViewWait,progressBarWait,textViewWait,displayWidth));  
  83.             return;  
  84.         }  
  85.         if(memoryCache==null)memoryCache = new ConcurrentHashMap<>();  
  86.         if(memoryCache.get(url)==null)memoryCache.put(url,new ArrayList<ProgressViews>());  
  87.         //将现在申请加载的这个imageView放到缓存里,防止重复加载  
  88.         memoryCache.get(url).add(new ProgressViews(imageViewWait,progressBarWait,textViewWait,displayWidth));  
  89.   
  90.         final HttpUtils http = new HttpUtils();  
  91.   
  92.         // 下载图片  
  93.         startDownLoad(url, new File(cacheFile.getAbsolutePath()+".tmp"), new DownLoadTask() {  
  94.             @Override  
  95.             public void onStart() {  
  96.                 JLogUtils.i("AlexGIF","下载GIF开始");  
  97.                 ProgressWheel progressBar = progressBarWait.get();  
  98.                 TextView tvProgress = textViewWait.get();  
  99.                 if(progressBar!=null){  
  100.                     progressBar.setVisibility(View.VISIBLE);  
  101.                     progressBar.setProgress(0);  
  102.                     if(tvProgress==null)return;  
  103.                     tvProgress.setVisibility(View.VISIBLE);  
  104.                     tvProgress.setText("1%");  
  105.                 }  
  106.             }  
  107.   
  108.             @Override  
  109.             public void onLoading(long total, long current) {  
  110.                 int progress = 0;  
  111.                 //得到要下载文件的大小,是通过http报文的header的Content-Length获得的,如果获取不到就是-1  
  112.                 if(total>0)progress = (int)(current*100/total);  
  113.                 JLogUtils.i("AlexGIF","下载gif的进度是"+progress+"%"+"    现在大小"+current+"   总大小"+total);  
  114.                 ArrayList<ProgressViews> viewses = memoryCache.get(url);  
  115.                 if(viewses ==null)return;  
  116.                 JLogUtils.i("AlexGIF","该gif的请求数量是"+viewses.size());  
  117.                 for(ProgressViews vs : viewses){
    //遍历所有的进度条,修改同一个url请求的进度显示  
  118.                     ProgressWheel progressBar = vs.progressWheelWeakReference.get();  
  119.                     if(progressBar!=null){  
  120.                         progressBar.setProgress((float)progress/100f);  
  121.                         if(total==-1)progressBar.setProgress(20);//如果获取不到大小,就让进度条一直转  
  122.                     }  
  123.                     TextView tvProgress = vs.textViewWeakReference.get();  
  124.                     if(tvProgress != null)tvProgress.setText(progress+"%");  
  125.                 }  
  126.         //显示第一帧直到全部下载完之后开始动画  
  127.                 getFirstPicOfGIF(new File(cacheFile.getAbsolutePath()+".tmp"),vs.gifImageViewWeakReference.get());  
  128.             }  
  129.   
  130.             public void onSuccess(File file) {  
  131.                 if(file==null)return;  
  132.                 String path = file.getAbsolutePath();  
  133.                 if(path==null || path.length()<5)return;  
  134.                 File downloadFile = new File(path);  
  135.                 File renameFile = new File(path.substring(0,path.length()-4));  
  136.                 if(path.endsWith(".tmp"))downloadFile.renameTo(renameFile);//将.tmp后缀去掉  
  137.                 Log.i("AlexGIF","下载GIf成功,文件路径是"+path+" 重命名之后是"+renameFile.getAbsolutePath());  
  138.                 if(memoryCache==null)return;  
  139.                 ArrayList<ProgressViews> viewArr = memoryCache.get(url);  
  140.                 if(viewArr==null || viewArr.size()==0)return;  
  141.                 for(ProgressViews ws:viewArr){
    //遍历所有的进度条和imageView,同时修改所有请求同一个url的进度  
  142.                     //显示imageView  
  143.                     GifImageView gifImageView = ws.gifImageViewWeakReference.get();  
  144.                     if (gifImageView!=null)displayImage(renameFile,gifImageView,ws.displayWidth);  
  145.                     //修改进度条  
  146.                     TextView tvProgress = ws.textViewWeakReference.get();  
  147.                     ProgressWheel progressBar = ws.progressWheelWeakReference.get();  
  148.                     if(progressBar!=null)progressBar.setVisibility(View.GONE);  
  149.                     if(tvProgress!=null)tvProgress.setVisibility(View.GONE);  
  150.                 }  
  151.                 JLogUtils.i("AlexGIF",url+"的imageView已经全部加载完毕,共有"+viewArr.size()+"个");  
  152.                 memoryCache.remove(url);//这个url的全部关联imageView都已经显示完毕,清除缓存记录  
  153.             }  
  154.   
  155.             @Override  
  156.             public void onFailure(Throwable e) {  
  157.                 Log.i("Alex","下载gif图片出现异常",e);  
  158.                 TextView tvProgress = textViewWait.get();  
  159.                 ProgressWheel progressBar = progressBarWait.get();  
  160.                 if(progressBar!=null)progressBar.setVisibility(View.GONE);  
  161.                 if(tvProgress!=null)tvProgress.setText("image download failed");  
  162.                 if(memoryCache!=null)memoryCache.remove(url);//下载失败移除所有的弱引用  
  163.             }  
  164.         });  
  165.     }  
  166.   
  167.   
  168.   
  169.     /** 
  170.      * 通过本地文件显示GIF文件 
  171.      * @param localFile 本地的文件指针 
  172.      * @param gifImageView 
  173.      * displayWidth imageView控件的宽度,用于根据gif的实际高度重设控件的高度来保证完整显示,传0表示不缩放gif的大小,显示原始尺寸 
  174.      */  
  175.     public static boolean displayImage(File localFile,GifImageView gifImageView,int displayWidth){  
  176.         if(localFile==null || gifImageView==null)return false;  
  177.         JLogUtils.i("AlexGIF","准备加载gif"+localFile.getAbsolutePath()+"显示宽度为"+displayWidth);  
  178.         GifDrawable gifFrom;  
  179.         try {  
  180.             gifFrom = new GifDrawable(localFile);  
  181.             int raw_height = gifFrom.getIntrinsicHeight();  
  182.             int raw_width = gifFrom.getIntrinsicWidth();  
  183.             JLogUtils.i("AlexGIF","图片原始height是"+raw_height+"  图片原始宽是:"+raw_width);  
  184.             if(gifImageView.getScaleType() != ImageView.ScaleType.CENTER_CROP && gifImageView.getScaleType()!= ImageView.ScaleType.FIT_XY){  
  185.                 //如果大小应该自适应的话进入该方法(也就是wrap content),不然高度不会自动变化  
  186.                 if(raw_width<1 || raw_height<1)return false;  
  187.                 int imageViewWidth = displayWidth;  
  188.                 if(imageViewWidth < 1)imageViewWidth = raw_width;//当传来的控件宽度不大对的时候,就显示gif的原始大小  
  189.                 int imageViewHeight = imageViewWidth*raw_height/raw_width;  
  190.                 JLogUtils.i("AlexGIF","缩放完的gif是"+imageViewWidth+" X "+imageViewHeight);  
  191.                 ViewGroup.LayoutParams params = gifImageView.getLayoutParams();  
  192.                 if(params!=null){  
  193.                     params.height = imageViewHeight;  
  194.                     params.width = imageViewWidth;  
  195.                 }  
  196.             }else {  
  197.                 JLogUtils.i("AlexGIF","按照固定大小进行显示");  
  198.             }  
  199.             gifImageView.setImageDrawable(gifFrom);  
  200.             return true;  
  201.         } catch (IOException e) {  
  202.             JLogUtils.i("AlexGIF","显示gif出现异常",e);  
  203.             return false;  
  204.         }  
  205.     }  
  206.   
  207.     /** 
  208.      * 用于获取一个String的md5值 
  209.      * @param str 
  210.      * @return 
  211.      */  
  212.     public static String getMd5(String str) {  
  213.         if(str==null || str.length()<1)return "no_image.gif";  
  214.         MessageDigest md5 = null;  
  215.         try {  
  216.             md5 = MessageDigest.getInstance("MD5");  
  217.             byte[] bs = md5.digest(str.getBytes());  
  218.             StringBuilder sb = new StringBuilder(40);  
  219.             for(byte x:bs) {  
  220.                 if((x & 0xff)>>4 == 0) {  
  221.                     sb.append("0").append(Integer.toHexString(x & 0xff));  
  222.                 } else {  
  223.                     sb.append(Integer.toHexString(x & 0xff));  
  224.                 }  
  225.             }  
  226.             if(sb.length()<24)return sb.toString();  
  227.             return sb.toString().substring(8,24);//为了提高磁盘的查找文件速度,让文件名为16位  
  228.         } catch (NoSuchAlgorithmException e) {  
  229.             JLogUtils.i("Alex","MD5加密失败");  
  230.             return "no_image.gif";  
  231.         }  
  232.     }  
  233.   
  234.     public static abstract class DownLoadTask{  
  235.         abstract void onStart();  
  236.         abstract void onLoading(long total, long current);  
  237.         abstract void onSuccess(File target);  
  238.         abstract void onFailure(Throwable e);  
  239.         boolean isCanceled;  
  240.     }  
  241.   
  242.     /** 
  243.      * 开启下载任务到线程池里,防止多并发线程过多 
  244.      * @param uri 
  245.      * @param targetFile 
  246.      * @param task 
  247.      */  
  248.     public static void startDownLoad(final String uri, final File targetFile, final DownLoadTask task){  
  249.         final Handler handler = new Handler();  
  250.         new AlxMultiTask<Void,Void,Void>(){
    //开启一个多线程池,大小为cpu数量+1  
  251.   
  252.             @Override  
  253.             protected Void doInBackground(Void... params) {  
  254.                 task.onStart();  
  255.                 downloadToStream(uri,targetFile,task,handler);  
  256.                 return null;  
  257.             }  
  258.         }.executeDependSDK();  
  259.     }  
  260.   
  261.   
  262.     /** 
  263.      * 通过httpconnection下载一个文件,使用普通的IO接口进行读写 
  264.      * @param uri 
  265.      * @param targetFile 
  266.      * @param task 
  267.      * @return 
  268.      */  
  269.     public static long downloadToStream(String uri, final File targetFile, final DownLoadTask task, Handler handler) {  
  270.   
  271.         if (task == null || task.isCanceled) return -1;  
  272.   
  273.         HttpURLConnection httpURLConnection = null;  
  274.         BufferedInputStream bis = null;  
  275.         OutputStream outputStream = null;  
  276.   
  277.         long result = -1;  
  278.         long fileLen = 0;  
  279.         long currCount = 0;  
  280.         try {  
  281.   
  282.                 try {  
  283.                     final URL url = new URL(uri);  
  284.                     outputStream = new FileOutputStream(targetFile);  
  285.                     httpURLConnection = (HttpURLConnection) url.openConnection();  
  286.                     httpURLConnection.setConnectTimeout(20000);  
  287.                     httpURLConnection.setReadTimeout(10000);  
  288.   
  289.                     final int responseCode = httpURLConnection.getResponseCode();  
  290.                     if (HttpURLConnection.HTTP_OK == responseCode) {  
  291.                         bis = new BufferedInputStream(httpURLConnection.getInputStream());  
  292.                         result = httpURLConnection.getExpiration();  
  293.                         result = result < System.currentTimeMillis() ? System.currentTimeMillis() + 40000 : result;  
  294.                         fileLen = httpURLConnection.getContentLength();//这里通过http报文的header Content-Length来获取gif的总大小,需要服务器提前把header写好  
  295.                     } else {  
  296.                         Log.e("Alex","downloadToStream -> responseCode ==> " + responseCode);  
  297.                         return -1;  
  298.                     }  
  299.                 } catch (final Exception ex) {  
  300.                     handler.post(new Runnable() {  
  301.                         @Override  
  302.                         public void run() {  
  303.                             task.onFailure(ex);  
  304.                         }  
  305.                     });  
  306.                     return -1;  
  307.                 }  
  308.   
  309.   
  310.             if (task.isCanceled) return -1;  
  311.   
  312.             byte[] buffer = new byte[4096];//每4k更新进度一次  
  313.             int len = 0;  
  314.             BufferedOutputStream out = new BufferedOutputStream(outputStream);  
  315.             while ((len = bis.read(buffer)) != -1) {  
  316.                 out.write(buffer, 0, len);  
  317.                 currCount += len;  
  318.                 if (task.isCanceled) return -1;  
  319.                 final long finalFileLen = fileLen;  
  320.                 final long finalCurrCount = currCount;  
  321.                 handler.post(new Runnable() {  
  322.                     @Override  
  323.                     public void run() {  
  324.                         task.onLoading(finalFileLen, finalCurrCount);  
  325.                     }  
  326.                 });  
  327.             }  
  328.             out.flush();  
  329.             handler.post(new Runnable() {  
  330.                 @Override  
  331.                 public void run() {  
  332.                     task.onSuccess(targetFile);  
  333.                 }  
  334.             });  
  335.         } catch (Throwable e) {  
  336.             result = -1;  
  337.             task.onFailure(e);  
  338.         } finally {  
  339.             if (bis != null) {  
  340.                 try {  
  341.                     bis.close();  
  342.                 } catch (final Throwable e) {  
  343.                     handler.post(new Runnable() {  
  344.                         @Override  
  345.                         public void run() {  
  346.                             task.onFailure(e);  
  347.                         }  
  348.                     });  
  349.                 }  
  350.             }  
  351.         }  
  352.         return result;  
  353.     }  
  354.   
  355.  /** 
  356.      * 加载gif的第一帧图像,用于下载完成前占位 
  357.      * @param gifFile 
  358.      * @param imageView 
  359.      */  
  360.     public static void getFirstPicOfGIF(File gifFile,GifImageView imageView){  
  361.         if(imageView==null)return;  
  362.         if(imageView.getTag(R.style.AppTheme) instanceof Integer)return;//之前已经显示过第一帧了,就不用再显示了  
  363.         try {  
  364.             GifDrawable gifFromFile = new GifDrawable(gifFile);  
  365.             boolean canSeekForward = gifFromFile.canSeekForward();  
  366.             if(!canSeekForward)return;  
  367.             JLogUtils.i("AlexGIF","是否能显示第一帧图片"+canSeekForward);  
  368.             //下面是一些其他有用的信息  
  369. //            int frames = gifFromFile.getNumberOfFrames();  
  370. //            JLogUtils.i("AlexGIF","已经下载完多少帧"+frames);  
  371. //            int bytecount = gifFromFile.getFrameByteCount();  
  372. //            JLogUtils.i("AlexGIF","一帧至少多少字节"+bytecount);  
  373. //            long memoryCost = gifFromFile.getAllocationByteCount();  
  374. //            JLogUtils.i("AlexGIF","内存开销是"+memoryCost);  
  375.             gifFromFile.seekToFrame(0);  
  376.             gifFromFile.pause();//静止在该帧  
  377.             imageView.setImageDrawable(gifFromFile);  
  378.             imageView.setTag(R.style.AppTheme,1);//标记该imageView已经显示过第一帧了  
  379.         } catch (IOException e) {  
  380.             JLogUtils.i("AlexGIF","获取gif信息出现异常",e);  
  381.         }  
  382.     }  
  383. }  

线程池:

[java]   
 
  1. import android.os.AsyncTask;  
  2. import android.os.Build;  
  3.   
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6.   
  7. /** 
  8.  * Created by Alex on 2016/4/19. 
  9.  * 用于替换系统自带的AsynTask,使用自己的多线程池,执行一些比较复杂的工作,比如select photos,这里用的是缓存线程池,也可以用和cpu数相等的定长线程池以提高性能 
  10.  */  
  11. public abstract class AlxMultiTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {  
  12.     private static ExecutorService photosThreadPool;//用于加载大图的线程池  
  13.     private final int CPU_COUNT = Runtime.getRuntime().availableProcessors();  
  14.     private final int CORE_POOL_SIZE = CPU_COUNT + 1;  
  15.     public void executeDependSDK(Params...params){  
  16.         if(photosThreadPool==null)photosThreadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);  
  17.         if(Build.VERSION.SDK_INT<11super.execute(params);  
  18.         else super.executeOnExecutor(photosThreadPool,params);  
  19.     }  
  20.   
  21. }  

ViewPager Adpater的写法(截取)

[java]   
 
  1. public class PhotoImageViewPageAdapter extends PagerAdapter {  
  2.     @Override  
  3.     public Object instantiateItem(ViewGroup container, int position) {  
  4.    
  5.                 String imageUrl = "http://xxx.com/sdf/xxx.gif";  
  6.                 JLogUtils.i("AlexGIF","当前图片->"+imageUrl);  
  7.                 if(imageUrl.endsWith(".gif")){
    //如果是gif动图  
  8.                 JLogUtils.i("AlexGIF","现在是gif大图");  
  9.                 View rl_gif = LayoutInflater.from(activity).inflate(R.layout.layout_photo_loading_gif_imageview, null);//这种方式容易导致内存泄漏  
  10.                     GifImageView gifImageView = (GifImageView) rl_gif.findViewById(R.id.gif_photo_view);  
  11.                     ProgressWheel progressWheel = (ProgressWheel) rl_gif.findViewById(R.id.progress_wheel);  
  12.                     CustomTextView tv_progress = (CustomTextView) rl_gif.findViewById(R.id.tv_progress);  
  13.                     AlxGifHelper.displayImage(imageUrl,gifImageView,progressWheel,tv_progress,0);//最后一个参数传0表示不缩放gif的大小,显示原始尺寸  
  14.                     try {  
  15.                         container.addView(rl_gif);//这里要注意由于container是一个复用的控件,所以频繁的addView会导致多张相同的图片重叠,必须予以处置  
  16.                     }catch (Exception e){  
  17.                         JLogUtils.i("AlexGIF","父控件重复!!!!,这里出现异常很正常",e);  
  18.                     }  
  19.                     return rl_gif;//这里有个大坑,千万不能return container,但是在return之前必须addView  
  20.                  
  21.             }  
  22.         }  
  23.         return container;  
  24.     }  
  25. }  

布局文件

[html]   
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:wheel="http://schemas.android.com/apk/res-auto"  
  4.     android:id="@+id/rl_gif"  
  5.     android:orientation="vertical"  
  6.     android:layout_width="match_parent"  
  7.     android:layout_height="match_parent">  
  8.   
  9.     <pl.droidsonroids.gif.GifImageView  
  10.         android:id="@+id/gif_photo_view"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent"  
  13.         android:layout_centerVertical="true"  
  14.         android:layout_centerHorizontal="true"  
  15.         />  
  16.     <TextView  
  17.         android:id="@+id/tv_progress"  
  18.         android:layout_width="wrap_content"  
  19.         android:layout_height="wrap_content"  
  20.         android:textSize="15sp"  
  21.         android:layout_centerHorizontal="true"  
  22.         android:layout_centerVertical="true"  
  23.         android:textColor="@color/white"  
  24.         android:text="2%"  
  25.         />  
  26.     <com.pnikosis.materialishprogress.ProgressWheel  
  27.         android:id="@+id/progress_wheel"  
  28.         android:layout_width="60dp"  
  29.         android:layout_height="60dp"  
  30.         android:layout_centerHorizontal="true"  
  31.         android:layout_centerVertical="true"  
  32.         android:layout_gravity="center"  
  33.         wheel:matProg_barColor="#5097DA"  
  34.         wheel:matProg_progressIndeterminate="true" />  
  35.   
  36. </RelativeLayout>  

中间的ProgressBar使用了一个第三方库

[java]   
 
  1. dependencies {  
  2.     compile 'com.pnikosis:materialish-progress:1.7'  
  3. }  

你可能感兴趣的文章
[LeetCode By Python]172. Factorial Trailing Zeroes
查看>>
[LeetCode By MYSQL] Combine Two Tables
查看>>
Mac删除文件&文件夹
查看>>
python jieba分词模块的基本用法
查看>>
[CCF BY C++]2017.12 最小差值
查看>>
[CCF BY C++]2017-12 游戏
查看>>
《Fluent Python》第三章Dictionaries and Sets
查看>>
如何打开ipynb文件
查看>>
[Leetcode BY python ]190. Reverse Bits
查看>>
面试---刷牛客算法题
查看>>
Android下调用收发短信邮件等(转载)
查看>>
Android中电池信息(Battery information)的取得
查看>>
SVN客户端命令详解
查看>>
Android/Linux 内存监视
查看>>
Linux系统信息查看
查看>>
用find命令查找最近修改过的文件
查看>>
Android2.1消息应用(Messaging)源码学习笔记
查看>>
在android上运行native可执行程序
查看>>
Phone双模修改涉及文件列表
查看>>
android UI小知识点
查看>>