天天看點

(周期計劃-2)RecyclerView封裝系列(1):Header、Footer寫在前面開始

2018年技術周期計劃:周期計劃-2(2018/1/8-2018/1/14)

寫在前面

寫這邊文章的時候是2018年1月12日,也就是18年的第二周,按照原計劃應該是關于分割線的封裝,不過後來想一想既然提到了封裝,那不如多寫一寫,是以有了這個封裝系列。

這個系列的開篇是關于Header和Footer的封裝思路。

感興趣的看官,可以看看我的其他文章:

1、常用集合的源碼分析:HashMap 2、Java反射實踐:從反射中了解class 3、從公司項目配置看Gradle

開始

效果在這裡就不浪費篇幅去展示了,估計大家也都知道。

正常思路

給RecyclerView增加頭部布局本身不存在什麼難度,按照我們正常的流程,寫一個符合我們業務邏輯的頭部布局的ViewHolder,然後在getItemViewType()裡通過position傳回一個表示頭部布局ViewHolder的Type類型,然後在onCreateItemViewHolder(ViewGroup parent, int viewType)裡通過viewType來初始化return我們不同的ViewHolder。

這用做法,肯定是沒有什麼毛病。但是如果我們需要頻繁的使用不同的頭部布局,那麼我們就要考慮去封裝這個問題了。這裡簡單的提供一種封裝的思路,(當然這種思路是存在于弊端的,那就是這種封裝是基于業務的封裝,系列計劃中會有關于抽取的封裝思路)如果有更好的思路,歡迎讨論。

封裝思路(有點水)

這裡提供的思路并不具備可移植性,因為是基于業務本身的封裝,是以核心是繼承RecyclerView.Adapter并拓展。因為這個項目的本身并不單單是封裝Header,而是同樣包括了下拉、下拉、分割線等操作。主要還是為了梳理并記錄這麼一種基于業務的思想。

這裡封裝的思想,直接延續正常的思路,也就是基于Type的對外封裝。

首先,讓我們的這個類去繼承RecyclerView.Adapter,例如這樣:HeaderRecyclerViewAdapter extends RecyclerView.Adapter。

然後在構造方法中傳入我們正常的Adapter,然後建立倆個List<View>,用于放置我們的Header以及Footer。

對應的我們要對外暴露相應的add方法:

public void addHeader(View headerView) {
        if (!headerViews.contains(headerView)) {
            headerViews.add(headerView);
            //當然這裡可以使用notifyItemChanged(int position)去優化
            notifyDataSetChanged();
        }
    }
           

給Header設定一個合适的Type,比如:

public static final int VIEW_TYPE_HEADER_OFFSET = Integer.MIN_VALUE;
           

接下來就是getItemViewType()方法的處理了。

@Override
    public int getItemViewType(int position) {
        if (!headerViews.isEmpty() && position < headerViews.size()) {
            return VIEW_TYPE_HEADER_OFFSET + position;
        }
        //計算我們正常資料的position,也就是減去Header的List的size的數量即可
        if (!headerViews.isEmpty()) {
            position -= headerViews.size();
        }
        //adapter就是我們在構造方法裡傳進來的RecyclerView.Adapter
        if (adapter != null && position < adapter.getItemCount()) {
            return adapter.getItemViewType(position);
        }     
    }
           

return了viewType,那麼我們就要處理onCreateViewHolder()方法了。

public static boolean isHeaderView(int viewType) {
        return viewType < VIEW_TYPE_HEADER_OFFSET;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (isHeaderView(viewType)) {
            return new HeaderViewHolder(headerViews.get(viewType - VIEW_TYPE_HEADER_OFFSET));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

           

緊接着就是處理onBindViewHolder()方法。其實這裡對于我們來說并不需要處理,因為我們來說,Header的操作應該交由外部處理,是以這裡我們return掉,隻需要對正常資料的adapter,調用super方法即可。

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        //這裡直接return的原因,我們這裡不做對Header的處理,交由外部。
        if (isHeaderView(viewType)) {
            return;
        }
        position -= headerViews.size();
        adapter.onBindViewHolder(holder, position);
    }
           

到這我們的針對Header的封裝就算是結束了,使用的時候也很簡單,隻需要在初始化了這個實作類後調用addHeader方法,傳入我們自己的布局即可。(單純隻看這個封裝,其實并不能給我們帶來多麼大的體驗,但是如果綜合多個功能去封裝那所帶來的戰鬥力将不言而喻)

Footer的話,和Header是同理的。

總結

其實單單是為了封裝Header本身的意義并不大,就像上訴的列子,并無法給我們帶來多麼大的便利,但是如果我們綜合去封裝,就可以發現它存在的意義,比如:

我的觀公司RecyclerView封裝有感

中那樣,把對應的功能封裝到一起,那麼作為一個業務的基類,我們将會發現可以很快捷完成不少類似的業務。

2018年7月2号,我正式開始了自己的Android工作,為了能夠讓自己能夠好好完成工作,并且能夠快速得到技術提升。是以打算以公衆号的方式去敦促自己學習,我會把自己日常的學習筆記釋出到公衆号上,如果可以,共同進步!~
(周期計劃-2)RecyclerView封裝系列(1):Header、Footer寫在前面開始
個人公衆号

繼續閱讀