RecyclerView-為Adapter增加粘性頭部支持(第5篇)

效果圖

粘性頭部

簡述

這一篇我們繼續(xù)擴展上一篇的SwipeAdapter,增加粘性頭部的支持蛮原,我們命名為PinnedAdapter卧须,在第一篇的demo截圖中有一張模擬了慕課網(wǎng)中Swift課程的實現(xiàn)就是用了本篇的擴展進行實現(xiàn)的。下文中的item header為粘性頭部控件儒陨。
這里我們用最簡單的一種方式實現(xiàn)花嘶,我們采取的實現(xiàn)方式是這樣的:每一個item view都帶一個item header,在需要的時候設(shè)置其可見性框全。
接下來還是定義下我們的需求:
a. 在item支持滑動菜單的情況下察绷,我們不允許item header滑動
b. item header當前沒有提供監(jiān)聽器

經(jīng)過上面的描述我們已經(jīng)知道,客戶端設(shè)置的最原始的item view可能經(jīng)過SwipeLayout的包裝津辩,到這里后我們還需要對其進行一次LinearLayout包裝拆撼,命名為PinnedLayout容劳,我們將用它創(chuàng)建新的viewHolder。

1. 創(chuàng)建viewHolder

@Override
final
public BaseViewHolder onCreateHolder(ViewGroup parent, int viewType) {
    BaseViewHolder holder = super.onCreateHolder(parent, viewType);
    View headerView = inflater.inflate(getPinnedItemViewLayout(), parent, false);
    PinnedLayout pinnedLayout = new PinnedLayout(context);
    pinnedLayout.setUpView(parent, holder.itemView, headerView);
    //說明客戶端設(shè)置了支持滑動菜單
    if (holder.itemView instanceof SwipeLayout) {
        View itemView = ((SwipeLayout) holder.itemView).getItemView();
        itemView.setClickable(true);
        return new BaseViewHolder(pinnedLayout, itemView);
    }else {
        return new BaseViewHolder(pinnedLayout);
    }
}

2. 數(shù)據(jù)綁定

在這部分的處理里面我們需要完成以下幾點:
a. 在原有的功能上我們需要擴展一個設(shè)置粘性頭部數(shù)據(jù)的方法

  /**
   * 設(shè)置粘性控件數(shù)據(jù)
   * @param holder
   * @param position 數(shù)據(jù)域中的索引
   * @param type 數(shù)據(jù)所在的組
   */
void convertPinnedHolder(BaseViewHolder holder, int position, int type);

b. 需要為item設(shè)置一個標識闸度,用來標識在這一組中該item位于哪個位置
這個是用于在滑動處理中用來處理每個item頭部可見性以及粘性頭部

private interface PinnedItemRange {
    int FIRST    = 1;
    int OTHER    = 2;
}

c. 為item設(shè)置關(guān)聯(lián)數(shù)據(jù)的索引位置
這個是為了在回調(diào)給客戶端設(shè)置頭部數(shù)據(jù)的時候用的竭贩。

關(guān)鍵的點已經(jīng)描述清楚,下面看具體邏輯
代碼如下:

@Override
final
protected void onBindHolder(BaseViewHolder holder, int position) {
    PinnedLayout pinnedLayout = (PinnedLayout) holder.itemView;
    //每個組的第一條需要設(shè)置header可見
    if (0 == position || getItem(position).type != getItem(position - 1).type) {
        pinnedLayout.pinnedView.setVisibility(View.VISIBLE);
        holder.itemView.setTag(R.string.pinned_item_status, PinnedItemRange.FIRST);
    } else {
        pinnedLayout.pinnedView.setVisibility(View.GONE);
        holder.itemView.setTag(R.string.pinned_item_status, PinnedItemRange.OTHER);
    }
    final int type = getItem(position).type;
    holder.itemView.setTag(R.string.pinned_item_data_type, type);
    holder.itemView.setTag(R.string.item_index_in_datasection, position);
    super.onBindHolder(holder, position);
    this.convertPinnedHolder(holder, position, type);
}

3. 滑動處理

數(shù)據(jù)處理完畢之后莺禁,我們需要處理滑動中數(shù)據(jù)的操作留量。在上面數(shù)據(jù)綁定的時候我們已經(jīng)設(shè)置了在處理過程中需要的標識數(shù)據(jù),在滑動過程中我們需要知道以下幾點:

  1. 滑動時粘性頭部控件pinnedView所在位置上方和下方的控件以及附帶的數(shù)據(jù)
  2. 滑動時pinnedView數(shù)據(jù)綁定
  3. 滑動時pinnedView動畫

以上3點具體的邏輯都在下面的監(jiān)聽器里

private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        final int pinnedViewWidth = pinnedView.getMeasuredWidth();
        final int pinnedViewHeight = pinnedView.getMeasuredHeight();
        //獲取Adapter中粘性頭部位置上方的控件
        View headerView = recyclerView.findChildViewUnder(pinnedViewWidth / 2, 5);
        Object obj = headerView.getTag(R.string.pinned_item_data_type);
        if (headerView != null && null != obj && obj instanceof Integer) {
            int position = (int) headerView.getTag(R.string.item_index_in_datasection);
            convertPinnedHolder(pinnedViewHolder, position, (int) obj);
        }
        //獲取Adapter中粘性頭部位置下方的控件
        headerView = recyclerView.findChildViewUnder(pinnedViewWidth / 2, pinnedViewHeight + 1);
        obj = headerView.getTag(R.string.pinned_item_status);
        if (null == headerView || null == obj || !(obj instanceof Integer)) {
            return;
        }
        int transViewStatus = (int) obj;
        int translationY = headerView.getTop() - pinnedViewHeight;
        if (transViewStatus == PinnedItemRange.FIRST) {
            if (headerView.getTop() > 0) {
                pinnedView.setTranslationY(translationY);
            } else {
                pinnedView.setTranslationY(0);
            }
        } else if (transViewStatus == PinnedItemRange.OTHER) {
            pinnedView.setTranslationY(0);
        }
    }
};

4. 設(shè)置粘性頭部及滑動監(jiān)聽

經(jīng)過上面的描述哟冬,所有的處理過程以ok楼熄,剩下最后一點就是為RecyclerView添加滾動監(jiān)聽,同時希望粘性頭部也讓我們的庫自動加上去浩峡,不必讓客戶端很羅嗦的修改xml文件等可岂,這樣就對界面的布局有點要求,我們這里限定為RecyclerView要有一個FrameLayout的父容器包裹翰灾。

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
    try {
        //父容器是FrameLayout,否則讓粘性功能失效
        FrameLayout parent = (FrameLayout) recyclerView.getParent();
        if (null == this.pinnedViewHolder) {
            this.initPinnedView(parent, recyclerView);
        }
        recyclerView.addOnScrollListener(scrollListener);
    } catch (Exception e) {
        Log.e("DEBUG", "RecyclerView parent must be FrameLayout");
        e.printStackTrace();
        Toast.makeText(context, "RecyclerView parent must be FrameLayout", Toast.LENGTH_SHORT).show();
    }
}

5. ItemWrapper包裝器

在我這種粘性頭部實現(xiàn)的方式中缕粹,從邏輯中也能看出來是將數(shù)據(jù)進行了分組,將每一個實體數(shù)據(jù)都關(guān)聯(lián)到組上纸淮,這一實現(xiàn)是通過將實體數(shù)據(jù)進行ItemWrapper包裝平斩,為每個實體數(shù)據(jù)設(shè)置type組屬性實現(xiàn)的,所以這一篇的PinnedAdapter將數(shù)據(jù)通過泛型的方式進行了限定public class PinnedAdapter<T extends ItemWrapper>咽块。

到這里為止绘面,為Adapter添加粘性頭部支持就結(jié)束了,Adapter的整個擴展系列也結(jié)束了糜芳,如果感覺有幫助的話歡迎到github star以下或者fork都行飒货,有問題也可以提出來,也可以幫助我更好的完善峭竣。

后面會抽出點時間基于這個擴展庫再封裝一個支持下拉、上拉的分頁處理庫晃虫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皆撩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哲银,更是在濱河造成了極大的恐慌扛吞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荆责,死亡現(xiàn)場離奇詭異滥比,居然都是意外死亡,警方通過查閱死者的電腦和手機做院,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門盲泛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來濒持,“玉大人,你說我怎么就攤上這事寺滚「逃” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵村视,是天一觀的道長官套。 經(jīng)常有香客問我,道長蚁孔,這世上最難降的妖魔是什么奶赔? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮杠氢,結(jié)果婚禮上纺阔,老公的妹妹穿的比我還像新娘。我一直安慰自己修然,他們只是感情好笛钝,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愕宋,像睡著了一般玻靡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上中贝,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天囤捻,我揣著相機與錄音,去河邊找鬼邻寿。 笑死蝎土,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的绣否。 我是一名探鬼主播誊涯,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蒜撮!你這毒婦竟也來了暴构?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤段磨,失蹤者是張志新(化名)和其女友劉穎取逾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苹支,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡砾隅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了债蜜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晴埂。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡究反,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邑时,到底是詐尸還是另有隱情奴紧,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布晶丘,位于F島的核電站黍氮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏浅浮。R本人自食惡果不足惜沫浆,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滚秩。 院中可真熱鬧专执,春花似錦、人聲如沸郁油。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桐腌。三九已至拄显,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間案站,已是汗流浹背躬审。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蟆盐,地道東北人承边。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像石挂,于是被迫代替她去往敵國和親博助。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容