可新增頭尾的RecycleView的實現

語言: CN / TW / HK

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第15天

介面編碼設計實現中,我們肯定會用到列表展示控制元件,大家肯定用過ListView。後來google推出了RecycleView,幫我們去做了很多優化(內建viewholder增加複用率、可以支援區域性重新整理、佈局可以通過外層指定layout等),正常的使用,如下:

```java MyRecycleViewAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_decorator);
    Component component = new ConCreateComponent();
    ComponentImplA impl1 = new ComponentImplA(component);
    impl1.operation();
    List<String> list = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        list.add("position " + i);
    }
    adapter = new MyRecycleViewAdapter(this);
    adapter.setData(list);
}

/**
 * 原始的yRecycleViewAdapter v1
 */
public void buttonv1(View view) {
    findViewById(R.id.recycleview).setVisibility(View.VISIBLE);
    findViewById(R.id.wrapperR).setVisibility(View.GONE);

    RecyclerView recyclerView = findViewById(R.id.recycleview);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(adapter);
}

```

在這裡插入圖片描述

但是RecycleView大家發現有一個問題,我們如果想要為這個RecycleView新增自定義的頭部view、尾部view的話,官方這個明顯做不到,那這時我們可以考慮用裝飾者模式或者繼承去擴充套件一下。

設計UML圖

首先我們通過UML圖,來設計一下,設計之前想一下,我們是想要擴充套件RecyclerView.Adapter和RecyclerView,從而可以實現addHeadView、addFootView的功能,那麼需要以下幾步驟。

1)首先,由於RecyclerView.Adapter已經是一個抽象類介面,我們自己繼承與它,然後進行包裝定義為WrapperRecyclerAdapter類 2)WrapperRecyclerAdapter肯定要持有RecyclerView.Adapter的引用,所以需要有一個構造方法,將RecyclerView.Adapter的引用傳遞進來 3)由於WrapperRecyclerAdapter繼承與RecyclerView.Adapter,肯定要去實現關鍵的方法,onCreateViewHolder(建立viewitem的holder)、onBindViewHolder(viewholder資料繫結)、getItemCount(獲取列表item的數量) 4)關鍵的一步來了,就是使用RecyclerView.Adapter、footviews、headviews,這三者組合,重寫上面的三個重要方法,給列表相應位置建立對應的item

在這裡插入圖片描述

程式碼實現1

WrapperRecyclerAdapter

```java package com.itbird.design.decorator.recycleview;

import android.view.View; import android.view.ViewGroup;

import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList; import java.util.List;

/* * RecyclerView.Adapter包裝類,擴充套件實現headView、footView的新增 * Created by itbird on 2022/6/10 / public class WrapperRecyclerAdapter extends RecyclerView.Adapter { RecyclerView.Adapter adapter; List headViews = new ArrayList<>(); List footViews = new ArrayList<>();

public WrapperRecyclerAdapter(RecyclerView.Adapter adapter) {
    this.adapter = adapter;
    adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            notifyDataSetChanged();
        }
    });
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
    //頭部的,返回頭部的viewholder
    if (position < headViews.size()) {
        return new WrapperViewHolder(headViews.get(position));
    }
    //adapter返回中間資料holder
    if (position >= headViews.size() && position < headViews.size() + adapter.getItemCount()) {
        return adapter.onCreateViewHolder(parent, adapter.getItemViewType(position - headViews.size()));
    }

    //尾部的,返回尾部的viewholder
    return new WrapperViewHolder(footViews.get(position - headViews.size() - adapter.getItemCount()));
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if (position < headViews.size() || position >= adapter.getItemCount() + headViews.size()) {
        return;
    }
    //頭部和底部不需要做處理,只需要真實的adapter需要處理
    adapter.onBindViewHolder(holder, position - headViews.size());
}

@Override
public int getItemViewType(int position) {
    return position;
}

@Override
public int getItemCount() {
    return headViews.size() + footViews.size() + adapter.getItemCount();
}

public void addHeadView(View view) {
    if (!headViews.contains(view)) {
        headViews.add(view);
        notifyDataSetChanged();
    }
}

public void addFootView(View view) {
    if (!footViews.contains(view)) {
        footViews.add(view);
        notifyDataSetChanged();
    }
}

public void removeHeadView(View view) {
    if (headViews.contains(view)) {
        headViews.add(view);
        notifyDataSetChanged();
    }
}

public void removeFootView(View view) {
    if (footViews.contains(view)) {
        footViews.remove(view);
        notifyDataSetChanged();
    }
}

static class WrapperViewHolder extends RecyclerView.ViewHolder {
    public WrapperViewHolder(@NonNull View itemView) {
        super(itemView);
    }
}

}

```

這時再去呼叫,發現就可以如下呼叫

```java /* * 擴充套件的,可以增加頭尾的recycleview v2 / public void buttonv2(View view) { findViewById(R.id.recycleview).setVisibility(View.VISIBLE); findViewById(R.id.wrapperR).setVisibility(View.GONE);

    RecyclerView recyclerView = findViewById(R.id.recycleview);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    WrapperRecyclerAdapter wrapperRecyclerAdapter = new WrapperRecyclerAdapter(adapter);
    //這裡head為什麼不會全屏,因為LayoutInflater需要parent才會全屏
    wrapperRecyclerAdapter.addHeadView(LayoutInflater.from(this).inflate(R.layout.layout_header_view, recyclerView, false));
    wrapperRecyclerAdapter.addFootView(new Button(this));
    recyclerView.setAdapter(wrapperRecyclerAdapter);

// 面向物件的六大基本原則,好像不符合最小知道原則,每次呼叫需要去new WrapperRecyclerAdapter這樣的一個包裝者,這肯定是不對的,所以需要封裝自己的recycleview } ```

看一下執行效果 在這裡插入圖片描述

程式碼實現2

這裡我們發現一個問題,這樣豈不是讓開發者,每每次去使用的時候,new原始的adapter,還需要去new WrapperRecyclerAdapter,然後才能給recyclerView去setAdapter,面向物件的六大基本原則,好像不符合最小知道原則,每次呼叫需要去new WrapperRecyclerAdapter這樣的一個包裝者,這肯定是不對的,所以需要封裝自己的recycleview。

所以我們做如下優化,將WrapperRecyclerAdapter的new操作,我們可以放入recyclerView中,這樣外界開發者只需要去關心WrapperRecycleView和RecyclerView.Adapter就可以了,對於開發者來講,只需關心RecyclerView自定義就可以了。

自定義WrapperRecycleView,重寫方法setAdapter,用於封裝new WrapperRecyclerAdapter的操作

```java package com.itbird.design.decorator.recycleview;

import android.content.Context; import android.util.AttributeSet; import android.view.View;

import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView;

/* * 自定義WrapperRecycleView,重寫方法setAdapter,用於封裝new WrapperRecyclerAdapter的操作 * Created by itbird on 2022/6/10 / public class WrapperRecycleView extends RecyclerView { WrapperRecyclerAdapter wrapperRecyclerAdapter;

public WrapperRecycleView(@NonNull Context context) {
    super(context);
}

public WrapperRecycleView(@NonNull Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public WrapperRecycleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


@Override
public void setAdapter(@Nullable Adapter adapter) {
    wrapperRecyclerAdapter = new WrapperRecyclerAdapter(adapter);
    super.setAdapter(wrapperRecyclerAdapter);
}

@Nullable
@Override
public Adapter getAdapter() {
    return wrapperRecyclerAdapter;
}

public void addHeadView(View view) {
    wrapperRecyclerAdapter.addHeadView(view);
}

public void addFootView(View view) {
    wrapperRecyclerAdapter.addFootView(view);
}

public void removeHeadView(View view) {
    wrapperRecyclerAdapter.removeHeadView(view);
}

public void removeFootView(View view) {
    wrapperRecyclerAdapter.removeFootView(view);
}

}

```

呼叫一下

```java /* * 將wrapperadapter的new操作,內部實現 v3 * 封裝的必要性,這樣的話,只需要關注WrapperRecycleView,不再需要關注WrapperRecyclerAdapter / public void buttonv3(View view) { findViewById(R.id.wrapperR).setVisibility(View.VISIBLE); findViewById(R.id.recycleview).setVisibility(View.GONE);

    WrapperRecycleView wrapperRecycleView = findViewById(R.id.wrapperR);
    wrapperRecycleView.setLayoutManager(new LinearLayoutManager(this));
    wrapperRecycleView.setAdapter(adapter);
    wrapperRecycleView.addHeadView(LayoutInflater.from(this).inflate(R.layout.layout_header_view, wrapperRecycleView, false));
    wrapperRecycleView.addFootView(new Button(this));

    //這時再去考慮一個事情,我們通過裝飾者模式把adapter封裝了一層,如果adpater有資料更新,導致變動,這時會有問題嗎?
    //這時會發現,並未更新,原因是裝飾類,並未做事件響應
}

```

是不是簡單了很多。