解决ListView滑动时卡的问题,实现异步加载图片解决

ListView是最为常见的空间之一,现在的应用的呈现形式大多数都需要用到ListView来呈现,以列表的方式最直观最便于操作。

那么在使用的过程中大家一定使用adapter适配器来匹配这个ListView,问题就来了,如果直接使用sampleAdapter的话,会出现诸多的问题,诸如滚动的时候很卡,特别是每一行都有头像啊什么的,再加上数据量一大,兼职就卡的不行,那么先来说说解决卡的问题的简单的实现方法吧。

首先需要自己来写一个myAdapter继承与BaseAdapter。

然后最关键的是在getView方法中做到以下几点判断convertView是否为空,这样可以避免每次滚动都去新建,可以节约大量资源,同时对于图片采用开启线程以异步加载的方式来加载它,又可以节约一部分资源,同时,将加载下来的图片缓存到本地,当下一次的时候首先读取本地图片,第一可以节约流量,其次速度非常快(不过服务器图片更新了这个要想好解决方案,不然你本地的图片不会被替换掉)。最后就是在滚动时间的时候让异步加载暂停,等手放开的时候再加载,这样可以保证滚动的超级流畅,不过同时会出现滚动比较大的时候图片都还是空的没有加载的现象,反正自己斟酌优劣吧,代码都有哈!接下来上代码了

首先是主activity

package com.challen;

import java.util.Vector;

import cindy.android.test.synclistview.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class TestListViewActivity extends Activity implements
		AdapterView.OnItemClickListener {

	ListView viewBookList;
	BookItemAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		viewBookList = (ListView) findViewById(R.id.viewBookList);

		adapter = new BookItemAdapter(this, viewBookList);

		viewBookList.setAdapter(adapter);
		viewBookList.setOnItemClickListener(this);
		reload();
	}

	private void reload() {
		adapter.clean();
		// loadStateView.startLoad();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(2 * 1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				loadDate();
				sendMessage(REFRESH_LIST);
			}
		}).start();
	}

	public void loadDate() {
		for (int i = 0; i < 100; i++) {
			adapter.addBook("我是challen的测试异步加" + i, "1",
					"http://ww1.sinaimg.cn/thumbnail/80ab1ad3gw1dx8tfjvbgdj.jpg");

			adapter.addBook("小美" + i, "2",
					"http://ww2.sinaimg.cn/thumbnail/7f9fd9a9jw1dtyrqrh4mjj.jpg");

			adapter.addBook("金总" + i, "3",
					"http://ww3.sinaimg.cn/thumbnail/9d57e8e4jw1dx6topumz5j.jpg");

			adapter.addBook("创意铺子" + i, "4",
					"http://www.pfwx.com/files/article/image/3/3237/3237s.jpg");

			adapter.addBook("人名日报" + i, "5",
					"http://ww2.sinaimg.cn/thumbnail/9263d293jw1dx8snx58s7j.jpg");

			adapter.addBook("名字是乱明的" + i, "6",
					"http://tp1.sinaimg.cn/1660452532/50/5646449168/0");
			adapter.addBook("帅哥即将出现" + i, "7",
					"http://p1.qhimg.com/t01a869bb64c7f3d8c6.png");
			adapter.addBook("注意了哦" + i, "8",
					"http://www.baidu.com/img/baidu_jgylogo3.gif");
			adapter.addBook("来拉" + i, "9",
					"http://tp4.sinaimg.cn/2190322767/50/5605436918/1");
			adapter.addBook("这个就是我啦" + i, "10",
					"http://avatar.csdn.net/E/7/2/3_jkingcl.jpg");

		}
	}

	private static final int REFRESH_LIST = 0x10001;
	public static final int SHOW_STR_TOAST = 0;
	public static final int SHOW_RES_TOAST = 1;

	private Handler pichandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (!Thread.currentThread().isInterrupted()) {
				handleOtherMessage(msg.what);
			}
		}
	};

	public void sendMessage(int flag) {
		pichandler.sendEmptyMessage(flag);
	}

	protected void handleOtherMessage(int flag) {
		switch (flag) {
		case REFRESH_LIST:
			adapter.notifyDataSetChanged();
		default:
			break;
		}
	}

	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		// TODO Auto-generated method stub

	}

	public class BookItemAdapter extends BaseAdapter {

		public class BookModel {
			public String book_id;
			public String out_book_url;
			public String author;
			public String book_state_s;
			public String leading_role;
			public String update_time;
			public String book_name;
			public String out_book_pic;
			public String sort_id;
			public String last_update_section_title;
			public String last_update_section_url;
			public String introduction;
		}

		private LayoutInflater mInflater;
		private Vector<BookModel> mModels = new Vector<BookModel>();
		private ListView mListView;
		SyncImageLoader syncImageLoader;

		public BookItemAdapter(Context context, ListView listView) {
			mInflater = LayoutInflater.from(context);
			syncImageLoader = new SyncImageLoader();
			mListView = listView;
			
			/*
			 *
			 * 这一句话取消掉注释的话,那么能更加的节省资源,不过体验稍微有点,
			 * 你滑动的时候不会读取图片,当手放开后才开始度图片速度更快,你们可以试一试
			 * */
			
			// mListView.setOnScrollListener(onScrollListener);
		}

		public void addBook(String book_name, String author, String out_book_pic) {
			BookModel model = new BookModel();
			model.book_name = book_name;
			model.author = author;
			model.out_book_pic = out_book_pic;
			mModels.add(model);
		}

		public void clean() {
			mModels.clear();
		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return mModels.size();
		}

		@Override
		public Object getItem(int position) {
			if (position >= getCount()) {
				return null;
			}
			return mModels.get(position);
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if (convertView == null) {
				convertView = mInflater.inflate(R.layout.item_adapter,
						null);
			}
			BookModel model = mModels.get(position);
			convertView.setTag(position);
			ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
			TextView sItemTitle = (TextView) convertView
					.findViewById(R.id.sItemTitle);
			TextView sItemInfo = (TextView) convertView
					.findViewById(R.id.sItemInfo);
			sItemTitle.setText(model.book_name);
			sItemInfo.setText(model.out_book_url);
			// 添加�?��背景在滑动的时�?就会显示背景而不是其他的缓存的照片,用户体验更好
			iv.setBackgroundResource(R.drawable.rc_item_bg);
			syncImageLoader.loadImage(position, model.out_book_pic,
					imageLoadListener, model.author);
			return convertView;
		}

		SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener() {

			@Override
			public void onImageLoad(Integer t, Drawable drawable) {
				// BookModel model = (BookModel) getItem(t);
				View view = mListView.findViewWithTag(t);
				if (view != null) {
					ImageView iv = (ImageView) view
							.findViewById(R.id.sItemIcon);
					iv.setBackgroundDrawable(drawable);
				}
			}

			@Override
			public void onError(Integer t) {
				BookModel model = (BookModel) getItem(t);
				View view = mListView.findViewWithTag(model);
				if (view != null) {
					ImageView iv = (ImageView) view
							.findViewById(R.id.sItemIcon);
					iv.setBackgroundResource(R.drawable.rc_item_bg);
				}
			}

		};

		public void loadImage() {
			int start = mListView.getFirstVisiblePosition();
			int end = mListView.getLastVisiblePosition();
			if (end >= getCount()) {
				end = getCount() - 1;
			}
			syncImageLoader.setLoadLimit(start, end);
			syncImageLoader.unlock();
		}

		AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				switch (scrollState) {
				case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
					syncImageLoader.lock();
					break;
				case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
					loadImage();
					break;
				case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
					syncImageLoader.lock();
					break;

				default:
					break;
				}

			}

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO Auto-generated method stub

			}
		};
	}

}


其次是实现异步加载和缓存图片的功能代码loader.java

package com.challen;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;

import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.Handler;

public class SyncImageLoader {

	private Object lock = new Object();
	
	private boolean mAllowLoad = true;
	
	private boolean firstLoad = true;
	
	private int mStartLoadLimit = 0;
	
	private int mStopLoadLimit = 0;
	
	final Handler handler = new Handler();
	
	private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();   
	
	public interface OnImageLoadListener {
		public void onImageLoad(Integer t, Drawable drawable);
		public void onError(Integer t);
	}
	
	public void setLoadLimit(int startLoadLimit,int stopLoadLimit){
		if(startLoadLimit > stopLoadLimit){
			return;
		}
		mStartLoadLimit = startLoadLimit;
		mStopLoadLimit = stopLoadLimit;
	}
	
	public void restore(){
		mAllowLoad = true;
		firstLoad = true;
	}
		
	public void lock(){
		mAllowLoad = false;
		firstLoad = false;
	}
	
	public void unlock(){
		mAllowLoad = true;
		synchronized (lock) {
			lock.notifyAll();
		}
	}

	public void loadImage(Integer t, String imageUrl,
			OnImageLoadListener listener,String author1) {
		final OnImageLoadListener mListener = listener;
		final String mImageUrl = imageUrl;
		final Integer mt = t;
		final String author = author1;
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				if(!mAllowLoad){
					synchronized (lock) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				
				if(mAllowLoad && firstLoad){
					loadImage(mImageUrl, mt, mListener,author);
				}
				
				if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){
					loadImage(mImageUrl, mt, mListener,author);
				}
			}

		}).start();
	}
	
	private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener,final String author){
		
		if (imageCache.containsKey(mImageUrl)) {  
			System.out.println("drawable");
            SoftReference<Drawable> softReference = imageCache.get(mImageUrl);  
            final Drawable d = softReference.get();  
            if (d != null) {  
            	handler.post(new Runnable() {
    				@Override
    				public void run() {
    					if(mAllowLoad){
    						mListener.onImageLoad(mt, d);
    					}
    				}
    			});
                return;  
            }  
        }  
		try {
			final Drawable d = loadImageFromUrl(mImageUrl,author);
			if(d != null){
                imageCache.put(mImageUrl, new SoftReference<Drawable>(d));
			}
			handler.post(new Runnable() {
				@Override
				public void run() {
					if(mAllowLoad){
						mListener.onImageLoad(mt, d);
					}
				}
			});
		} catch (IOException e) {
			handler.post(new Runnable() {
				@Override
				public void run() {
					mListener.onError(mt);
				}
			});
			e.printStackTrace();
		}
	}

	public static Drawable loadImageFromUrl(String url,String author) throws IOException {
		//是否SD卡可用
		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
			//检查是或有保存图片的文件夹,没有就穿件一个
			String FileUrl = Environment.getExternalStorageDirectory()+"/TestSyncListView/";
			File folder = new File(FileUrl);
			if(!folder.exists()){
				folder.mkdir();
			}
			File f = new File(FileUrl+author+".jpg");
			//SD卡中是否有该文件,有则直接读取返回
			if(f.exists()){
				FileInputStream fis = new FileInputStream(f);
				Drawable d = Drawable.createFromStream(fis, "src");
				return d;
			}
			//没有的话则去连接下载,并写入到SD卡中
			URL m = new URL(url);
			InputStream i = (InputStream) m.getContent();
			DataInputStream in = new DataInputStream(i);
			FileOutputStream out = new FileOutputStream(f);
			byte[] buffer = new byte[1024];
			int   byteread=0;
			while ((byteread = in.read(buffer)) != -1) {
				out.write(buffer, 0, byteread);
			}
			in.close();
			out.close();
			Drawable d = Drawable.createFromStream(i, "src");
			return loadImageFromUrl(url,author);
		}
		//SD卡不可用则直接加载使用
		else{
			URL m = new URL(url);
			InputStream i = (InputStream) m.getContent();
			Drawable d = Drawable.createFromStream(i, "src");
			return d;
		}
		
	}
}


 

 

最后附上整个测试demo的下载地址,各位可以去下载,然后根据自己的需求加入到自己的项目中,希望可以帮助到大家,大家多多交流哦!

http://download.csdn.net/detail/jkingcl/4726519

 

 

posted @ 2012-11-02 17:34  雷鸣的游戏人生  阅读(231)  评论(0编辑  收藏  举报