Android開發中我們會經常遇到圖片過多或操作不當造成OOM異常,有時雖然是解決了這個問題但卻會影響程序的運行效率,例如:當用戶在快速滑動滾動條的過程中,我們程序在仍在艱難的加載服務器端的圖片,這樣給用戶造成了極不好的體驗。其實網絡上關於圖片的異步加載和緩存的講解很多,但是其實,寫一個這方面的程序還是比較麻煩的,要考慮多線程,緩存,內存溢出等很多方面,針對這一光大開發者都會遇到的問題,一些牛人們已經幫我們解決了這一問題,今天我爲大家介紹一款很流行的開源類庫,可以很很好的解決大家的煩惱!
一.介紹:
Android-Universal-Image-Loader是一個開源的UI組件程序,該項目的目的是提供一個可重複使用的儀器爲異步圖像加載,緩存和顯示。
在GitHub上面的一個開源類庫(官方下載:https://github.com/nostra13/Android-Universal-Image-Loader)
特點:
1.多線程的圖像加載;
2.圖片異步加載緩存機制,包括內存緩存(軟引用)及本地緩存;
3.動態對ImageLoader的配置(線程池的大小,HTTP選項,內存和光盤高速緩存方式,顯示圖像,以及其他選項);
4.對加載過程實現監聽和事件處理;
5.能夠配置加載圖片的顯示選項,包括圖片圓角處理和加載完成顯示動畫等;
(官方截圖)
二.使用
1.將下載下來的zip包,解壓開得到如下圖所示文件夾
2.將universal-image-loader-1.8.6-with-sources.jar導入到新建的項目中,參考sample中的例子進行使用即可。爲了讓新手們快速掌握這裏我簡單講解一下它的使用過程(使用該類庫中ImageLoader加載圖片,ListView、GridView、ViewPager)
Demo項目圖解:
2.1.在程序啓動時,用戶可以根據自己的情況初始化ImageLoaderConfiguration
public class MyApplication extends Application{ @Override public void onCreate() { super.onCreate(); initImageLoader(getApplicationContext()); } /**初始化圖片加載類配置信息**/ public static void initImageLoader(Context context) { // This configuration tuning is custom. You can tune every option, you may tune some of them, // or you can create default configuration by // ImageLoaderConfiguration.createDefault(this); // method. ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .threadPriority(Thread.NORM_PRIORITY - 2)//加載圖片的線程數 .denyCacheImageMultipleSizesInMemory() //解碼圖像的大尺寸將在內存中緩存先前解碼圖像的小尺寸。 .discCacheFileNameGenerator(new Md5FileNameGenerator())//設置磁盤緩存文件名稱 .tasksProcessingOrder(QueueProcessingType.LIFO)//設置加載顯示圖片隊列進程 .writeDebugLogs() // Remove for release app .build(); // Initialize ImageLoader with configuration. ImageLoader.getInstance().init(config); } }
別忘了在AndroidManifest.xml中
android:name=".MyApplication"
2.2.在MainActivity中我們做的僅僅是跳轉到對應的界面,下面看一下ListView中的具體使用,剩下兩個自己看一下Demo,原理一樣。
ImageListActivity:
/** * listView中使用ImageLoader * @author ZHF * */ public class ImageListActivity extends AbsListViewBaseActivity { DisplayImageOptions options; //配置圖片加載及顯示選項 String[] imageUrls; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac_image_list); //獲取url數組 Bundle bundle = getIntent().getExtras(); imageUrls = bundle.getStringArray(Extra.IMAGES); //配置圖片加載及顯示選項(還有一些其他的配置,查閱doc文檔吧) options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.ic_stub) //在ImageView加載過程中顯示圖片 .showImageForEmptyUri(R.drawable.ic_empty) //image連接地址爲空時 .showImageOnFail(R.drawable.ic_error) //image加載失敗 .cacheInMemory(true) //加載圖片時會在內存中加載緩存 .cacheOnDisc(true) //加載圖片時會在磁盤中加載緩存 .displayer(new RoundedBitmapDisplayer(20)) //設置用戶加載圖片task(這裏是圓角圖片顯示) .build(); listView = (ListView) findViewById(android.R.id.list); //綁定適配器 listView.setAdapter(new ItemAdapter()); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { startImagePagerActivity(position); } }); } @Override public void onBackPressed() { AnimateFirstDisplayListener.displayedImages.clear(); super.onBackPressed(); } private void startImagePagerActivity(int position) { Intent intent = new Intent(this, ImagePagerActivity.class); intent.putExtra(Extra.IMAGES, imageUrls); intent.putExtra(Extra.IMAGE_POSITION, position); startActivity(intent); } /**自定義圖片適配器**/ class ItemAdapter extends BaseAdapter { private ImageLoadingListener animateFirstListener = new AnimateFirstDisplayListener(); private class ViewHolder { public TextView text; public ImageView image; } @Override public int getCount() { return imageUrls.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { View view = convertView; final ViewHolder holder; if (convertView == null) { view = getLayoutInflater().inflate(R.layout.item_list_image, parent, false); holder = new ViewHolder(); holder.text = (TextView) view.findViewById(R.id.text); holder.image = (ImageView) view.findViewById(R.id.image); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } holder.text.setText("Item " + (position + 1)); //Adds display image task to execution pool. Image will be set to ImageView when it's turn. imageLoader.displayImage(imageUrls[position], holder.image, options, animateFirstListener); return view; } } /**圖片加載監聽事件**/ private static class AnimateFirstDisplayListener extends SimpleImageLoadingListener { static final List<String> displayedImages = Collections.synchronizedList(new LinkedList<String>()); @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { if (loadedImage != null) { ImageView imageView = (ImageView) view; boolean firstDisplay = !displayedImages.contains(imageUri); if (firstDisplay) { FadeInBitmapDisplayer.animate(imageView, 500); //設置image隱藏動畫500ms displayedImages.add(imageUri); //將圖片uri添加到集合中 } } } } }
說明:
1.使用ImageLoader加載圖片,只要在Adapter的getView方法中調用displayImage方法完成了異步列表圖片加載,其中options是之前定義的圖片加載和顯示選項(我們這裏使用的是RoundedBitmapDisplayer圓角圖片顯示),animateFirstListener是當圖片第一次加載的監聽事件,目的在於顯示一個淡入的顯示效果動畫,可以添加其他事件
2.進本人測試,官網例子中的Constant類中圖片的Uri在手機中鏈接很慢,完全達不到效果,之後我將其更改爲其他一系列圖片的Uri,便於觀察效果!
效果圖:
最後,大家在使用的時候記得關注一下在/sdcard/Android/data/[package_name]/cache目錄下的緩存的文件。記得定期清理緩存,否則時間一長,SD卡就會被佔滿了,同時也可以在ImageLoaderConfiguration中配置SD的緩存策略,限制緩存文件數量(memoryCacheSizePercentage),限制緩存文件最大尺寸(memoryCacheSize)等選項。
Demo源碼已上傳!