1. 程式人生 > >Android開發之listview優化+圖片非同步載入+避免圖片顯示閃爍(修改版)

Android開發之listview優化+圖片非同步載入+避免圖片顯示閃爍(修改版)

小鹿路過此地。。心熱之下寫寫Android開發中的listview運用(閒的沒事幹,改動了一點程式碼,但實現功能不變)

小鹿學Android開發已有兩年多了,總算有一點點小收穫,雖算不上大牛大神級人物,但有些東西可以和一家一起分享一起交流一起學習一起進步...。還有,望檢視此文章的廢話不多說,直接主題

(注:此內容適合有點Android開發基礎的)

學習過Android開發的人都知道listview這個控制元件了吧,意思就是說listview對我們這些Android開發人員來說並不陌生了。Android開發中使用listview說簡單並不簡單,說難也不是難,總之...看你怎麼開發和使用它了

此內容講解listview的優化+圖片非同步載入+避免圖片顯示閃爍等問題,由於小鹿第一次寫這個部落格,文章格式不懂怎麼排布,亂點請不要噴,僅供大家一起交流學習。

說明實現原理:

大家學過listview圖片非同步載入等知識都知道,網上有很多很好的部落格文章,實現原理大家也知道得差不多了

小鹿的這篇部落格文章的listview也跟網上那些大同小異。知識上的一些細節有稍微的改動。

1、getview中的view重用,用使用viewholder。

2、listview中圖片顯示為當前介面圖片顯示。

3、滑動時停止載入,停止滑動時載入圖片顯示與listview介面。

4、給圖片設定標籤,判斷並實現避免閃爍功能

5、非同步載入過程就不用多說了....

看程式碼:(建議大家拷貝到專案中,這樣會好看得多····)

顯示資訊原始碼:FerrariMessage.java

public class FerrariMessage {
	public static String[] Names = { "法拉利1", "法拉利2", "法拉利3", "法拉利4", "法拉利5",
			"法拉利6", "法拉利7", "法拉利8", "法拉利9", "法拉利10", "法拉利11", "法拉利12", "法拉利13",
			"法拉利14", "法拉利15", "法拉利16", "法拉利17", "法拉利18", "法拉利19", "法拉利20"};
	public static String[] Pics = { "901萬", "902萬", "903萬", "904萬", "905萬",
			"906萬", "907萬", "908萬", "909萬", "910萬", "911萬", "912萬", "913萬",
			"914萬", "915萬", "916萬", "917萬", "918萬", "919萬", "920萬" };
	public static String[] Urls = {
			"http://pic7.nipic.com/20100518/3409334_031036043513_2.jpg",
			"http://pic7.nipic.com/20100521/1383578_011117510279_2.jpg",
			"http://pic7.nipic.com/20100515/1383578_012657947151_2.jpg",
			"http://xm.05927.com/UploadFiles/pic_2008121110263688.jpg",
			"http://pic5.nipic.com/20100129/1295091_171443267500_2.jpg",
			"http://pic4.nipic.com/20090925/3054494_105159945284_2.jpg",
			"http://s9.knowsky.com/bizhi/l/35001-45000/200952904410415637552.jpg",
			"http://picm.bbzhi.com/qichebizhi/gaoqingqichebizhixinshang/gaoqingqichebizhixinshang_423950_m.jpg",
			"http://pic7.nipic.com/20100524/1383578_033343366370_2.jpg",
			"http://www.lyt.com.cn/lytongger/uploads/201104/1302940767MbGrMW2F.jpg",
			"http://img1.100ye.com/img1/4/996/468/10494468/msgpic/51043248.jpg",
			"http://pic3.nipic.com/20090709/2082016_175148046_2.jpg",
			"http://file-ps.sioe.cn/201006/5/B6132219150.jpg",
			"http://home.tongyi.com/attachment/201001/23/332350_1264213529hJj2.jpg",
			"http://f.hiphotos.bdimg.com/album/w%3D2048/sign=4e37a00c14ce36d3a20484300ecb3887/3801213fb80e7bec41655f372e2eb9389a506bac.jpg",
			"http://gi1.md.alicdn.com/bao/uploaded/i1/TB1jFUUFVXXXXaRXXXXXXXXXXXX_!!0-item_pic.jpg_430x430q90.jpg",
			"http://gd4.alicdn.com/bao/uploaded/i4/TB13YjTFVXXXXXVaXXXXXXXXXXX_!!0-item_pic.jpg_400x400.jpg",
			"http://gd3.alicdn.com/bao/uploaded/i3/TB1iI9pGpXXXXXwXXXXXXXXXXXX_!!0-item_pic.jpg_400x400.jpg",
			"http://gd3.alicdn.com/bao/uploaded/i3/TB1UqnNGXXXXXaFXFXXXXXXXXXX_!!0-item_pic.jpg_400x400.jpg",
			"http://gd3.alicdn.com/bao/uploaded/i3/TB1lNP7FVXXXXXnXXXXXXXXXXXX_!!0-item_pic.jpg_400x400.jpg" };
}

主介面原始碼:MainActivity.java

public class MainActivity extends Activity {

	private ListView mListView = null;
	private List<HashMap<String, Object>> mList = new ArrayList<HashMap<String, Object>>();
	private ViewHolder_ListViewAdapter adapter = null;
	private HashMap<String, Object> hashMap=null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		initFile();
		setContentView(R.layout.activity_main);
		mListView = (ListView) findViewById(R.id.mListView);
		adapter = new ViewHolder_ListViewAdapter(mList, mListView,
				getApplicationContext());
		mListView.setAdapter(adapter);
		 mList.clear();
			for (int i = 0; i < FerrariMessage.Urls.length; i++) {
				hashMap = new HashMap<String, Object>();
				hashMap.put("name", FerrariMessage.Names[i]);
				hashMap.put("pic",FerrariMessage.Pics[i]);
				hashMap.put("url", FerrariMessage.Urls[i]);
				mList.add(hashMap);
			}
			adapter.notifyDataSetChanged();
	}

	private void initFile() {
		File file2 = new File(Environment.getExternalStorageDirectory()
				+ "/test/");
		if (!file2.exists()) {
			file2.mkdirs();
		}
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		File file = new File(Environment.getExternalStorageDirectory()
				+ "/test");
		deleteFile(file);
	}

	// 將SD卡檔案刪除
	private void deleteFile(File file) {
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			if (file.exists()) {
				if (file.isFile()) {
					file.delete();
				}
				// 如果它是一個目錄
				else if (file.isDirectory()) {
					// 宣告目錄下所有的檔案 files[];
					File files[] = file.listFiles();
					for (int i = 0; i < files.length; i++) { // 遍歷目錄下所有的檔案
						deleteFile(files[i]); // 把每個檔案 用這個方法進行迭代
					}
				}
				file.delete();
			}
		}
	}

}

介面卡:ViewHolder_ListViewAdapter.java

public class ViewHolder_ListViewAdapter extends BaseAdapter {
	private List<HashMap<String, Object>> mList;
	private LayoutInflater inflater = null;
	private ImageRegister imageRegister=null;
	private ListView mListView=null;

	public ViewHolder_ListViewAdapter(List<HashMap<String, Object>> list,
			ListView listView, Context context) {
		this.mList = list;
		this.imageRegister = new ImageRegister();
		this.mListView = listView;
		this.mListView.setOnScrollListener(onScrollListener);
		inflater = LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		return mList.size();
	}

	@Override
	public Object getItem(int position) {
		return mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(final int position, View v, ViewGroup parent) {
		ViewHolder vh = null;
		if (v == null) {
			vh = new ViewHolder();
			v = inflater.inflate(R.layout.item_listview, null);
			vh.title1 = (TextView) v.findViewById(R.id.title1);
			vh.title2 = (TextView) v.findViewById(R.id.title2);
			vh.imageView = (ImageView) v.findViewById(R.id.imageView);
			v.setTag(vh);
		} else {
			vh = (ViewHolder) v.getTag();
		}
		mListView.setTag(mList.get(position).get("url"));
		vh.title1.setText(mList.get(position).get("name").toString());
		vh.title2.setText(mList.get(position).get("pic").toString());
		vh.imageView.setBackgroundResource(R.drawable.ic_launcher);
		vh.imageView.setTag(mList.get(position).get("url").toString());
		imageRegister.loadImage(position, vh.imageView, mList.get(position)
				.get("url").toString(), "test");
		return v;
	}

	private class ViewHolder {
		TextView title1;
		TextView title2;
		ImageView imageView;
	}

	AbsListView.OnScrollListener onScrollListener = new OnScrollListener() {

		@Override
		public void onScrollStateChanged(AbsListView view, int scrollState) {
			// TODO Auto-generated method stub
			switch (scrollState) {
			case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
				imageRegister.Lock();
				break;
			case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
				loadImage();
				break;
			case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
				imageRegister.Lock();
				break;
			}
		}

		@Override
		public void onScroll(AbsListView view, int firstVisibleItem,
				int visibleItemCount, int totalItemCount) {
		}
	};

	protected void loadImage() {
		// TODO Auto-generated method stub
		int start = mListView.getFirstVisiblePosition();
		int end = mListView.getLastVisiblePosition();
		if (end >= getCount()) {
			end = getCount() - 1;
		}
		imageRegister.setLoadLimit(start, end);
		imageRegister.unLock();
	}
}

非同步載入類:ImageRegister.java

public class ImageRegister {

	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 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(final Integer position, final ImageView imageView,
			final String imageUrl, final String fileName) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				if (!mAllowLoad) {
					synchronized (lock) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}

				if (mAllowLoad && firstLoad) {
					loadImage2(position,imageUrl, imageView, fileName);
				}
				if (mAllowLoad && position<= mStopLoadLimit && position >= mStartLoadLimit) {
					loadImage2(position,imageUrl, imageView, fileName);
				}
			}
		}).start();
	}

	private void loadImage2(final Integer position,final String mImageUrl, final ImageView imageView,
			final String fileName) {
		if (imageCache.containsKey(mImageUrl)) {
			SoftReference<Drawable> softReference = imageCache.get(mImageUrl);
			final Drawable d = softReference.get();
			if (d != null) {
				handler.post(new Runnable() {
					@SuppressLint("NewApi") @Override
					public void run() {
						if (mAllowLoad) {
							if (imageView.getTag().equals(mImageUrl)) {
								imageView.setBackground(d);
							}
						}
					}
				});
				return;
			}
		}
		try {
			final Drawable d = loadImageFromUrl(position,imageView,mImageUrl, fileName);
			if (d != null) {
				imageCache.put(mImageUrl, new SoftReference<Drawable>(d));
				handler.post(new Runnable() {
					@SuppressLint("NewApi") @Override
					public void run() {
						if (mAllowLoad) {
							if (imageView.getTag().equals(mImageUrl)) {
								imageView.setBackground(d);
							}
						}
					}
				});
			}
		} catch (IOException e) {
			handler.post(new Runnable() {
				@Override
				public void run() {
					imageView.setBackgroundResource(R.drawable.ic_launcher);
				}
			});
			e.printStackTrace();
		}
	}

	@SuppressLint("NewApi") 
	public Drawable loadImageFromUrl(Integer position,ImageView imageView,String url, String fileName)
			throws IOException {
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			String imageName = url.substring(url.lastIndexOf("/"));
			String pathName = (Environment.getExternalStorageDirectory() + "/"
					+ fileName + imageName);
			File f = new File(pathName);
			if (f.exists()) {
				Bitmap bitmap = compressImageFromFile(pathName);
				@SuppressWarnings("deprecation")
				BitmapDrawable d = new BitmapDrawable(bitmap);
				System.out.println("載入了第:"+position);
				return d;
			}else{
			System.out.println("下載了第:"+position);
			URL m = new URL(url);
			InputStream inputStream = (InputStream) m.getContent();
			DataInputStream in = new DataInputStream(inputStream);
			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();
			return loadImageFromUrl(position,imageView,url, fileName);
			}
		} else {
			URL m = new URL(url);
			InputStream inputStream = (InputStream) m.getContent();
			Drawable drawable = Drawable.createFromStream(inputStream, null);
			inputStream.close();
			return drawable;
		}
		    
	}

	private Bitmap compressImageFromFile(String srcPath) {
		BitmapFactory.Options newOpts = new BitmapFactory.Options();
		newOpts.inJustDecodeBounds = true;
		Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
		newOpts.inJustDecodeBounds = false;
		int w = newOpts.outWidth;
		int h = newOpts.outHeight;
		float hh = 100f;
		float ww = 120f;
		int be = 1;
		if (w > h && w > ww) {
			be = (int) (newOpts.outWidth / ww);
		} else if (w < h && h > hh) {
			be = (int) (newOpts.outHeight / hh);
		}
		if (be <= 0)
			be = 1;
		newOpts.inSampleSize = be;
		newOpts.inPreferredConfig = Config.ARGB_8888;
		newOpts.inPurgeable = true;
		newOpts.inInputShareable = true;
		bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
		return bitmap;
	}

}

mainactivity介面的佈局檔案activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
     <ListView
        android:id="@+id/mListView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:cacheColorHint="#00000000"
        android:dividerHeight="1.0dip"
        android:listSelector="#00000000" />
</LinearLayout>

listview條目的佈局檔案item_listview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:orientation="horizontal" >
     
    <ImageView 
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_weight="1.0"
        />
    
    <TextView
        android:id="@+id/title1"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1.0"
        android:gravity="center"
        android:text="" />

    <TextView
        android:id="@+id/title2"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1.0"
        android:gravity="center"
        android:text="" />

    <TextView
        android:id="@+id/title3"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1.0"
        android:gravity="center"
        android:text="" />


    <TextView
        android:id="@+id/title4"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1.0"
        android:gravity="center"
        android:text="" />

</LinearLayout>

最後,在androidmanifest.xml中新增許可權

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RESTART_PACKAGES" />

點評:此專案又優點也有缺點,我直接說缺點好了:①當網速不好的時候,下載圖片速度慢,顯示在listview介面有時圖片不全(暫時沒有解決方案,希望大家一起交流)。②介面太簡單(這靠大家設計UI了,小鹿能力有限)。

程式碼已奉上完了,小鹿第一次用部落格寫文章,本來想給大家發上demo,可小鹿不懂怎麼發,時間也不多了,希望大家檢視此文章後能一起交流技術,此文章有不足之處請大家點評。。。。小鹿要睡覺了。。。希望能和大家一起交流。。。午安