国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android照片墻應用實現 再多的圖片也不怕崩潰

2019-12-12 05:02:02
字體:
來源:轉載
供稿:網友

照片墻這種功能現在應該算是挺常見了,在很多應用中你都可以經常看到照片墻的身影。它的設計思路其實也非常簡單,用一個GridView控件當作“墻”,然后隨著GridView的滾動將一張張照片貼在“墻”上,這些照片可以是手機本地中存儲的,也可以是從網上下載的。制作類似于這種的功能的應用,有一個非常重要的問題需要考慮,就是圖片資源何時應該釋放。因為隨著GridView的滾動,加載的圖片可能會越來越多,如果沒有一種合理的機制對圖片進行釋放,那么當圖片達到一定上限時,程序就必然會崩潰。

今天我們照片墻應用的實現,重點也是放在了如何防止由于圖片過多導致程序崩潰上面。主要的核心算法使用了Android中提供的LruCache類,這個類是3.1版本中提供的,如果你是在更早的Android版本中開發,則需要導入android-support-v4的jar包。
關于LruCache用法的詳細講解,可以參考Android高效加載大圖、多圖方案,有效避免程序OOM

那我們開始動手吧,新建一個Android項目,起名叫PhotoWallDemo,這里我使用的是Android 4.0的API。

第一個要考慮的問題就是,我們從哪兒去收集這么多的圖片呢?這里我從谷歌官方提供的Demo里將圖片源取了出來,我們就從這些網址中下載圖片,代碼如下所示:

public class Images {    public final static String[] imageThumbUrls = new String[] {         "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s160-c/Valley%252520Sunset.jpg",       "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s160-c/Windmill%252520Sunrise.jpg",       "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s160-c/Windmill.jpg",       "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s160-c/Windmills.jpg",       "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s160-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg",       "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s160-c/Yosemite%252520Tree.jpg", }; } 

圖片源已經有了,現在我們就該考慮在哪里放置這些圖片了。新建或打開activity_main.xml作為程序的主布局,加入如下代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="wrap_content"   android:layout_height="wrap_content" >      <GridView      android:id="@+id/photo_wall"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:columnWidth="90dip"     android:stretchMode="columnWidth"     android:numColumns="auto_fit"     android:verticalSpacing="10dip"     android:gravity="center"     ></GridView>    </LinearLayout> 

可以看到,我們在這個布局文件中僅加入了一個GridView,這也就是我們程序中的“墻”,所有的圖片都將貼在這個“墻”上。
接著我們定義GridView中每一個子View的布局,新建一個photo_layout.xml布局,加入如下代碼:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="wrap_content"   android:layout_height="wrap_content" >    <ImageView      android:id="@+id/photo"     android:layout_width="90dip"     android:layout_height="90dip"     android:src="@drawable/empty_photo"     android:layout_centerInParent="true"     />  </RelativeLayout> 

在每一個子View中我們就簡單使用了一個ImageView來顯示一張圖片。這樣所有的布局就已經定義好了。
接下來新建PhotoWallAdapter做為GridView的適配器,代碼如下所示:

public class PhotoWallAdapter extends ArrayAdapter<String> implements OnScrollListener {    /**    * 記錄所有正在下載或等待下載的任務。    */   private Set<BitmapWorkerTask> taskCollection;    /**    * 圖片緩存技術的核心類,用于緩存所有下載好的圖片,在程序內存達到設定值時會將最少最近使用的圖片移除掉。    */   private LruCache<String, Bitmap> mMemoryCache;    /**    * GridView的實例    */   private GridView mPhotoWall;    /**    * 第一張可見圖片的下標    */   private int mFirstVisibleItem;    /**    * 一屏有多少張圖片可見    */   private int mVisibleItemCount;    /**    * 記錄是否剛打開程序,用于解決進入程序不滾動屏幕,不會下載圖片的問題。    */   private boolean isFirstEnter = true;    public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,       GridView photoWall) {     super(context, textViewResourceId, objects);     mPhotoWall = photoWall;     taskCollection = new HashSet<BitmapWorkerTask>();     // 獲取應用程序最大可用內存     int maxMemory = (int) Runtime.getRuntime().maxMemory();     int cacheSize = maxMemory / 8;     // 設置圖片緩存大小為程序最大可用內存的1/8     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {       @Override       protected int sizeOf(String key, Bitmap bitmap) {         return bitmap.getByteCount();       }     };     mPhotoWall.setOnScrollListener(this);   }    @Override   public View getView(int position, View convertView, ViewGroup parent) {     final String url = getItem(position);     View view;     if (convertView == null) {       view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);     } else {       view = convertView;     }     final ImageView photo = (ImageView) view.findViewById(R.id.photo);     // 給ImageView設置一個Tag,保證異步加載圖片時不會亂序     photo.setTag(url);     setImageView(url, photo);     return view;   }    /**    * 給ImageView設置圖片。首先從LruCache中取出圖片的緩存,設置到ImageView上。如果LruCache中沒有該圖片的緩存,    * 就給ImageView設置一張默認圖片。    *    * @param imageUrl    *      圖片的URL地址,用于作為LruCache的鍵。    * @param imageView    *      用于顯示圖片的控件。    */   private void setImageView(String imageUrl, ImageView imageView) {     Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);     if (bitmap != null) {       imageView.setImageBitmap(bitmap);     } else {       imageView.setImageResource(R.drawable.empty_photo);     }   }    /**    * 將一張圖片存儲到LruCache中。    *    * @param key    *      LruCache的鍵,這里傳入圖片的URL地址。    * @param bitmap    *      LruCache的鍵,這里傳入從網絡上下載的Bitmap對象。    */   public void addBitmapToMemoryCache(String key, Bitmap bitmap) {     if (getBitmapFromMemoryCache(key) == null) {       mMemoryCache.put(key, bitmap);     }   }    /**    * 從LruCache中獲取一張圖片,如果不存在就返回null。    *    * @param key    *      LruCache的鍵,這里傳入圖片的URL地址。    * @return 對應傳入鍵的Bitmap對象,或者null。    */   public Bitmap getBitmapFromMemoryCache(String key) {     return mMemoryCache.get(key);   }    @Override   public void onScrollStateChanged(AbsListView view, int scrollState) {     // 僅當GridView靜止時才去下載圖片,GridView滑動時取消所有正在下載的任務     if (scrollState == SCROLL_STATE_IDLE) {       loadBitmaps(mFirstVisibleItem, mVisibleItemCount);     } else {       cancelAllTasks();     }   }    @Override   public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,       int totalItemCount) {     mFirstVisibleItem = firstVisibleItem;     mVisibleItemCount = visibleItemCount;     // 下載的任務應該由onScrollStateChanged里調用,但首次進入程序時onScrollStateChanged并不會調用,     // 因此在這里為首次進入程序開啟下載任務。     if (isFirstEnter && visibleItemCount > 0) {       loadBitmaps(firstVisibleItem, visibleItemCount);       isFirstEnter = false;     }   }    /**    * 加載Bitmap對象。此方法會在LruCache中檢查所有屏幕中可見的ImageView的Bitmap對象,    * 如果發現任何一個ImageView的Bitmap對象不在緩存中,就會開啟異步線程去下載圖片。    *    * @param firstVisibleItem    *      第一個可見的ImageView的下標    * @param visibleItemCount    *      屏幕中總共可見的元素數    */   private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {     try {       for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {         String imageUrl = Images.imageThumbUrls[i];         Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);         if (bitmap == null) {           BitmapWorkerTask task = new BitmapWorkerTask();           taskCollection.add(task);           task.execute(imageUrl);         } else {           ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);           if (imageView != null && bitmap != null) {             imageView.setImageBitmap(bitmap);           }         }       }     } catch (Exception e) {       e.printStackTrace();     }   }    /**    * 取消所有正在下載或等待下載的任務。    */   public void cancelAllTasks() {     if (taskCollection != null) {       for (BitmapWorkerTask task : taskCollection) {         task.cancel(false);       }     }   }    /**    * 異步下載圖片的任務。    *    * @author guolin    */   class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {      /**      * 圖片的URL地址      */     private String imageUrl;      @Override     protected Bitmap doInBackground(String... params) {       imageUrl = params[0];       // 在后臺開始下載圖片       Bitmap bitmap = downloadBitmap(params[0]);       if (bitmap != null) {         // 圖片下載完成后緩存到LrcCache中         addBitmapToMemoryCache(params[0], bitmap);       }       return bitmap;     }      @Override     protected void onPostExecute(Bitmap bitmap) {       super.onPostExecute(bitmap);       // 根據Tag找到相應的ImageView控件,將下載好的圖片顯示出來。       ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);       if (imageView != null && bitmap != null) {         imageView.setImageBitmap(bitmap);       }       taskCollection.remove(this);     }      /**      * 建立HTTP請求,并獲取Bitmap對象。      *      * @param imageUrl      *      圖片的URL地址      * @return 解析后的Bitmap對象      */     private Bitmap downloadBitmap(String imageUrl) {       Bitmap bitmap = null;       HttpURLConnection con = null;       try {         URL url = new URL(imageUrl);         con = (HttpURLConnection) url.openConnection();         con.setConnectTimeout(5 * 1000);         con.setReadTimeout(10 * 1000);         bitmap = BitmapFactory.decodeStream(con.getInputStream());       } catch (Exception e) {         e.printStackTrace();       } finally {         if (con != null) {           con.disconnect();         }       }       return bitmap;     }    }  } 

PhotoWallAdapter是整個照片墻程序中最關鍵的一個類了,這里我來重點給大家講解一下。首先在PhotoWallAdapter的構造函數中,我們初始化了LruCache類,并設置了最大緩存容量為程序最大可用內存的1/8,接下來又為GridView注冊了一個滾動監聽器。然后在getView()方法中,我們為每個ImageView設置了一個唯一的Tag,這個Tag的作用是為了后面能夠準確地找回這個ImageView,不然異步加載圖片會出現亂序的情況。之后調用了setImageView()方法為ImageView設置一張圖片,這個方法首先會從LruCache緩存中查找是否已經緩存了這張圖片,如果成功找到則將緩存中的圖片顯示在ImageView上,否則就顯示一張默認的空圖片。

看了半天,那到底是在哪里下載圖片的呢?這是在GridView的滾動監聽器中進行的,在onScrollStateChanged()方法中,我們對GridView的滾動狀態進行了判斷,如果當前GridView是靜止的,則調用loadBitmaps()方法去下載圖片,如果GridView正在滾動,則取消掉所有下載任務,這樣可以保證GridView滾動的流暢性。在loadBitmaps()方法中,我們為屏幕上所有可見的GridView子元素開啟了一個線程去執行下載任務,下載成功后將圖片存儲到LruCache當中,然后通過Tag找到相應的ImageView控件,把下載好的圖片顯示出來。

由于我們使用了LruCache來緩存圖片,所以不需要擔心內存溢出的情況,當LruCache中存儲圖片的總大小達到容量上限的時候,會自動把最近最少使用的圖片從緩存中移除。
最后新建或打開MainActivity作為程序的主Activity,代碼如下所示:

public class MainActivity extends Activity {    /**    * 用于展示照片墻的GridView    */   private GridView mPhotoWall;    /**    * GridView的適配器    */   private PhotoWallAdapter adapter;    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     mPhotoWall = (GridView) findViewById(R.id.photo_wall);     adapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls, mPhotoWall);     mPhotoWall.setAdapter(adapter);   }    @Override   protected void onDestroy() {     super.onDestroy();     // 退出程序時結束所有的下載任務     adapter.cancelAllTasks();   }  } 

MainActivity中的代碼非常簡單,沒什么需要說明的了,在Activity被銷毀時取消掉了所有的下載任務,避免程序在后臺耗費流量。另外由于我們使用了網絡功能,別忘了在AndroidManifest.xml中加入網絡權限的聲明。
現在可以運行一下程序了,效果如下圖所示: 

                                        

可以看到,滾動照片墻,會異步加載圖片到相應的ImageView上。隨著加載圖片的增多,會釋放掉一些之前加載過的圖片,你多滾動幾次就可以看得出了。另外為了能讓大家明顯看出圖片的釋放情況,我在這個程序中沒有使用本地緩存,所有被釋放掉的圖片再次顯示需要從網絡上再下載一遍。在實際的項目中配合適當的本地緩存效果會更好。
打開DDMS,我們可以發現,由于有LruCache幫我們管理圖片緩存,不管如何滾動照片墻,程序內存始終會保持在一個合理的范圍內。

本篇文章的重點在于如何對圖片進行更好的回收,因此照片墻只是簡單地使用GridView進行了展示,想要看更酷更炫的照片墻效果的朋友,可以參考我后面的一篇文章 Android瀑布流照片墻實現,體驗不規則排列的美感

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 杨浦区| 新建县| 甘孜县| 普定县| 永靖县| 揭西县| 东至县| 拉孜县| 遂昌县| 外汇| 彭阳县| 玉环县| 德昌县| 安西县| 佛冈县| 康定县| 肥城市| 淮阳县| 襄樊市| 抚松县| 望谟县| 潞西市| 腾冲县| 桂阳县| 西藏| 深圳市| 新沂市| 六枝特区| 山东省| 新疆| 邯郸市| 肇东市| 柳州市| 邹城市| 会昌县| 焉耆| 贺兰县| 五台县| 松溪县| 霍州市| 洛宁县|