Android 復雜數據流的 “高效” 渲染
代碼結構

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

Paste_Image.png

-
Adapter
Adapter中放置了adapter和各種類型的holder,這里把多種數據類型拆分成了card、divider、header、image、link、text、title,并為每種類型設置了相應的布局。
-
Util
Util中設置如何transform數據到相應的展示模版,以及解決按壓效果的問題。
代碼思路
我們的目的是將復雜的數據類型進行拆分,從而達到細顆粒的view復用,降低內存占用。
- 確定拆分后的展示類型,這里使用了一個enum類型:
public enum ItemType { TITLE, CARD, HEADER, IMAGE, TEXT, LINK, DIVIDER } - transform數據到模板,拆分后一個數據類型對應多個模板,這里我們使用hashmap建立數據到模板的影射關系:
private static Map<Class, List<ItemType>> map = new HashMap<>();
getTransformedItem方法將原始數據進行拆分,注意每種原始數據類型中都要加入divider模板,用于展示ListView的分割線。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; } - 根據不同的展示類型提供不同的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; } - 按壓效果的實現
對數據進行拆分后,有一個坑就是按壓效果的實現,這個時候listView中的每個item都不是一個完整的原始數據,要實現一個整體的按壓效果,demo的思路是:
當按壓任意一個view時,通知相應的item,改變item包含的所有view的狀態。具體實現時定義了一個BackgroundLinearLayout:
可以完成view按壓的回調。@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; }
但是每個holder需要如思路圖所示綁定到View,綁定可以在adapter的getview中完成。:
基本demo就完成了,按壓效果如圖:@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; }
總結
由于主要展現功能,界面沒有進行太多調整,另外mock數據是復制了部分[one 一個]應用的數據,表示感謝。代碼中有什么問題,或者有什么不合理的地方,感興趣的同學可以建立pull request,歡迎討論。
來自:http://www.jianshu.com/p/1ec90ddb65c5
本文由用戶 hanguoqian 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!