BRVAH+MTRVA,複雜?不存在的

前言

遙想Android當年,UI出來了,兩眼必定,一Bean一XML,談笑間,設計師瑟瑟發抖。額,不要在乎這首尬詩,請忽略- -!物是人非啊,如今動不動掏出個淘寶頁面,還條目不固定,還能愉快玩耍嗎?再加上雜七雜八的技術加進去,好比說埋點,UI框架愈來愈沉重,都是淚啊!若是咱們能回到過去那該多好,來吧,朋友,這是真的這不是夢。git

實戰

就算是之前,不少基礎工做仍是要作的,XML你總要寫吧?Bean你總要寫吧?渲染邏輯逃不掉吧?那麼,今天事後,額。。。這些你仍是要寫,可是其餘的非業務邏輯你能夠不寫。首先,咱們來看看這樣的首頁效果。github

效果圖展現

複雜不失簡單,簡單又不失內涵,這就是我朋友孫老師的偉大傑做,而你看到的是我用程序把它展示出來了,孫老師,哪裏不滿意,你儘管說。bash

基礎介紹

言歸正傳,這樣的一個首頁,咱們須要作怎麼樣的基礎工做呢?或者說,碰到之後更復雜的頁面咱們應該怎麼作?這裏小提示下,不要再用什麼相似ScrollView的這種東西了,誒,好像說的有點絕對,儘可能不要用,這不是谷歌想要看到的,5.0谷歌推出了RecyclerView,從它的整個設計架構來看,簡直就是爲這而生的。而RecyclerView的視圖是經過Adapter來渲染的。原始的Adapter,讓人很蛋疼,重複工做太多,咱們應該要有封裝的思想,把最須要的部分提供出來,其它不用管。架構

Adapter最火的庫我想是BRVAH了,怎麼個簡單法呢?框架

public class QuickAdapter extends BaseQuickAdapter<Status, BaseViewHolder> {
    public QuickAdapter() {
        super(R.layout.tweet, DataServer.getSampleData());
    }

    @Override
    protected void convert(BaseViewHolder viewHolder, Status item) {
        viewHolder.setText(R.id.tweetName, item.getUserName())
                .setText(R.id.tweetText, item.getText())
                .setText(R.id.tweetDate, item.getCreatedAt())
                .setVisible(R.id.tweetRT, item.isRetweet())
                .linkify(R.id.tweetText);
                 Glide.with(mContext).load(item.getUserAvatar()).crossFade().into((ImageView) viewHolder.getView(R.id.iv));
    }
}複製代碼

這樣就好了,具體戳這裏ide

該庫也提供了渲染複雜視圖的方法,但用起來仍是有必定麻煩。我我的以爲不是不寫,而是不必寫,分工明確就好了,侵入性太強不太好。那麼MTRVA就誕生了,你們不要再糾結這個名字了。函數

MTRVA最初是由於多類型視圖又繼承了Adapter才這樣命名的,後來領略了繼承的侷限性,改爲了與Adapter組合的形式呈現,叫AdapterHelper更爲合適點,由於它能夠配合絕大多數的Adapter。處理多類型視圖是最初的一個想法,也是如今的一個功能點而已,其實它內部是接管了Adapter的資源和數據源,讓咱們的數據處理更加方便,快捷,不用再去考慮資源和數據源的問題。說再多也沒用哈,實戰演練走起。工具

Tips:全文,甚至庫的demo都是以BRVAH爲配合對象。複製代碼

BaseAdapter封裝

既然要配合,那麼老是要加上無畏的代碼量,放心,我都考慮到了。放上咱們簡單基礎的BaseAdapter,固然你能夠根據本身的項目加入其它。ui

public abstract class BaseAdapter<T extends MutiTypeTitleEntity, K extends BaseViewHolder, H extends RecyclerViewAdapterHelper<T, BaseAdapter>> extends BaseQuickAdapter<T, K> {

    protected H mHelper;

    public BaseAdapter(H helper) {
        super(helper.getData());
        mHelper = helper;
        mHelper.bindAdapter(this);
    }

    @Override
    protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
        return createBaseViewHolder(parent, mHelper.getLayoutId(viewType));
    }

    @Override
    protected int getDefItemViewType(int position) {
        return mHelper.getItemViewType(position);
    }

    public H getHelper() {
        return mHelper;
    }
}複製代碼

在構造方法中,讓Helper去綁定Adapter,並把本身的數據源還給Adapter,在onCreateDefViewHolder方法中,把Helper註冊的資源還給Adapter,ItemViewType同理。到這裏,BaseAdapter封裝就結束了,並無什麼難度,代碼量也不大。this

XML和Bean

Bean我就不說了哈,跟服務端同志好好溝通,嗯,好好溝通。既然Helper接管了Adapter的數據源和資源,而後再把本身建立的提供給Adapter便可,提供方法在BaseAdapter封裝已經介紹過了,那麼它是如何建立的呢?

public class MyAdapterHelper extends AsynAdapterHelper<MutiTypeTitleEntity, BaseAdapter> {


    public MyAdapterHelper() {
        super(null);
    }

    @Override
    protected void registerMoudle() {

        ...
        registerMoudle(ItemEntity3.TYPE_3)
                .level(2)
                .layoutResId(R.layout.item_3)
                .register();
        ...
    }
}複製代碼

最簡單的時候,只要這樣就好了,這裏的工做量就是每個Adapter可能會多建立一個Helper,這裏用多是由於咱們有時候能夠複用。在構造函數中注入數據源,固然你也能夠像示例代碼中傳null,它會默認建立一個空集合。咱們能夠爲每一個ItemType註冊對應xml視圖,正如過去每一個Activity對應一個xml,固然資源註冊功能遠遠不止這些,好比咱們的庫把一個ItemType視圖,分爲header,data,footer三個部分,你能夠分別填充不一樣的資源,單個Type你又能夠在data,loading,empty,error這幾個視圖自如切換,毫無壓力。其它還有不少,我就不介紹了,否則篇幅太長,不少手機黨要罵娘了。

迴歸Adapter

Helper建立完資源後老是給迴歸於Adapter,在Base封裝中,咱們已經知道,Helper是如何和Adapter綁定在一塊兒的。來看下Adapter的示例代碼,結合代碼更加形象直觀。

public class MyAdapter extends BaseAdapter<MutiTypeTitleEntity, BaseViewHolder, MyAdapterHelper> {


    private CommonHeadEntity entity1Header = new CommonHeadEntity(ItemEntity1.HEADER_TITLE, ItemEntity1.TYPE_1);
    private CommonHeadEntity entity2Header = new CommonHeadEntity(ItemEntity2.HEADER_TITLE, ItemEntity2.TYPE_2);
    private CommonHeadEntity entity4Header = new CommonHeadEntity(ItemEntity2.HEADER_TITLE, ItemEntity4.TYPE_4);
    private CommonFooterEntity entity1Footer = new CommonFooterEntity(Constants.EXPAND, ItemEntity1.TYPE_1);

    public MyAdapter() {
        super(new MyAdapterHelper());
    }

    @Override
    protected void convert(BaseViewHolder helper, MutiTypeTitleEntity item) {
        int itemType = item.getItemType();
        switch (itemType) {
            case ItemEntity1.TYPE_1:
                renderEntity1(helper, (ItemEntity1) item);
                break;
              ...
            case ItemEntity1.TYPE_1 - RecyclerViewAdapterHelper.HEADER_TYPE_DIFFER:
            case ItemEntity2.TYPE_2 - RecyclerViewAdapterHelper.HEADER_TYPE_DIFFER:
            case ItemEntity4.TYPE_4 - RecyclerViewAdapterHelper.HEADER_TYPE_DIFFER:
                renderHeader(helper, (CommonHeadEntity) item);
                break;
            case ItemEntity1.TYPE_1 - RecyclerViewAdapterHelper.FOOTER_TYPE_DIFFER:
                renderFooter(helper, (CommonFooterEntity) item);
                break;
        }
    }

    private void renderEntity1(BaseViewHolder helper, ItemEntity1 item) {
        helper.setImageResource(R.id.item_1_img, item.getImg());
        helper.setText(R.id.item_1_title, item.getTitle());
        helper.setText(R.id.item_1_content, item.getContent());
        helper.setText(R.id.item_1_time, item.getTime());
        helper.setText(R.id.item_1_time_flag, item.getTimeFlag());
    }

    ...

    private void renderHeader(BaseViewHolder helper, CommonHeadEntity item) {
        helper.setText(R.id.title, item.getTitle());
    }

    private void renderFooter(BaseViewHolder helper, final CommonFooterEntity item) {
        final int type = item.getItemType() + RecyclerViewAdapterHelper.FOOTER_TYPE_DIFFER;
        final TextView footer = helper.getView(R.id.item_footer);
        footer.setText(item.getTitle());
        footer.setOnClickListener(v -> {
            if (Constants.EXPAND.equals(item.getTitle())) {
                item.setTitle(Constants.FOLD);
                mHelper.foldType(type, false);
            } else {
                item.setTitle(Constants.EXPAND);
                mHelper.foldType(type, true);
            }
            footer.setText(item.getTitle());
        });

    }
    ...
    public void notifyType1(List<ItemEntity1> itemEntity1s) {
        mHelper.notifyMoudleDataAndHeaderAndFooterChanged(entity1Header, itemEntity1s, entity1Footer, ItemEntity1.TYPE_1);
    }
    ...
}複製代碼

這麼一個複雜視圖,就這麼點代碼,你沒有看錯。核心方法就是convert()方法,根據ItemType渲染相對應的視圖,這正如對應不一樣的Activity有相對應的xml,Bean和渲染邏輯。關於render()方法,我就不說了吧,估計你們都寫吐了。Helper除了支持整個數據源注入外,還支持單個Type注入,甚至細化到單個Type中一個小Type,例如header。簡直炫酷的不要不要的。而這裏的notifyType1()方法是爲了注入type爲ItemEntity1.TYPE_1相對應的數據,取這個方法是由於它把全部小類型都體現了。這裏有個小tips是,建議你們把刷新的方法封裝在Adapter中,萬一哪天版本大升級,直接使用helper的朋友可要改死了,這個也同咱們在開發中封裝圖片加載庫的須要二次封裝一個工具類,萬一哪天換庫了,你不會想跑路吧?

結束語

彷佛就這麼結束了?這麼一個複雜界面就這麼結束了?逗我?這麼簡單? 額,老哥,確實就這麼多,我不會告訴你我寫這個界面只花了kjadadnkdkladllllll,sorry,我整理下發型。老實說,配合BRVAH工做量減小真的太多,說%70真的不爲過。若是你不服,你能夠用原生的寫一樣的界面,和用BRVHA+MTRVA計時比較下,我都不想跟你算代碼質量問題。甚至你能夠用專門針對多類型複雜視圖的Adapter庫,一樣的效果,一樣的功能,計時比較下你就知道了。這裏插個嘴,要善用include和merge標籤,你會有意外收穫的。這裏咱們不是說你敲代碼有多快,而是總體的一個效率問題,時間短,質量高,流程簡單易懂,還有什麼理由不使用一下?

這裏感謝下孫老師提供的設計以及一直支持個人人,很感謝。關於庫的使用確實就那麼點流程,很簡單。若是想體驗更多的用法,能夠在github上看使用指南。

傳送門

Github:github.com/crazysunj/M…

相關文章
相關標籤/搜索