Android實作類別似ListView模式的回收和更新機制的瀑布流

來源:互聯網
上載者:User

Android實作類別似ListView模式的回收和更新機制的瀑布流

部落格地址:http://blog.csdn.net/u010593680/article/details/43771857(轉載請保留原文地址)

項目地址:https://git.oschina.net/0-0Xuan/XWaterFall

分析問題:

在做項目中遇到了需要使用瀑布流的情況,於是便和往常一樣使用ScroollView模式的瀑布流,但是瀑布流效果容易實現,可一旦載入大量圖片,則一不了心就記憶體溢出了,而瀑布流往往需要添加大量的圖片,記憶體管理可以說是必要之舉,那麼問題就來了,如何在瀑布流中進行記憶體管理呢?

解決方案一: 使用各種ImageLoader。將圖片的Bitmap全都在ImageLoader中進行統計管理,如果圖片使用記憶體超過限制了,則釋放部分圖片的Bitmap,但這裡就有個顯而易見的問題,即釋放哪個圖片的bitmap?這往往是最困難的一步,很多模式採取釋放最近最少使用的Bitmap,但統計bitmap的使用本身就是一件比較難的事情,所以ImageLoader主要是用於快速高效的載入圖片

解決方案二:使用自訂View,當View出現在手機螢幕上時,WindowsManager會調用View的onDraw方法來描繪View,這樣即可即時的確定哪個View的圖片需要使用,再載入對應的圖片進記憶體,這樣就可最大限度減少記憶體的使用,這個辦法幾乎能完美解決記憶體溢出的問題,但是,使用該方法需要在onDraw()方法調用時,再載入圖片,那麼載入圖片的延遲將使使用者體驗大打折扣,當然也可以在代碼中添加一些聯絡的代碼,當某個View顯示時,一併載入該View附近的View的圖片,但如此一來各個View的聯絡就十分密切,代碼編寫難度會提高不少。

解決方案三:類比ListView使得該瀑布流可以回收不顯示的View。知道了哪些view沒有被顯示,那麼只要回收這些View的圖片的bitmap即可。

本部落客要介紹第三種解決方案!

實現目標:一、使waterfall能夠發現正在螢幕中顯示的View,並且能夠設定滑動時,載入View的正向和反向需要緩衝的View的個數,以提前載入View,使使用者體驗更好

實現目標二、使waterfall能實作類別似於ListView的notifyDataSetChange()方法,使得調用notifyDataSetChange()後ListView將檢查原來的View是否有更新,如果有更新則進行相應的更新。這樣就能方便的修改、增加、刪除waterfall中的View了。


XWaterFall的使用:

第一步繼承XWaterFall,繼承後會顯示7個必需實現的方法:

/** * notifyDataSetChange時調用來擷取子View,不建議在此進行大量的記憶體操作,片bitmap的載入,應將該操作放置在onResumeView(int position, View view)中 *  * @param position * @return 返回需要添加的View */@Overridepublic View getView(int position) {// TODO Auto-generated method stubreturn null;}/** * 如果能確定對應的View的高度則可在子類覆蓋該方法 *  * @param position * @return */@Overridepublic int getHeight(int position) {// TODO Auto-generated method stubreturn 0;}/** * 返回position對應的View的標識,用以判斷View是否需要更新 *  * @param position * @return */@Overridepublic Object getMark(int position) {// TODO Auto-generated method stubreturn null;}/** *  * @return WaterFall中子View的數量 */@Overridepublic int getContentChildCount() {// TODO Auto-generated method stubreturn 0;}/** *  * @return 一次notifyDataSetChanged()最多添加的View的個數 */@Overridepublic int getNewChildNum() {// TODO Auto-generated method stubreturn 0;}/** * 有新超出顯示屏的View時調用該方法 *  * @param position * @param view */@Overrideprotected void onRecycleView(int position, View view) {// TODO Auto-generated method stub}/** * 有新進入顯示屏的View時調用該方法 *  * @param position * @param view */@Overrideprotected void onResumeView(int position, View view) {// TODO Auto-generated method stub}
瞭解了方法的實現要求,我們現在來實現一個顯示美女和美女position的瀑布流,實現效果如下:

實現的代碼是:

public class ImageWaterFall extends XWaterFall {private String TAG = "ImageWaterFall";private Context context;private List imgs;private LayoutInflater inflater;public ImageWaterFall(Context context, List imgs) {super(context,3,0);//設定waterfall的context、列數、寬度// TODO Auto-generated constructor stubthis.context = context;this.inflater = LayoutInflater.from(context);this.imgs = imgs;}@Overridepublic View getView(int position) {// TODO Auto-generated method stubView view = inflater.inflate(R.layout.item_my_image, null);MyImageView iv = (MyImageView) view.findViewById(R.id.iv);iv.setName("美女"+position);TextView tv = (TextView) view.findViewById(R.id.tvName);tv.setText("美女: " + position);return view;}@Overridepublic int getContentChildCount() {// TODO Auto-generated method stubreturn imgs.size();}@Overridepublic int getNewChildNum() {// TODO Auto-generated method stubreturn 12;}@Overridepublic void onScrollToBottom() {// TODO Auto-generated method stubnotifyDataSetChanged();}@Overridepublic Object getMark(int position) {// TODO Auto-generated method stubreturn imgs.get(position);}@Overrideprotected void onRecycleView(int position, View view) {// TODO Auto-generated method stubMyImageView iv = (MyImageView) view.findViewById(R.id.iv);TextView tv = (TextView)view.findViewById(R.id.tvName);tv.setBackgroundColor(context.getResources().getColor(R.color.orange));String url = imgs.get(position);Bitmap bm = ImageLoader.getBitmap(url);if (bm != null && !bm.isRecycled())bm.recycle();iv.setImageBitmap(null);ImageLoader.deleteBitmap(url);android.view.ViewGroup.LayoutParams params = iv.getLayoutParams();params.height = iv.getHeight();params.width = iv.getWidth();iv.setLayoutParams(params);iv.recycle();}@Overrideprotected void onResumeView(int position, View view) {// TODO Auto-generated method stubTextView tv = (TextView)view.findViewById(R.id.tvName);tv.setBackgroundColor(context.getResources().getColor(R.color.green));MyImageView iv = (MyImageView) view.findViewById(R.id.iv);ImageLoader.loadImg(imgs.get(position), iv);iv.resume();}@Overridepublic int getChildHeight(int position) {// TODO Auto-generated method stubreturn 0;}}


需要注意的是:

一、getChildHeight()中返回的是子View的高度,如果不能事Crowdsourced Security Testing道View的高度,則可以返回0或負數,那麼會自動使用預設的選擇最短列的演算法

二、當發生View的更新和添加等操作,將會調用getView方法,但可能該View沒在顯示屏和緩衝區內,在這裡耗費大量記憶體顯然不合適,而當View第一次出現在顯示屏和緩衝區中時,將會調用onResumeView()方法,所以onResumeView()更適合作為載入圖片或其他非常耗費記憶體方法調用的地方

三、保證某個View的onResumeView()方法會在getView()之後,並且只有當View第一次出現在顯示屏和緩衝區中時才會被調用

當某個View第一次離開顯示屏和緩衝區中時,onRecycleView()會被調用

onResumeView()和onRecycleView()會被交替調用,並且保證先調用onResumeView(),才有可能調用onRecycleView()


在ImageWaterFall代碼中也進行第三點的測試:當onResumeView()和onRecycleView()被調用時,會分別調用MyImageView的resume()和recycle()

public class MyImageView extends ImageView {private String TAG = "MyImageView";private String name;private int state; //用來檢驗onResumeView和onRecycleView交替執行,且先onResumeView再有onRecycleViewpublic MyImageView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub this.setOnClickListener(onclick);//XWaterFall不會影響上層View的OnClickListener等操作}public void setName(String name) {this.name = name;}@Overridepublic void setImageBitmap(Bitmap bm) {// TODO Auto-generated method stub// Log.v(name, "setImageBitmap");super.setImageBitmap(bm);}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stub// Log.v(name, "onDraw :" + drawNum++ );try {super.onDraw(canvas);} catch (Exception e) {e.printStackTrace();Log.v(TAG, name);}}public void recycle(){Log.v(TAG, "XX回收" + name);state--;if(state!= 0){Log.v(TAG, "XX錯誤: state:" + state);}}public void resume(){Log.v(TAG, "XX恢複" + name);state++;if(state != 1){Log.v(TAG, "XX錯誤: state:" + state);}}OnClickListener onclick = new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubLog.v(TAG, "OnClickListener");}};}


XWaterFall的實現弄好了,接下來就是具體的使用了:

第一步:建立:ImageWaterFall iwf = new ImageWaterFall(this, imgs);

第二步:添加到某容器裡:llContent.addView(iwf);

第三步:更新ImageWaterfall中的內容:iwf.notifyDataSetChanged();

前面兩步和普通的View的使用類似,第三步則是添加ImageWaterfall子View的操作

還可以設定正反方向上緩衝的數量,設定較大的緩衝數量有助於減少使用者等待查看的時間,改善使用者體驗

(手指向上滑,則準備顯示的是下方的View,所以下方為正方向,上方為反方向,手指向下滑,則準備顯示的是上方的View,所以上方為正方向,下方為反方向)

iwf.setPositionCacheNum(18);//設定載入View時正向的緩衝的View的數量iwf.setOppositeCacheNum(18);//設定載入View時反向的緩衝的View的數量

接下來介紹如何更新、刪除、插入View到ImageWaterFall中:

switch(view.getId()){case R.id.btRefrash://更新position為1的Viewimgs.set(1, "http://g.hiphotos.baidu.com/image/pic/item/1ad5ad6eddc451da9f2e8e8cb5fd5266d11632f8.jpg");iwf.notifyDataSetChanged();break;case R.id.btDelete://刪除position為1的Viewimgs.remove(1);iwf.notifyDataSetChanged();break;case R.id.btInseart://在position為1的View前插入新Viewimgs.add(1, "http://h.hiphotos.baidu.com/image/pic/item/810a19d8bc3eb1350c58efbca41ea8d3fd1f441d.jpg");iwf.notifyDataSetChanged();break;}
注意上面的imgs對應的是ImageWaterFall中的private List imgs;這個用法和ListView中的用法非常相似

XWaterFall的使用就介紹完畢了,源碼在部落格頂部,如有任何問題,歡迎反饋!!




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.