Apakah ada setara addHeaderView untuk RecyclerView?

290

Saya mencari yang setara dengan addHeaderView untuk tampilan pendaur ulang. Pada dasarnya saya ingin memiliki gambar dengan 2 tombol yang ditambahkan sebagai header ke listview. Apakah ada cara berbeda untuk menambahkan tampilan header ke tampilan pendaur ulang? Contoh untuk panduan akan sangat membantu

EDIT 2 (menambahkan tata letak fragmen):

Setelah menambahkan pernyataan log, sepertinya getViewType hanya pernah menerima posisi 0. Hal ini menyebabkan onCreateView hanya memuat satu tata letak:

10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> onCreateViewHolder, viewtype: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info Adapter-> onBindViewHolder, viewType: 0

Transisi fragmen untuk memuat CommentFragment:

@Override
public void onPhotoFeedItemClick(View view, int position) {
    if (fragmentManager == null)
        fragmentManager = getSupportFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    if (view.getId() == R.id.button_comment){
        CommentFragment commentFragment = CommentFragment.newInstance("","", position);
        fragmentTransaction.add(R.id.main_activity, commentFragment,"comment_fragment_tag");
        fragmentTransaction.addToBackStack(Constants.TAG_COMMENTS);
        fragmentTransaction.commit();
    }
}

OnCreateView Fragmen:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_comment, container, false);
    mRecyclerView = (RecyclerView) view.findViewById(R.id.list_recylclerview);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(_context));
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    mAdapter = new CommentAdapter(R.layout.row_list_comments, R.layout.row_header_comments, _context, comments);
    mRecyclerView.setAdapter(mAdapter);
    return view;
}

Fragmen berisi recycleview:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context="co.testapp.fragments.CommentFragment"
    android:background="@color/white">
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
            <android.support.v7.widget.RecyclerView
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/list_recylclerview"
                android:layout_width="match_parent"
                android:layout_height="200dp" />
        </RelativeLayout>
</RelativeLayout>

Tata letak baris komentar:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent" android:layout_margin="10dp"
    android:background="@color/white">
    <!--Profile Picture-->
    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:id="@+id/profile_picture"
        android:background="@color/blue_testapp"/>
    <!--Name-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="First Name Last Name"
        android:textSize="16dp"
        android:textColor="@color/blue_testapp"
        android:id="@+id/name_of_poster"
        android:layout_toRightOf="@id/profile_picture"
        />
    <!--Comment-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_marginTop="-5dp"
        android:text="This is a test comment"
        android:textSize="14dp"
        android:textColor="@color/black"
        android:id="@+id/comment"
        android:layout_below="@id/name_of_poster"
        android:layout_toRightOf="@id/profile_picture"/>
</RelativeLayout>

Header

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="300dp"
        android:id="@+id/header_photo"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>

Kode Adaptor (terima kasih kepada hister untuk memulai saya):

public class CommentAdapter extends RecyclerView.Adapter<ViewHolder>{

    private final int rowCardLayout;
    public static Context mContext;
    private final int headerLayout;
    private final String [] comments;
    private static final int HEADER = 0;
    private static final int OTHER = 0;

    public CommentAdapter(int rowCardLayout, int headerLayout, Context context, String [] comments) {
        this.rowCardLayout = rowCardLayout;
        this.mContext = context;
        this.comments = comments;
        this.headerLayout = headerLayout;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        logger.i("onCreateViewHolder, viewtype: " + i); //viewtype always returns 0 so OTHER layout is never inflated
        if (i == HEADER) {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(headerLayout, viewGroup, false);
            return new ViewHolderHeader(v);
        }
        else if (i == OTHER){
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowCardLayout, viewGroup, false);
            return new ViewHolderComments(v);
        }
        else 
          throw new RuntimeException("Could not inflate layout");
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        logger.i("onBindViewHolder, viewType: " + i);

        if (viewHolder instanceof ViewHolderComments)
            ((ViewHolderComments) viewHolder).comment.setText(comments[i].toString());
        if (viewHolder instanceof ViewHolderHeader)
           ((ViewHolderHeader) viewHolder).header.setImageResource(R.drawable.image2);
        else {
            logger.e("no instance of viewholder found");
        }
    }

    @Override
    public int getItemCount() {
        int count = comments.length + 1;
        logger.i("getItemCount: " + count);
        return count;
    }

    @Override
    public int getItemViewType(int position) {
        logger.i("getItemViewType position: " + position);
        if (position == HEADER)
            return HEADER;
        else
            return OTHER;
    }

    public static class ViewHolderComments extends RecyclerView.ViewHolder {
        public TextView comment;
        public ImageView image;

        public ViewHolderComments(View itemView) {
            super(itemView);
            comment = (TextView) itemView.findViewById(R.id.comment);
            image = (ImageView) itemView.findViewById(R.id.image);
        }
    }

    public static class ViewHolderHeader extends RecyclerView.ViewHolder {
        public final ImageView header;

        public ViewHolderHeader(View itemView){
            super(itemView);
            header = (ImageView) itemView.findViewById(R.id.header_photo);
        }
    }
}

Dengan menggunakan kode di atas, hanya tata letak tajuk yang ditampilkan karena tipe tampilan selalu 0. Tampak seperti ini . Jika saya memaksakan tata letak lainnya seperti ini :

ViciDroid
sumber
Karena ini adalah duplikat dari pertanyaan di sini , saya memposting jawaban saya di sana :
seb
Solusi yang elegan: stackoverflow.com/questions/33579800/…
Ignacio Hagopian

Jawaban:

457

Tidak ada cara yang mudah seperti listview.addHeaderView()tetapi Anda dapat mencapainya dengan menambahkan tipe ke adaptor Anda untuk header.

Berikut ini sebuah contoh

public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;
    String[] data;

    public HeaderAdapter(String[] data) {
        this.data = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_ITEM) {
            //inflate your layout and pass it to view holder
            return new VHItem(null);
        } else if (viewType == TYPE_HEADER) {
            //inflate your layout and pass it to view holder
            return new VHHeader(null);
        }

        throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof VHItem) {
            String dataItem = getItem(position);
            //cast holder to VHItem and set data
        } else if (holder instanceof VHHeader) {
            //cast holder to VHHeader and set data for header.
        }
    }

    @Override
    public int getItemCount() {
        return data.length + 1;
    }

    @Override
    public int getItemViewType(int position) {
        if (isPositionHeader(position))
            return TYPE_HEADER;

        return TYPE_ITEM;
    }

    private boolean isPositionHeader(int position) {
        return position == 0;
    }

    private String getItem(int position) {
        return data[position - 1];
    }

    class VHItem extends RecyclerView.ViewHolder {
        TextView title;

        public VHItem(View itemView) {
            super(itemView);
        }
    }

    class VHHeader extends RecyclerView.ViewHolder {
        Button button;

        public VHHeader(View itemView) {
            super(itemView);
        }
    }
}

tautan ke inti -> di sini

EC84B4
sumber
2
semuanya tampak baik-baik saja dan itu harus berfungsi, tetapi bagaimanapun membuat tampilan pendaur ulang MATCH_PARENT melihat apakah ada perubahan. juga jika memungkinkan membuat pendaur ulang melihat akar dari tata letak itu (bagus untuk kinerja).
EC84B4
1
Sun of a gun, membersihkan recyclerview dan menambahkan induk pertandingan bekerja .... Terima kasih banyak atas bantuannya! Saya akan mendukung Anda sesegera mungkin.
ViciDroid
14
private String getItem(int position) { return data[position + 1]; }Menyebabkan NPE. Karena posisi kami telah bertambah satu, karena tajuk, kami harus mengurangi 1 untuk mendapatkan item yang benar dari data kami []. private String getItem(int position) { return data[position - 1]; }
Tim Malseed
2
jika kisi Anda hanyalah kisi sederhana, gunakan LinearLayoutManager dan tampilkan item dalam satu baris. disebut baris ember yang pernah saya lihat google menggunakannya di beberapa aplikasi mereka.
EC84B4
4
@nsL Anda dapat menggunakan pendekatan ini dan juga menambahkan setSpanSizeLookupuntuk GridLayoutManager, sehingga header akan mengambil semua kolom
Dmitry Zaytsev
62

Mudah dan dapat digunakan kembali ItemDecoration

Header statis dapat dengan mudah ditambahkan dengan ItemDecorationdan tanpa perubahan lebih lanjut.

// add the decoration. done.
HeaderDecoration headerDecoration = new HeaderDecoration(/* init */);
recyclerView.addItemDecoration(headerDecoration);

Dekorasi juga dapat digunakan kembali karena tidak perlu memodifikasi adaptor atau RecyclerViewsama sekali.

Kode sampel yang disediakan di bawah ini akan memerlukan tampilan untuk ditambahkan ke atas yang hanya dapat meningkat seperti yang lainnya. Ini bisa terlihat seperti ini:

Sampel HeaderDecoration

Mengapa statis ?

Jika Anda hanya perlu menampilkan teks dan gambar, solusi ini cocok untuk Anda — tidak ada kemungkinan interaksi pengguna seperti tombol atau pager tampilan, karena itu hanya akan ditarik ke atas daftar Anda.

Penanganan daftar kosong

Jika tidak ada tampilan untuk didekorasi, dekorasi tidak akan ditarik. Anda masih harus menangani daftar kosong sendiri. (Satu solusi yang mungkin dilakukan adalah menambahkan item dummy ke adaptor.)

Kode

Anda dapat menemukan kode sumber lengkap di GitHub termasuk Builderuntuk membantu inisialisasi dekorator, atau cukup gunakan kode di bawah ini dan berikan nilai Anda sendiri kepada konstruktor.

Harap pastikan untuk mengatur yang benar layout_heightuntuk tampilan Anda. mis. match_parentmungkin tidak berfungsi dengan baik.

public class HeaderDecoration extends RecyclerView.ItemDecoration {

    private final View mView;
    private final boolean mHorizontal;
    private final float mParallax;
    private final float mShadowSize;
    private final int mColumns;
    private final Paint mShadowPaint;

    public HeaderDecoration(View view, boolean scrollsHorizontally, float parallax, float shadowSize, int columns) {
        mView = view;
        mHorizontal = scrollsHorizontally;
        mParallax = parallax;
        mShadowSize = shadowSize;
        mColumns = columns;

        if (mShadowSize > 0) {
            mShadowPaint = new Paint();
            mShadowPaint.setShader(mHorizontal ?
                    new LinearGradient(mShadowSize, 0, 0, 0,
                            new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
                            new float[]{0f, .5f, 1f},
                            Shader.TileMode.CLAMP) :
                    new LinearGradient(0, mShadowSize, 0, 0,
                            new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
                            new float[]{0f, .5f, 1f},
                            Shader.TileMode.CLAMP));
        } else {
            mShadowPaint = null;
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        // layout basically just gets drawn on the reserved space on top of the first view
        mView.layout(parent.getLeft(), 0, parent.getRight(), mView.getMeasuredHeight());

        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            if (parent.getChildAdapterPosition(view) == 0) {
                c.save();
                if (mHorizontal) {
                    c.clipRect(parent.getLeft(), parent.getTop(), view.getLeft(), parent.getBottom());
                    final int width = mView.getMeasuredWidth();
                    final float left = (view.getLeft() - width) * mParallax;
                    c.translate(left, 0);
                    mView.draw(c);
                    if (mShadowSize > 0) {
                        c.translate(view.getLeft() - left - mShadowSize, 0);
                        c.drawRect(parent.getLeft(), parent.getTop(), mShadowSize, parent.getBottom(), mShadowPaint);
                    }
                } else {
                    c.clipRect(parent.getLeft(), parent.getTop(), parent.getRight(), view.getTop());
                    final int height = mView.getMeasuredHeight();
                    final float top = (view.getTop() - height) * mParallax;
                    c.translate(0, top);
                    mView.draw(c);
                    if (mShadowSize > 0) {
                        c.translate(0, view.getTop() - top - mShadowSize);
                        c.drawRect(parent.getLeft(), parent.getTop(), parent.getRight(), mShadowSize, mShadowPaint);
                    }
                }
                c.restore();
                break;
            }
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getChildAdapterPosition(view) < mColumns) {
            if (mHorizontal) {
                if (mView.getMeasuredWidth() <= 0) {
                    mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                            View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
                }
                outRect.set(mView.getMeasuredWidth(), 0, 0, 0);
            } else {
                if (mView.getMeasuredHeight() <= 0) {
                    mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                            View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
                }
                outRect.set(0, mView.getMeasuredHeight(), 0, 0);
            }
        } else {
            outRect.setEmpty();
        }
    }
}

Harap dicatat: Proyek GitHub adalah taman bermain pribadi saya. Hal ini tidak thorougly diuji, yang mengapa ada perpustakaan belum .

Apa fungsinya?

An ItemDecorationadalah gambar tambahan untuk item daftar. Dalam hal ini, dekorasi digambar di bagian atas item pertama.

Tampilan diukur dan ditata, lalu ditarik ke bagian atas item pertama. Jika efek paralaks ditambahkan, itu juga akan terpotong ke batas yang benar.

David Medenjak
sumber
1
terlihat solusi yang elegan, tetapi, saya punya pertanyaan: kapan saya harus memanggil recyclerView.addItemDecoration?
Weibo
1
Terima kasih, itu bekerja dengan baik! Satu-satunya downside menggunakan metode ini, adalah bahwa Anda harus memiliki setidaknya 1 baris di RecyclerView.
Philip Giuliani
3
Bagaimana cara onClickListener untuk header?
Prashant Kedia
1
Saya mendapatkan pengecualian berikut: java.lang.NullPointerException: Mencoba untuk memanggil metode virtual 'boolean android.support.v7.widget.RecyclerView $ ViewHolder.shouldIgnore ()' pada referensi objek nol
Makalele
1
Apakah ada cara untuk mengakses tampilan di dekorasi? Saya ingin mengatur teks secara dinamis.
SMahdiS
44

Jangan ragu untuk menggunakan perpustakaan saya, tersedia di sini .

Ini memungkinkan Anda membuat tajuk Viewuntuk apa pun RecyclerViewyang menggunakan LinearLayoutManageratau GridLayoutManagerhanya dengan pemanggilan metode sederhana.

masukkan deskripsi gambar di sini

Bartek Lipinski
sumber
bagaimana cara mengubah tinggi header?
ingsaurabh
Saya telah menggunakan ini untuk menunjukkan tajuk di bagian bawah atau Anda dapat mengatakan itu sebagai catatan kaki, tetapi saya mendapatkan masalah ketika tayangan saya dimuat untuk pertama kalinya menunjukkan posisi terakhir dan semua item daftar ditampilkan dalam urutan terbalik. @blipinsk
Ronak Joshi
Apakah Anda berpikir bahwa masalah yang Anda uraikan dapat terkait dengan ini: github.com/blipinsk/RecyclerViewHeader/issues/16 ?
Bartek Lipinski
Lipinski telah pensiun dari perpustakaan ini dan menyarankan untuk menggunakan yang ini sebagai gantinya: github.com/Karumi/HeaderRecyclerView
radley
1
Saya pensiun perpustakaan ini tepatnya. Saya masih memberikan dukungan ringan untuk itu, tetapi memang saya menyarankan untuk pergi dengan adaptor khusus sebagai gantinya (misalnya yang dari Karumi).
Bartek Lipinski
31

Akan menunjukkan kepada Anda untuk membuat tajuk dengan item dalam tampilan Pendaur Ulang.Tampilan pendaur ulang dengan Header

Langkah 1- Tambahkan ketergantungan ke file gradle Anda.

compile 'com.android.support:recyclerview-v7:23.2.0'
// CardView
compile 'com.android.support:cardview-v7:23.2.0'

Cardview digunakan untuk tujuan dekorasi.

Langkah2 - Buat tiga file xml. Satu untuk aktivitas utama. Kedua untuk tata letak Header. Ketiga untuk tata letak item daftar.

activity_main.xml

<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

header.xml

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardElevation="2dp">

    <TextView
        android:id="@+id/txtHeader"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="@dimen/abc_text_size_large_material"
        android:background="#DCDCDC"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</android.support.v7.widget.CardView>

list.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardElevation="1dp">

        <TextView
            android:id="@+id/txtName"
            android:text="abc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.v7.widget.CardView>

</LinearLayout>

Langkah 3- Buat tiga kelas kacang.

Header.java

public class Header extends ListItem {
    private String header;

    public String getHeader() {
        return header;
    }
    public void setHeader(String header) {
        this.header = header;
    }
}

ContentItem.java

public class ContentItem extends ListItem {

    private String name;
    private String rollnumber;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    public String getRollnumber() {
        return rollnumber;
    }

    public void setRollnumber(String rollnumber) {
        this.rollnumber = rollnumber;
    }
}

ListItem.java

public class ListItem {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

Langkah 4- Buat adaptor bernama MyRecyclerAdapter.java

public class MyRecyclerAdapter extends  RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;

    //Header header;
    List<ListItem> list;
    public MyRecyclerAdapter(List<ListItem> headerItems) {
        this.list = headerItems;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        if (viewType == TYPE_HEADER) {
            View v = inflater.inflate(R.layout.header, parent, false);
            return  new VHHeader(v);
        } else {
            View v = inflater.inflate(R.layout.list, parent, false);
            return new VHItem(v);
        }
        throw new IllegalArgumentException();
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof VHHeader) {
           // VHHeader VHheader = (VHHeader)holder;
            Header  currentItem = (Header) list.get(position);
            VHHeader VHheader = (VHHeader)holder;
            VHheader.txtTitle.setText(currentItem.getHeader());
        } else if (holder instanceof VHItem) 
            ContentItem currentItem = (ContentItem) list.get(position);
            VHItem VHitem = (VHItem)holder;
            VHitem.txtName.setText(currentItem.getName());
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (isPositionHeader(position))
            return TYPE_HEADER;
        return TYPE_ITEM;
    }

    private boolean isPositionHeader(int position) {
        return list.get(position) instanceof Header;
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    class VHHeader extends RecyclerView.ViewHolder{
        TextView txtTitle;
        public VHHeader(View itemView) {
            super(itemView);
            this.txtTitle = (TextView) itemView.findViewById(R.id.txtHeader);
        }
    }
    class VHItem extends RecyclerView.ViewHolder{
        TextView txtName;
        public VHItem(View itemView) {
            super(itemView);
            this.txtName = (TextView) itemView.findViewById(R.id.txtName);
        }
    }
}

Langkah 5- Di MainActivity tambahkan kode berikut:

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List<List<ListItem>> arraylist;
    MyRecyclerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        adapter = new MyRecyclerAdapter(getList());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);
    }

    private ArrayList<ListItem> getList() {
        ArrayList<ListItem> arrayList = new ArrayList<>();
        for(int j = 0; j <= 4; j++) {
            Header header = new Header();
            header.setHeader("header"+j);
            arrayList.add(header);
            for (int i = 0; i <= 3; i++) {
                ContentItem item = new ContentItem();
                item.setRollnumber(i + "");
                item.setName("A" + i);
                arrayList.add(item);
            }
        }
        return arrayList;
    }

}

Fungsi getList () secara dinamis menghasilkan data untuk header dan untuk item daftar.

Anshul Aggarwal
sumber
Salah satu jawaban terbaik. Saya menggunakannya di menu navigasiView untuk navigasi
Saurabh Bhandari
Ini adalah jawaban sederhana yang bagus - Saya tidak bisa memikirkan trik untuk dengan mudah menggabungkan 2 item data menjadi satu ListItem - warisan membuatnya mudah dan dapat dicapai - ya!
yura
8

Anda dapat mencapainya menggunakan perpustakaan SectionedRecyclerViewAdapter , ia memiliki konsep "Bagian", di mana Bagian memiliki Header, Footer dan Konten (daftar item). Dalam kasus Anda, Anda mungkin hanya membutuhkan satu Bagian tetapi Anda dapat memiliki banyak Bagian:

masukkan deskripsi gambar di sini

1) Buat kelas Bagian kustom:

class MySection extends StatelessSection {

    List<String> myList = Arrays.asList(new String[] {"Item1", "Item2", "Item3" });

    public MySection() {
        // call constructor with layout resources for this Section header, footer and items 
        super(R.layout.section_header, R.layout.section_footer,  R.layout.section_item);
    }

    @Override
    public int getContentItemsTotal() {
        return myList.size(); // number of items of this section
    }

    @Override
    public RecyclerView.ViewHolder getItemViewHolder(View view) {
        // return a custom instance of ViewHolder for the items of this section
        return new MyItemViewHolder(view);
    }

    @Override
    public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyItemViewHolder itemHolder = (MyItemViewHolder) holder;

        // bind your view here
        itemHolder.tvItem.setText(myList.get(position));
    }
}

2) Buat ViewHolder kustom untuk item:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

    public MyItemViewHolder(View itemView) {
        super(itemView);

        tvItem = (TextView) itemView.findViewById(R.id.tvItem);
    }
}

3) Siapkan ReclyclerView Anda dengan SectionedRecyclerViewAdapter

// Create an instance of SectionedRecyclerViewAdapter 
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();

MySection mySection = new MySection();

// Add your Sections
sectionAdapter.addSection(mySection);

// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
Gustavo
sumber
ini mudah digunakan tetapi bagaimana menggunakannya untuk gulir horizontal .. setiap kali saya mengubah orientasi ke horizontal maka seluruh perubahan recyclerview ke gulir horizontal tetapi saya ingin bagian barang menjadi gulir horizontal saja .. tolong bantu saya untuk melakukannya
roshan posakya
6

Anda bisa menempatkan tajuk dan RecyclerView Anda di NestedScrollView:

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <include
            layout="@layout/your_header"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/list_recylclerview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>

Agar pengguliran berfungsi dengan benar, Anda harus menonaktifkan pengguliran bersarang di RecyclerView Anda:

myRecyclerView.setNestedScrollingEnabled(false);
Cristan
sumber
3
atau gunakan android: nestedScrollingEnabled = "false"
radley
android: nestedScrollingEnabled = "false" membutuhkan api level 21, yang menyebalkan. Pokoknya solusi yang bagus! Hal yang sama Anda harus menambahkan footer lain atau bahkan mungkin recyclerview kedua?
Makalele
47
JANGAN PERNAH melakukan ini. Ini sepertinya akan berfungsi dengan baik. Tapi yang sebenarnya dilakukannya adalah meneruskan scroll ke srollView. Jadi recyclerView tidak melakukan apa pun dengan sendirinya. Tidak ada daur ulang tampilan. Aplikasi ini akan macet jika Anda mendapatkan terlalu banyak item di recyclerView Anda karena sekarang berfungsi seperti scrollView sederhana.
VipulKumar
2
Komentar @ VipulKumar sepenuhnya benar. Ketika Anda menggunakan recyclerview di dalam scrollview lain, tidak akan ada daur ulang dan itu akan membuat semua item.
VolkanSahin45
Anda dapat melakukan ini jika item Anda di recyclerView kurang dari 10. 😄
JIE WANG
5

API asli tidak memiliki fitur "addHeader", tetapi memiliki konsep "addItem".

Saya dapat memasukkan fitur spesifik header ini dan meluas untuk footer juga dalam proyek FlexibleAdapter saya . Saya menyebutnya Header dan Footer yang Dapat Digulir .

Di sini cara kerjanya:

Header dan Foot yang Dapat Digulir adalah item-item khusus yang digulirkan bersama dengan semua yang lain, tetapi itu bukan milik item utama (item bisnis) dan mereka selalu ditangani oleh adaptor di samping item utama. Barang-barang itu secara terus-menerus terletak di posisi pertama dan terakhir.

masukkan deskripsi gambar di sini

Ada banyak yang bisa dikatakan tentang mereka, lebih baik membaca halaman wiki terperinci .

Selain itu, FlexibleAdapter memungkinkan Anda membuat header / bagian, juga Anda dapat membuatnya lengket dan puluhan fitur lainnya seperti item yang dapat diperluas, gulir tanpa akhir, ekstensi UI dll ... semua dalam satu perpustakaan!

Davideas
sumber
4

Berdasarkan posting ini , saya membuat subclass dari RecyclerView.Adapter yang mendukung sejumlah header dan footer yang berubah-ubah.

https://gist.github.com/mheras/0908873267def75dc746

Meskipun tampaknya menjadi solusi, saya juga berpikir hal ini harus dikelola oleh LayoutManager. Sayangnya, saya membutuhkannya sekarang dan saya tidak punya waktu untuk mengimplementasikan StaggeredGridLayoutManager dari awal (atau bahkan memperpanjang dari itu).

Saya masih mengujinya, tetapi Anda bisa mencobanya jika mau. Tolong beri tahu saya jika Anda menemukan masalah dengan itu.

mato
sumber
2

Ada satu solusi lagi yang mencakup semua kasus penggunaan di atas: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

Anda dapat membuat Adaptor Grup yang memegang Adaptor Anda apa adanya, bersama dengan adaptor dengan satu item untuk mewakili header. Kode ini mudah dan dapat dibaca:

AdapterGroup adapterGroup = new AdapterGroup();
adapterGroup.addAdapter(SingleAdapter.create(R.layout.header));
adapterGroup.addAdapter(new CommentAdapter(...));

recyclerView.setAdapter(adapterGroup);

AdapterGroup juga memungkinkan penyatuan, jadi untuk adaptor dengan bagian, Anda dapat membuat AdapterGroup per bagian. Kemudian letakkan semua bagian di root AdapterGroup.

blurkidi
sumber
1

HeaderView tergantung pada LayoutManager. Tidak ada LayoutManagers default yang mendukung ini dan mungkin tidak akan. HeaderView di ListView menciptakan banyak kerumitan tanpa manfaat signifikan.

Saya sarankan membuat kelas adaptor dasar yang menambahkan item untuk Header jika disediakan. Jangan lupa untuk mengganti metode notifikasi * untuk mengimbanginya dengan benar tergantung pada apakah header ada atau tidak.

yigit
sumber
Apakah ada contoh Anda bisa mengarahkan saya ke yang menggunakan tampilan pendaur ulang dengan adaptor dasar? Terima kasih!
ViciDroid
Ada satu manfaat yang sangat signifikan untuk Header / Footer dalam daftar: Anda dapat menggulirnya keluar dari tampilan. Lihat contoh ini di mana jumlah baris yang terlihat hampir dua kali lipat segera setelah pinguin keluar , saya tidak mengetahui cara lain untuk melakukan ini, tetapi ListView.addHeaderViewatau jawaban untuk pertanyaan ini.
TWiStErRob
Saya rasa saya tidak melakukannya dengan benar. Jika itu adalah item pertama dalam adaptor, mengapa tidak bisa gulir seperti yang ada dalam contoh?
yigit
2
Anda tidak dapat mengganti metode notifikasi *, karena ditandai sebagai final.
mato
hmm saya tidak memeriksa maaf itu. Sebagai gantinya, Anda dapat membuat pembungkus adaptor yang menambahkan dapat diamati ke adaptor yang dibungkus dan mengirimkan acara yang dipindahkan dari dirinya sendiri.
yigit
1
First - extends RecyclerView.Adapter<RecyclerView.ViewHolder>

public class MenuAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

Setelah - Ganti metode getItemViewTpe *** Lebih Penting

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

metode onCreateViewHolder

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
    View header = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_header_item, parent, false);
    Log.d("onCreateViewHolder", String.valueOf(viewType));

    if (viewType == 0) {
        return new MenuLeftHeaderViewHolder(header, onClickListener);
    } else {
        return new MenuLeftViewHolder(view, onClickListener);
    }
}

metode onBindViewHolder

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (position == 0) {
        MenuHeaderViewHolder menuHeaderViewHolder = (MenuHeaderViewHolder) holder;
        menuHeaderViewHolder.mTitle.setText(sMenuTitles[position]);
        menuHeaderViewHolder.mImage.setImageResource(sMenuImages[position]);
    } else {
        MenuViewHolder menuLeftViewHolder = (MenuLeftViewHolder) holder;
        menuViewHolder.mTitle.setText(sMenuTitles[position]);
        menuViewHolder.mImage.setImageResource(sMenuImages[position]);
    }
}

pada akhirnya mengimplementasikan statis kelas ViewHolders

public static class MenuViewHolder extends RecyclerView.ViewHolder 

public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 
vrbsm
sumber
1

berikut beberapa itemdecoration untuk recyclerview

public class HeaderItemDecoration extends RecyclerView.ItemDecoration {

private View customView;

public HeaderItemDecoration(View view) {
    this.customView = view;
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
    customView.layout(parent.getLeft(), 0, parent.getRight(), customView.getMeasuredHeight());
    for (int i = 0; i < parent.getChildCount(); i++) {
        View view = parent.getChildAt(i);
        if (parent.getChildAdapterPosition(view) == 0) {
            c.save();
            final int height = customView.getMeasuredHeight();
            final int top = view.getTop() - height;
            c.translate(0, top);
            customView.draw(c);
            c.restore();
            break;
        }
    }
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (parent.getChildAdapterPosition(view) == 0) {
        customView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
        outRect.set(0, customView.getMeasuredHeight(), 0, 0);
    } else {
        outRect.setEmpty();
    }
}
}      
Trunks ssj
sumber
1

Saya membuat implementasi berdasarkan @ hister untuk keperluan pribadi saya, tetapi menggunakan warisan.

Saya menyembunyikan mekanisme detail implementasi (seperti menambah 1 ke itemCount, mengurangi 1 dari position) dalam kelas super abstrak HeadingableRecycleAdapter, dengan menerapkan metode yang diperlukan dari Adaptor seperti onBindViewHolder, getItemViewTypedan getItemCount, menjadikan metode itu final, dan menyediakan metode baru dengan logika tersembunyi kepada klien:

  • onAddViewHolder(RecyclerView.ViewHolder holder, int position),
  • onCreateViewHolder(ViewGroup parent),
  • itemCount()

Inilah HeadingableRecycleAdapterkelas dan klien. Saya meninggalkan tata letak tajuk agak keras karena sesuai dengan kebutuhan saya.

public abstract class HeadingableRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int HEADER_VIEW_TYPE = 0;

    @LayoutRes
    private int headerLayoutResource;
    private String headerTitle;
    private Context context;

    public HeadingableRecycleAdapter(@LayoutRes int headerLayoutResourceId, String headerTitle, Context context) {
        this.headerLayoutResource = headerLayoutResourceId;
        this.headerTitle = headerTitle;
        this.context = context;
    }

    public Context context() {
        return context;
    }

    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == HEADER_VIEW_TYPE) {
            return new HeaderViewHolder(LayoutInflater.from(context).inflate(headerLayoutResource, parent, false));
        }
        return onCreateViewHolder(parent);
    }

    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        if (viewType == HEADER_VIEW_TYPE) {
            HeaderViewHolder vh = (HeaderViewHolder) holder;
            vh.bind(headerTitle);
        } else {
            onAddViewHolder(holder, position - 1);
        }
    }

    @Override
    public final int getItemViewType(int position) {
        return position == 0 ? 0 : 1;
    }

    @Override
    public final int getItemCount() {
        return itemCount() + 1;
    }

    public abstract int itemCount();

    public abstract RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);

    public abstract void onAddViewHolder(RecyclerView.ViewHolder holder, int position);

}



@PerActivity
public class IngredientsAdapter extends HeadingableRecycleAdapter {
    public static final String TITLE = "Ingredients";
    private List<Ingredient> itemList;


    @Inject
    public IngredientsAdapter(Context context) {
        super(R.layout.layout_generic_recyclerview_cardified_header, TITLE, context);
    }

    public void setItemList(List<Ingredient> itemList) {
        this.itemList = itemList;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
        return new ViewHolder(LayoutInflater.from(context()).inflate(R.layout.item_ingredient, parent, false));
    }

    @Override
    public void onAddViewHolder(RecyclerView.ViewHolder holder, int position) {
        ViewHolder vh = (ViewHolder) holder;
        vh.bind(itemList.get(position));
    }

    @Override
    public int itemCount() {
        return itemList == null ? 0 : itemList.size();
    }

    private String getQuantityFormated(double quantity, String measure) {
        if (quantity == (long) quantity) {
            return String.format(Locale.US, "%s %s", String.valueOf(quantity), measure);
        } else {
            return String.format(Locale.US, "%.1f %s", quantity, measure);
        }
    }


    class ViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.text_ingredient)
        TextView txtIngredient;

        ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

        void bind(Ingredient ingredient) {
            String ingredientText = ingredient.getIngredient();
            txtIngredient.setText(String.format(Locale.US, "%s %s ", getQuantityFormated(ingredient.getQuantity(),
                    ingredient.getMeasure()), Character.toUpperCase(ingredientText.charAt(0)) +
                    ingredientText
                            .substring(1)));
        }
    }
}
alexpfx
sumber
1

Mungkin membungkus header dan recyclerview ke dalam koordinatorlayout :

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:elevation="0dp">

    <View
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll" />

</android.support.design.widget.AppBarLayout>

<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

lenhuy2106
sumber
Masalahnya adalah ini akan selalu memungkinkan pengguliran untuk ketinggian AppBarLayout, bahkan jika daftar lebih pendek dari layar. Untuk memperbaikinya, Anda dapat melakukan seperti yang dijelaskan di sini
Daniel López Lacalle
0

Mungkin http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ akan membantu. Hanya menggunakan RecyclerView dan CardView. Ini adaptornya:

public class DifferentRowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<CityEvent> mList;
    public DifferentRowAdapter(List<CityEvent> list) {
        this.mList = list;
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        switch (viewType) {
            case CITY_TYPE:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_city, parent, false);
                return new CityViewHolder(view);
            case EVENT_TYPE:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_event, parent, false);
                return new EventViewHolder(view);
        }
        return null;
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        CityEvent object = mList.get(position);
        if (object != null) {
            switch (object.getType()) {
                case CITY_TYPE:
                    ((CityViewHolder) holder).mTitle.setText(object.getName());
                    break;
                case EVENT_TYPE:
                    ((EventViewHolder) holder).mTitle.setText(object.getName());
                    ((EventViewHolder) holder).mDescription.setText(object.getDescription());
                    break;
            }
        }
    }
    @Override
    public int getItemCount() {
        if (mList == null)
            return 0;
        return mList.size();
    }
    @Override
    public int getItemViewType(int position) {
        if (mList != null) {
            CityEvent object = mList.get(position);
            if (object != null) {
                return object.getType();
            }
        }
        return 0;
    }
    public static class CityViewHolder extends RecyclerView.ViewHolder {
        private TextView mTitle;
        public CityViewHolder(View itemView) {
            super(itemView);
            mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
        }
    }
    public static class EventViewHolder extends RecyclerView.ViewHolder {
        private TextView mTitle;
        private TextView mDescription;
        public EventViewHolder(View itemView) {
            super(itemView);
            mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
            mDescription = (TextView) itemView.findViewById(R.id.descriptionTextView);
        }
    }
}

Dan inilah entitas:

public class CityEvent {
    public static final int CITY_TYPE = 0;
    public static final int EVENT_TYPE = 1;
    private String mName;
    private String mDescription;
    private int mType;
    public CityEvent(String name, String description, int type) {
        this.mName = name;
        this.mDescription = description;
        this.mType = type;
    }
    public String getName() {
        return mName;
    }
    public void setName(String name) {
        this.mName = name;
    }
    public String getDescription() {
        return mDescription;
    }
    public void setDescription(String description) {
        this.mDescription = description;
    }
    public int getType() {
        return mType;
    }
    public void setType(int type) {
        this.mType = type;
    }
}
CoolMind
sumber
0

Anda bisa membuat addHeaderView dan menggunakannya

adapter.addHeaderView(View).

Kode ini membangun addHeaderViewlebih dari satu tajuk. header harus memiliki:

android:layout_height="wrap_content"

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_ITEM = -1;
    public class MyViewSHolder extends RecyclerView.ViewHolder {
        public MyViewSHolder (View view) {
            super(view);
        }
        // put you code. for example:
        View mView;
        ...
    }

    public class ViewHeader extends RecyclerView.ViewHolder {
        public ViewHeader(View view) {
            super(view);
        }
    }

    private List<View> mHeaderViews = new ArrayList<>();
    public void addHeaderView(View headerView) {
        mHeaderViews.add(headerView);
    }

    @Override
    public int getItemCount() {
       return ... + mHeaderViews.size();
    }

    @Override
    public int getItemViewType(int position) {
        if (mHeaderViews.size() > position) {
            return position;
        }

        return TYPE_ITEM;
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType != TYPE_ITEM) {
            //inflate your layout and pass it to view holder
            return new ViewHeader(mHeaderViews.get(viewType));
        }
        ...
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int basePosition1) {
        if (holder instanceof ViewHeader) {
            return;
        }
        int basePosition = basePosition1 -  mHeaderViews.size();
        ...
    }
}
buka barshem
sumber
0

Sudah beberapa tahun, tapi siapa tahu ada yang membaca ini nanti ...

Menggunakan kode di atas, hanya tata letak tajuk yang ditampilkan karena tipe tampilan selalu 0.

Masalahnya adalah dalam deklarasi konstan:

private static final int HEADER = 0;
private static final int OTHER = 0;  <== bug 

Jika Anda mendeklarasikan keduanya sebagai nol, maka Anda akan selalu mendapatkan nol!

Kiwi
sumber
0

Saya telah menerapkan pendekatan yang sama yang diusulkan oleh jawaban EC84B4 , tapi saya mengabstraksi RecycleViewAdapter dan membuatnya mudah diresapi melalui antarmuka.

Jadi, untuk menggunakan pendekatan saya, Anda harus menambahkan kelas dasar dan antarmuka berikut ke proyek Anda:

1) Antarmuka yang menyediakan data untuk Adaptor (kumpulan tipe T generik, dan parameter tambahan (jika perlu) tipe P generik)

public interface IRecycleViewListHolder<T,P>{
            P getAdapterParameters();
            T getItem(int position);
            int getSize();
    }

2) Pabrik untuk mengikat item Anda (header / item):

public interface IViewHolderBinderFactory<T,P> {
        void bindView(RecyclerView.ViewHolder holder, int position,IRecycleViewListHolder<T,P> dataHolder);
}

3) Pabrik untuk viewHolders (header / item):

public interface IViewHolderFactory {
    RecyclerView.ViewHolder provideInflatedViewHolder(int viewType, LayoutInflater layoutInflater,@NonNull ViewGroup parent);
}

4) Kelas dasar untuk Adaptor dengan Header:

public class RecycleViewHeaderBased<T,P> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    public final static int HEADER_TYPE = 1;
    public final static int ITEM_TYPE = 0;
    private final IRecycleViewListHolder<T, P> dataHolder;
    private final IViewHolderBinderFactory<T,P> binderFactory;
    private final IViewHolderFactory viewHolderFactory;

    public RecycleViewHeaderBased(IRecycleViewListHolder<T,P> dataHolder, IViewHolderBinderFactory<T,P> binderFactory, IViewHolderFactory viewHolderFactory) {
        this.dataHolder = dataHolder;
        this.binderFactory = binderFactory;
        this.viewHolderFactory = viewHolderFactory;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        return viewHolderFactory.provideInflatedViewHolder(viewType,layoutInflater,parent);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            binderFactory.bindView(holder, position,dataHolder);
    }

    @Override
    public int getItemViewType(int position) {
        if(position == 0)
            return HEADER_TYPE;
        return ITEM_TYPE;
    }

    @Override
    public int getItemCount() {
        return dataHolder.getSize()+1;
    }
}

Contoh penggunaan :

1) Implementasi IRecycleViewListHolder:

public class AssetTaskListData implements IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> {
    private List<Map.Entry<Integer, Integer>> assetCountList;
    private GroupedRecord record;

    public AssetTaskListData(Map<Integer, Integer> assetCountListSrc, GroupedRecord record) {
        this.assetCountList =  new ArrayList<>();
        for(Object  entry: assetCountListSrc.entrySet().toArray()){
            Map.Entry<Integer,Integer> entryTyped = (Map.Entry<Integer,Integer>)entry;
            assetCountList.add(entryTyped);
        }
        this.record = record;
    }

    @Override
    public GroupedRecord getAdapterParameters() {
        return record;
    }

    @Override
    public Map.Entry<Integer, Integer> getItem(int position) {
        return assetCountList.get(position-1);
    }

    @Override
    public int getSize() {
        return assetCountList.size();
    }
}

2) Implementasi IViewHolderBinderFactory:

public class AssetTaskListBinderFactory implements IViewHolderBinderFactory<Map.Entry<Integer, Integer>, GroupedRecord> {
    @Override
    public void bindView(RecyclerView.ViewHolder holder, int position, IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> dataHolder) {
        if (holder instanceof AssetItemViewHolder) {
            Integer assetId = dataHolder.getItem(position).getKey();
            Integer assetCount = dataHolder.getItem(position).getValue();
            ((AssetItemViewHolder) holder).bindItem(dataHolder.getAdapterParameters().getRecordId(), assetId, assetCount);
        } else {
            ((AssetHeaderViewHolder) holder).bindItem(dataHolder.getAdapterParameters());
        }
    }
}

3) Implementasi IViewHolderFactory:

public class AssetTaskListViewHolderFactory implements IViewHolderFactory {
    private IPropertyTypeIconMapper iconMapper;
    private ITypeCaster caster;

    public AssetTaskListViewHolderFactory(IPropertyTypeIconMapper iconMapper, ITypeCaster caster) {
        this.iconMapper = iconMapper;
        this.caster = caster;
    }

    @Override
    public RecyclerView.ViewHolder provideInflatedViewHolder(int viewType, LayoutInflater layoutInflater, @NonNull ViewGroup parent) {
        if (viewType == RecycleViewHeaderBased.HEADER_TYPE) {
            AssetBasedHeaderItemBinding item = DataBindingUtil.inflate(layoutInflater, R.layout.asset_based_header_item, parent, false);
            return new AssetHeaderViewHolder(item.getRoot(), item, caster);
        }
        AssetBasedListItemBinding item = DataBindingUtil.inflate(layoutInflater, R.layout.asset_based_list_item, parent, false);
        return new AssetItemViewHolder(item.getRoot(), item, iconMapper, parent.getContext());
    }
}

4) Memperoleh adaptor

public class AssetHeaderTaskListAdapter extends RecycleViewHeaderBased<Map.Entry<Integer, Integer>, GroupedRecord> {
   public AssetHeaderTaskListAdapter(IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> dataHolder,
                                      IViewHolderBinderFactory binderFactory,
                                      IViewHolderFactory viewHolderFactory) {
        super(dataHolder, binderFactory, viewHolderFactory);
    }
}

5) Instantiate kelas adaptor:

private void setUpAdapter() {
        Map<Integer, Integer> objectTypesCountForGroupedTask = groupedTaskRepository.getObjectTypesCountForGroupedTask(this.groupedRecordId);
        AssetTaskListData assetTaskListData = new AssetTaskListData(objectTypesCountForGroupedTask, getGroupedRecord());
        adapter = new AssetHeaderTaskListAdapter(assetTaskListData,new AssetTaskListBinderFactory(),new AssetTaskListViewHolderFactory(iconMapper,caster));
        assetTaskListRecycler.setAdapter(adapter);
    }

PS : AssetItemViewHolder, AssetBasedListItemBinding, dll. Struktur aplikasi saya sendiri yang harus ditukar oleh Anda sendiri, untuk keperluan Anda sendiri.

Sergei Yendiyarov
sumber