Android複雜數據流的「高效」渲染(二)

上篇Android ListView中複雜數據流的高效渲染文章中介紹如何高效利用ListView的緩存進行渲染。以後有挺多同窗有些疑惑,但願能夠有一個demo,因而利用業餘時間把demo櫓出來了,若是有什麼問題你們能夠評論或者在ComplexDataStream issue中提issue。這裏貼一下demo的地址:ComplexDataStream。另我的以爲這個思路實際增長了代碼複雜度,在邏輯上並不高效,因而在題目中加了引號,可是實測一個複雜列表中能夠節約10-20m的內存,這一點是頗有誘惑力的。下面結合代碼介紹一下。javascript

代碼結構

Paste_Image.png

  • Model
    model中是全部的原始數據類,這裏爲了方便,每種數據的名字實際包含了須要展現的模型:如CardWithTitleItem數據實際須要展現一個標題和一個卡片,HeaderImageCardItem須要展現一個頭部、圖片、卡片。

Paste_Image.png

Paste_Image.png

  • Adapter
    Adapter中放置了adapter和各類類型的holder,這裏把多種數據類型拆分紅了card、divider、header、image、link、text、title,併爲每種類型設置了相應的佈局。java

  • Util
    Util中設置如何transform數據到相應的展現模版,以及解決按壓效果的問題。git

代碼思路

咱們的目的是將複雜的數據類型進行拆分,從而達到細顆粒的view複用,下降內存佔用。github

  1. 肯定拆分後的展現類型,這裏使用了一個enum類型:
    public enum ItemType {
     TITLE,
     CARD,
     HEADER,
     IMAGE,
     TEXT,
     LINK,
     DIVIDER
    }複製代碼
  2. transform數據到模板,拆分後一個數據類型對應多個模板,這裏咱們使用hashmap創建數據到模板的影射關係:
    private static Map<Class, List<ItemType>> map = new HashMap<>();複製代碼
    public static List<ItemWrap> getTransformedItem(List<BaseItem> baseItems) {
         List<ItemWrap> itemWraps = new ArrayList<>();
         for (BaseItem baseItem : baseItems) {
             for (ItemType itemType : map.get(baseItem.getClass())) {
                 ItemWrap temp = new ItemWrap(baseItem, itemType);
                 itemWraps.add(temp);
                 baseItem.itemWraps.add(temp);
             }
             ItemWrap divider = new ItemWrap(baseItem, ItemType.DIVIDER);
             itemWraps.add(divider);
             baseItem.itemWraps.add(divider);
         }
         return itemWraps;
     }複製代碼
    getTransformedItem方法將原始數據進行拆分,注意每種原始數據類型中都要加入divider模板,用於展現ListView的分割線。
  3. 根據不一樣的展現類型提供不一樣的view:
    public static View createItemView(ItemType itemType) {
         View view = null;
         BaseHolder baseHolder = null;
         switch (itemType) {
             case TITLE:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.title_item, null);
                 baseHolder = new TitleHolder();
                 break;
             case CARD:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.card_item, null);
                 baseHolder = new CardHolder();
                 break;
             case TEXT:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.text_item, null);
                 baseHolder = new TextHolder();
                 break;
             case IMAGE:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.image_item, null);
                 baseHolder = new ImageHolder();
                 break;
             case LINK:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.link_item, null);
                 baseHolder = new LinkHolder();
                 break;
             case HEADER:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.header_item, null);
                 baseHolder = new HeaderHolder();
                 break;
             case DIVIDER:
                 view = LayoutInflater.from(_Application.applicationContext).inflate(R.layout.divider_item, null);
                 baseHolder = new DividerHolder();
                 break;
         }
         baseHolder.setup(view);
         view.setTag(baseHolder);
         return view;
     }複製代碼
  4. 按壓效果的實現
    對數據進行拆分後,有一個坑就是按壓效果的實現,這個時候listView中的每一個item都不是一個完整的原始數據,要實現一個總體的按壓效果,demo的思路是:
    Paste_Image.png

    當按壓任意一個view時,通知相應的item,改變item包含的全部view的狀態。具體實現時定義了一個BackgroundLinearLayout:
    @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_UP) {
             if (mListener != null) {
                 mListener.onStatePress(false);
             }
         } else if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             if (mListener != null) {
                 mListener.onStatePress(true);
             }
         } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) {
             if (mListener != null) {
                 mListener.onStatePress(false);
             }
         }
         super.dispatchTouchEvent(ev);
         return true;
     }複製代碼
    能夠完成view按壓的回調。
    可是每一個holder須要如思路圖所示綁定到View,綁定能夠在adapter的getview中完成。:
    @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         ItemWrap itemWrap = (ItemWrap) getItem(position);
         if(convertView == null){
             convertView = ItemWrapHelper.getItemView(itemWrap.getItemType());
         }else {
             ((ItemWrap)(convertView.getTag(R.string.tag_key))).unBindView();
         }
         convertView.setTag(R.string.tag_key, itemWrap);
         BaseHolder baseHolder = (BaseHolder) convertView.getTag();
         baseHolder.render(itemWrap.getBaseItem());
         itemWrap.bindView(convertView);
         return convertView;
     }複製代碼
    基本demo就完成了,按壓效果如圖:
    Paste_Image.png

    總結

    因爲主要展示功能,界面沒有進行太多調整,另外mock數據是複製了部分[one 一個]應用的數據,表示感謝。代碼中有什麼問題,或者有什麼不合理的地方,感興趣的同窗能夠創建pull request,歡迎討論。項目地址ComplexDataStream

Other

歡迎關注公衆號wutongke,天天推送移動開發前沿技術文章:緩存

wutongke
相關文章
相關標籤/搜索