listview,gridview 动态加载网络图片

来源:互联网 发布:开展数据库系统培训 编辑:程序博客网 时间:2024/05/21 14:50

        最近在做一个android播放器客户端。需要把服务器上的资源信息拉到手机上显示。这就离不开listview。一开始我举得很容易的,但真正做起来就会出现很多意想不到的问题。比如说listview滑动不流畅,图片加载混乱,甚至会有OOM。等等这些问题我都碰到过,可能我是菜鸟,这些低级的问题都被我碰到了,但我很肯定的说,以后再做类似的事情,问题就迎刃而解了。

        说一下listview 和gridview的区别。其实没有什么区别,我们能理解生活中用到的桶和盆有什么区别,就能理解这两个的区别,同样的容器,只是外表不一样而已。实现方式都会是new 一个adapter出来,然后用listview(gridview).setAdapter(adapter)。再加一些监听之类的就差不多了。

    说了半天,还没进入主题。listview加载网络图片的实现,我有找过很多资料,但用的都不是很符合自己的要求。最后找到了一个高手的code,得以解决之。添加url在此:http://www.cnblogs.com/liongname/articles/2345087.html

文章中只有用到临时缓存,我这里再添加一个存储,保存到sdcard。

首先我们要实现一个异步加载图片的类,用来对listview的每一个item加载图片:

 

public enum BitmapManager {      INSTANCE;      public static final String TAG = "BitmapManager";    private final Map<String, SoftReference<Bitmap>> cache;    //对图片做缓存    private final ExecutorService pool;      private Map<ImageView, String> imageViews = Collections              .synchronizedMap(new WeakHashMap<ImageView, String>());      private Bitmap placeholder;        BitmapManager() {          cache = new HashMap<String, SoftReference<Bitmap>>();          pool = Executors.newFixedThreadPool(5);  //线程池    }        public void setPlaceholder(Bitmap bmp) {     //设置占位图,如果加载未完成前应该显示的图片        placeholder = bmp;      }        public Bitmap getBitmapFromCache(String url) {          if (cache.containsKey(url)) {              return cache.get(url).get();          }            return null;      }        public void queueJob(final String url, final ImageView imageView,              final int width, final int height) {          /* Create handler in UI thread. */          final Handler handler = new Handler() {              @Override              public void handleMessage(Message msg) {                  String tag = imageViews.get(imageView);                  if (tag != null && tag.equals(url)) {   //根据url对应的ImageView 设置图片显示                     if (msg.obj != null) {                          imageView.setImageBitmap((Bitmap) msg.obj);                      } else {                          imageView.setImageBitmap(placeholder);                          Log.d(null, "fail " + url);                      }                  }              }          };            pool.submit(new Runnable() {  //线程异步load图片             @Override              public void run() {                  final Bitmap bmp = downloadBitmap(url, width, height);                  Message message = Message.obtain();                  message.obj = bmp;                  Log.d(null, "Item downloaded: " + url);                    handler.sendMessage(message);              }          });      }        public void loadBitmap(final String url, final ImageView imageView,              final int width, final int height) {          imageViews.put(imageView, url);  //对传进来的imageView 和url做关联,防止图片混乱          Bitmap bitmap = getBitmapFromCache(url);            // check in UI thread, so no concurrency issues          if (bitmap != null) {              Log.d(null, "Item loaded from cache: " + url);              imageView.setImageBitmap(bitmap);          } else {              imageView.setImageBitmap(placeholder);              queueJob(url, imageView, width, height);          }      }        private Bitmap downloadBitmap(String url, int width, int height) {      Bitmap sdcardFile = getSDCardBitmap(url);   //优先从sdcard中取图片    if(sdcardFile != null){    sdcardFile = Bitmap.createScaledBitmap(sdcardFile, width, height, true);    cache.put(url, new SoftReference<Bitmap>(sdcardFile));       return sdcardFile;    }        try {              Bitmap bitmap = BitmapFactory.decodeStream((InputStream) new URL(                      url).getContent());              bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);            saveSDCardBitmap(url,bitmap); //存入sdcard;            cache.put(url, new SoftReference<Bitmap>(bitmap));              return bitmap;          } catch (MalformedURLException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }            return null;      }          private void saveSDCardBitmap(String url,Bitmap bm){    String filePath = Environment.getExternalStorageDirectory()+"/imagecache/"+url.substring(url.lastIndexOf("/")+1); //从url中取文件名以保存到sdcard,方便取。    final File f = new File(filePath);    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){    File path = new File(Environment.getExternalStorageDirectory()+"/imagecache");if(!path.exists()){path.mkdirs();}try {FileOutputStream out = new FileOutputStream(f);bm.compress(Bitmap.CompressFormat.PNG, 100, out);out.flush();out.close();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}catch(IOException e){e.printStackTrace(); }    }    }        private Bitmap getSDCardBitmap(String url){    String filePath = Environment.getExternalStorageDirectory()+"/imagecache/"+url.substring(url.lastIndexOf("/")+1);    final File f = new File(filePath);    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  //sdcard 是否存在    File path = new File(Environment.getExternalStorageDirectory()+"/imagecache");if(!path.exists()){path.mkdirs();}if(f.exists()){Bitmap tmp = BitmapFactory.decodeFile(filePath);return tmp;}    }    return null;        }}


这个类写好后,我们就只需要在Adapter中的getView()方法中调用这个类就行了。

下面我们来看getView()方法:

public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){holder = new ViewHolder();convertView = mInflater.inflate(R.layout.list_unit,null);  /获取list单元布局holder.Image = (ImageView)findViewById(R.id.mImage);holder.Title = (TextView)findViewbyId(R.id.mTitle);convertView.setTag(hodler);//添加标签}else{holder = convertView.getTag();}if(!mBusy){BitmapManager.INSTANCE.setPlaceholder(bm); //设置默认图片  BitmapManager.INSTANCE.loadBitmap(url, holder.Image, imageWidth,imageHeight);}else{holder.Image.setImageBitmap(bm);}return convertView;}public static class ViewHolder{   ImageViwe Image;TextView Title;}OnScrollListener mScrollListener = new OnScrollListener() {   //对listview的滚动监听@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {switch (scrollState) {case OnScrollListener.SCROLL_STATE_FLING:ListViewAdapter.this.setFlagBusy(true);break;case OnScrollListener.SCROLL_STATE_IDLE:ListViewAdapter.this.setFlagBusy(false);break;case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:ListViewAdapter.this.setFlagBusy(false);break;default:break;}ListViewAdapter.this.notifyDataSetChanged();}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {}};protected void setFlagBusy(boolean b) {// TODO Auto-generated method stubmBusy = b;   //全局变量 mBusy,表示listview是否滚动}


到这里,我们的ListView异步加载图片的主要工作基本完成。剩下的就是在activity中实例化以及set的工作。这个方法对ListView 和GridView都是通用的。目前我还没有发现有图片混乱,内存溢出,滑动不流畅等问题。当然,再次申明,这里的大部分代码不是我的,我只是拿来优化成了一个适合我自己的东西了。平时都忙着coding,对于很多临时找到的好东西,自己感悟出来的东西(当然很少了),都没有及时记下来,等到需要时又找不到了,不得不说这是一大损失。