ViewPager做图片浏览器,加载大量图片OOM的问题修正
1 /** 2 * @author CHQ 3 * @version 1.0 4 * @date 创建时间: 2016/7/26 17:18 5 * @parameter 6 * @return 7 * 图片查看器 8 * //可以查看网络图片 9 * //可以查看本地图片 10 */ 11 public class PhotoScan extends Activity { 12 private PhotoViewPager mViewPager; 13 private List<View> mViews; 14 private List<String> mPics; 15 private int index; 16 17 private ImageView mImageView; //当前图片 18 private PhotoViewAttacher mAttacher; //图片放大缩小查看 19 20 private TextView tv_hint01; 21 private TextView tv_hint02; 22 private int imageType = 0; //判断图片浏览器查看的图片类型:0.网络图片 1.本地图片 23 private LruCache<String,Bitmap> mLruCache; 24 private int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024); 25 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_picture_view); 30 init(); 31 setEvent(); 32 } 33 34 private void init() { 35 MyApplication.getInstance().addActivity("PhotoScan",PhotoScan.this); 36 index = getIntent().getIntExtra("index",0); 37 imageType = getIntent().getIntExtra("imageType",0); 38 mViewPager = (PhotoViewPager) findViewById(R.id.viewPager); 39 mLruCache = new LruCache<>(maxMemory/8); 40 tv_hint01 = (TextView) findViewById(R.id.tv_hint01); 41 tv_hint02 = (TextView) findViewById(R.id.tv_hint02); 42 43 mPics = getIntent().getStringArrayListExtra("pics"); 44 tv_hint02.setText(" /"+mPics.size()); 45 mViews = new ArrayList<>(); 46 LayoutInflater inflater = LayoutInflater.from(this); 47 for(int i = 0 ; i< mPics.size() ; i++){ 48 View view = inflater.inflate(R.layout.item_picture_view,null); 49 mViews.add(view); 50 } 51 52 MyAdapter adapter = new MyAdapter(mViews,mPics,this); 53 mViewPager.setAdapter(adapter); 54 mViewPager.setCurrentItem(index); 55 index += 1; //设置当前图片指示器 56 tv_hint01.setText(""+index); 57 } 58 59 private void setEvent() { 60 mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 61 @Override 62 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 63 64 } 65 @Override 66 public void onPageSelected(int position) { 67 index = position+1; //设置当前图片指示器 68 tv_hint01.setText(""+index); 69 } 70 @Override 71 public void onPageScrollStateChanged(int state) { 72 73 } 74 }); 75 } 76 77 @Override 78 public boolean onKeyDown(int keyCode, KeyEvent event) { 79 if(keyCode == KeyEvent.KEYCODE_BACK){ 80 MyApplication.getInstance().deleteActivity("PhotoScan",PhotoScan.this); 81 } 82 return super.onKeyDown(keyCode, event); 83 } 84 85 private class MyAdapter extends PagerAdapter{ 86 List<View> mViews; 87 List<String> mPics; 88 Context context; 89 public MyAdapter(List<View> mViews,List<String> mPics,Context context){ 90 this.mViews = mViews; 91 this.mPics = mPics; 92 this.context = context; 93 } 94 //viewpager中的组件数量 95 @Override 96 public int getCount() { 97 return mViews.size(); 98 } 99 100 //滑动切换的时候销毁当前的组件 101 @Override 102 public void destroyItem(ViewGroup container, int position, Object object) { 103 ((ViewPager)container).removeView(mViews.get(position)); 104 } 105 106 //每次滑动的时候,生成组件 107 @Override 108 public Object instantiateItem(ViewGroup container, int position) { 109 View view = mViews.get(position); 110 mImageView = ((ImageView) view.findViewById(R.id.iv)); 111 String picurl = mPics.get(position); 112 setPic(picurl,view); 113 ((ViewPager)container).removeView(mViews.get(position)); 114 ((ViewPager)container).addView(mViews.get(position)); 115 return mViews.get(position); 116 } 117 118 @Override 119 public boolean isViewFromObject(View view, Object object) { 120 return view == object; 121 } 122 123 @Override 124 public int getItemPosition(Object object) { 125 return super.getItemPosition(object); 126 } 127 } 128 129 /** 130 * 加载网络图片 131 */ 132 private void setPic(String picurl, final View parentView){ 133 if(!picurl.contains("http://") && imageType == 0) { 134 picurl = MyConfig.picFirst+picurl; 135 } 136 if(imageType == 1){ //"/mnt/sdcard/mImageView.png" 137 picurl = ImageDownloader.Scheme.FILE.wrap(picurl); 138 } 139 Log.i("main",picurl); 140 //加载自定义配置的一个图片的,网络加载监听,等待加载图片完成再初始化缩小放大 141 ImageLoader.getInstance().displayImage(picurl, mImageView, MyApplication.OptionsNoCache, new SimpleImageLoadingListener() { 142 @Override 143 public void onLoadingStarted(String imageUri, View view) { 144 super.onLoadingStarted(imageUri, view); 145 Utility.setLoadingProgressbar(null,parentView,true); 146 } 147 148 @Override 149 public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { 150 super.onLoadingComplete(imageUri, view, loadedImage); 151 Utility.setLoadingProgressbar(null,parentView,false); 152 mAttacher = new PhotoViewAttacher(mImageView); 153 mAttacher.update(); 154 } 155 }, new ImageLoadingProgressListener() { 156 @Override 157 public void onProgressUpdate(String s, View view, int i, int i1) { 158 Log.i("main","i="+i+",il="+i1); 159 } 160 }); 161 162 } 163 }
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" 7 > 8 <include 9 layout="@layout/ll_progessbar1" 10 /> 11 <ImageView 12 android:id="@+id/iv" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent" 15 android:scaleType="centerInside" 16 > 17 </ImageView> 18 </LinearLayout>
mViews<View>存放在(包含)ImageView的引用,随着imageView设置Bitmap的增加,大概30张大图之后就基本OOM了,这时要做优化修改:
1、修改mViews的大小,默认只有4个
1 for(int i = 0 ; i< 4 ; i++){ 2 View view = inflater.inflate(R.layout.item_picture_view,null); 3 mViews.add(view); 4 }
2、修改容器里面关于mViews的使用
private class MyAdapter extends PagerAdapter{
List<View> mViews;
List<String> mPics;
Context context;
public MyAdapter(List<View> mViews,List<String> mPics,Context context){
this.mViews = mViews;
this.mPics = mPics;
this.context = context;
}
//viewpager中的组件数量
@Override
public int getCount() {
return mPics.size();
}
//滑动切换的时候销毁当前的组件
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
int i = position%4;
Log.i("main","正在销毁第几页:"+position+",正在销毁对应mViews的第几个数据源:"+i);
((ViewPager)container).removeView(mViews.get(i));
}
//每次滑动的时候,生成组件
@Override
public Object instantiateItem(ViewGroup container, int position) {
int i = position%4;
View view = mViews.get(i);
mImageView = ((ImageView) view.findViewById(R.id.iv));
String picurl = mPics.get(position);
setPic(picurl,view);
Log.i("main","正在生成第几页:"+position+",正在调用mViews的第几个数据源:"+i);
((ViewPager)container).addView(mViews.get(i));
return mViews.get(i);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public int getItemPosition(Object object) {
return super.getItemPosition(object);
}
}
/**
* 加载网络图片
*/
private void setPic(String picurl, final View parentView){
if(!picurl.contains("http://") && imageType == 0) {
picurl = MyConfig.picFirst+picurl;
}
if(imageType == 1){ //"/mnt/sdcard/mImageView.png"
picurl = ImageDownloader.Scheme.FILE.wrap(picurl);
}
Log.i("main",picurl);
//加载自定义配置的一个图片的,网络加载监听,等待加载图片完成再初始化缩小放大
ImageLoader.getInstance().displayImage(picurl, mImageView, MyApplication.OptionsWithCache, new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
super.onLoadingStarted(imageUri, view);
Utility.setLoadingProgressbar(null,parentView,true);
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
Utility.setLoadingProgressbar(null,parentView,false);
mAttacher = new PhotoViewAttacher(mImageView);
mAttacher.update();
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String s, View view, int i, int i1) {
Log.i("main","i="+i+",il="+i1);
}
});
}
也就是说,整个方案中最多只保存3个ImageView,建立4个数据源的mViews,那时因为ViewPager在滑动的时候:
打开第一页时:
第2页、第3页:
第4页、第5页:
第5页-》第4页:
这里可以看到,从第2页滑动向第3页时,是先销毁第1页,再构造第3页,也就是先destoryItem再InstantiateItem,而销毁和构造ViewPager视图的都是用到同一个mView.get(i)(如从第2-》3页,销毁和构造都是0;从第4-》5页,销毁和构造都是2),这里是无问题的;
但是,如果从第5页滑到回来第4页就出问题了,这里是先构造第4页的前前一页,也就是第2页,销毁的时候,正在被当前视图使用,这里就会出错了。所有改用包含4个子项的mViews
最后上一个修改之后的log,页数变化如:1-2-3-4-5-6-5-4