Android 復雜數據流的 “高效” 渲染

hanguoqian 9年前發布 | 6K 次閱讀 安卓開發 Android開發 移動開發

代碼結構

  • Model

    model中是所有的原始數據類,這里為了方便,每種數據的名字實際包含了需要展示的模型:如CardWithTitleItem數據實際需要展示一個標題和一個卡片,HeaderImageCardItem需要展示一個頭部、圖片、卡片。

Paste_Image.png

  • Adapter

    Adapter中放置了adapter和各種類型的holder,這里把多種數據類型拆分成了card、divider、header、image、link、text、title,并為每種類型設置了相應的布局。

  • Util

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

代碼思路

我們的目的是將復雜的數據類型進行拆分,從而達到細顆粒的view復用,降低內存占用。

  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的思路是:

    當按壓任意一個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就完成了,按壓效果如圖:

    總結

    由于主要展現功能,界面沒有進行太多調整,另外mock數據是復制了部分[one 一個]應用的數據,表示感謝。代碼中有什么問題,或者有什么不合理的地方,感興趣的同學可以建立pull request,歡迎討論。

來自:http://www.jianshu.com/p/1ec90ddb65c5

 

 本文由用戶 hanguoqian 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!