มี addHeaderView ที่เทียบเท่าสำหรับ RecyclerView หรือไม่


290

ฉันกำลังมองหาสิ่งที่เทียบเท่ากับ addHeaderView สำหรับมุมมองรีไซเคิล โดยทั่วไปฉันต้องการให้ภาพที่มี 2 ปุ่มเพิ่มเป็นส่วนหัวของ listview มีวิธีอื่นในการเพิ่มมุมมองส่วนหัวในมุมมองรีไซเคิลหรือไม่ ตัวอย่างคำแนะนำจะเป็นประโยชน์

แก้ไข 2 (เพิ่มโครงร่างชิ้นส่วน):

หลังจากเพิ่มคำสั่งบันทึกดูเหมือนว่า getViewType จะได้รับตำแหน่งเป็น 0 เท่านั้นสิ่งนี้นำไปสู่ ​​onCreateView เพียงแค่โหลดหนึ่งเลย์เอาต์:

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

การแตกแฟรกเมนต์เพื่อโหลด 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();
    }
}

The Fragment's onCreateView:

@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;
}

ส่วนที่มีการรีไซเคิลมุมมอง:

<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>

เค้าโครงแถวความคิดเห็น:

<?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>

ส่วนหัว

<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>

รหัสอะแดปเตอร์ (ขอบคุณ hister ที่ให้ฉันเริ่มต้น):

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);
        }
    }
}

ใช้รหัสด้านบนเฉพาะเค้าโครงส่วนหัวเท่านั้นที่จะแสดงเป็น viewType เสมอ 0 ดูเหมือนว่า นี้ ถ้าฉันบังคับรูปแบบอื่น ๆ ก็มีลักษณะเช่นนี้ :



เช่นนี้เป็นซ้ำของคำถามที่นี่ผมโพสต์คำตอบของฉันมี :
seb

ทางออกที่สง่างาม: stackoverflow.com/questions/33579800/…
Ignacio Hagopian

คำตอบ:


457

ไม่มีวิธีง่าย ๆ เช่น listview.addHeaderView()แต่คุณสามารถทำได้โดยเพิ่มชนิดลงในอะแดปเตอร์ของคุณสำหรับส่วนหัว

นี่คือตัวอย่าง

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);
        }
    }
}

ลิงก์ไปยังส่วนสำคัญ -> ที่นี่


2
ทุกอย่างดูเหมือนจะโอเคและควรใช้งานได้ แต่ให้มุมมองนักรีไซเคิล MATCH_PARENT ดูว่ามีอะไรเปลี่ยนแปลงหรือไม่ ถ้าเป็นไปได้ให้ผู้รีไซเคิลดูรูทของเลย์เอาต์นั้น (มันดีต่อประสิทธิภาพ)
EC84B4

1
ดวงอาทิตย์ของปืนทำความสะอาด recyclerview และเพิ่มการจับคู่แม่ทำงาน .... ขอบคุณมากสำหรับความช่วยเหลือ! ฉันจะลงคะแนนให้คุณโดยเร็วที่สุด
ViciDroid

14
private String getItem(int position) { return data[position + 1]; }ทำให้เกิด NPE เนื่องจากตำแหน่งของเราเพิ่มขึ้นหนึ่งตำแหน่งเนื่องจากส่วนหัวเราต้องลบ 1 เพื่อรับรายการที่ถูกต้องจากข้อมูลของเรา [] private String getItem(int position) { return data[position - 1]; }
Tim Malseed

2
หากกริดของคุณเป็นเพียงกริดธรรมดาเพียงใช้ LinearLayoutManager และแสดงไอเท็มในหนึ่งแถว แถวที่เรียกว่าถังข้อมูลฉันเคยเห็น google ใช้ในบางแอพ
EC84B4

4
@nsL คุณสามารถใช้วิธีการนี้และยังเพิ่มsetSpanSizeLookupสำหรับGridLayoutManagerดังนั้นส่วนหัวของคุณจะใช้เวลาทั้งหมดของคอลัมน์
Dmitry Zaytsev

62

ง่ายและนำมาใช้ใหม่ ItemDecoration

ส่วนหัวแบบคงที่สามารถเพิ่มได้อย่างง่ายดายด้วยItemDecorationและไม่มีการเปลี่ยนแปลงเพิ่มเติมใด ๆ

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

การตกแต่งยังสามารถใช้ซ้ำได้เนื่องจากไม่จำเป็นต้องดัดแปลงอะแดปเตอร์หรือเปลี่ยนRecyclerViewเลย

โค้ดตัวอย่างที่ให้ไว้ด้านล่างจะต้องใช้มุมมองเพื่อเพิ่มไปยังด้านบนซึ่งสามารถพองเหมือนทุกอย่างอื่น มันอาจมีลักษณะเช่นนี้:

ตัวอย่าง HeaderDecoration

ทำไมถึงคงที่ ?

หากคุณเพียงแค่ต้องแสดงข้อความและรูปภาพโซลูชันนี้มีไว้สำหรับคุณ - ไม่มีความเป็นไปได้สำหรับการโต้ตอบกับผู้ใช้เช่นปุ่มหรือดูเพจเจอร์เนื่องจากมันจะถูกดึงไปด้านบนของรายการของคุณ

จัดการรายการว่างเปล่า

หากไม่มีมุมมองในการตกแต่งการตกแต่งจะไม่ถูกดึงออกมา คุณยังจะต้องจัดการรายการว่างเปล่าด้วยตัวเอง (วิธีแก้ปัญหาหนึ่งที่เป็นไปได้คือการเพิ่มรายการจำลองไปยังอะแดปเตอร์)

รหัส

คุณสามารถค้นหาซอร์สโค้ดแบบเต็มได้ที่นี่ใน GitHubรวมถึงBuilderเพื่อช่วยในการเริ่มต้นมัณฑนากรหรือเพียงแค่ใช้โค้ดด้านล่างและส่งค่าของคุณเองไปที่คอนสตรัคเตอร์

โปรดตรวจสอบให้แน่ใจว่าได้ตั้งค่าที่ถูกต้องlayout_heightสำหรับมุมมองของคุณ เช่นmatch_parentอาจทำงานไม่ถูกต้อง

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();
        }
    }
}

โปรดทราบ:โครงการ GitHub เป็นสนามเด็กเล่นส่วนตัวของฉัน มันไม่ได้ผ่านการทดสอบอย่างละเอียดซึ่งเป็นเหตุผลที่มีห้องสมุดไม่มีเลย

มันทำอะไร?

An ItemDecorationเป็นการวาดรูปเพิ่มเติมสำหรับรายการในรายการ ในกรณีนี้การตกแต่งจะถูกวาดไปที่ด้านบนของรายการแรก

มุมมองได้รับการวัดและจัดวางจากนั้นจะถูกดึงไปที่ด้านบนของรายการแรก หากมีการเพิ่มเอฟเฟกต์พารัลแลกซ์ก็จะถูกตัดไปที่ขอบเขตที่ถูกต้อง


1
ดูเป็นโซลูชันที่สวยงาม แต่ฉันมีคำถาม: เมื่อใดที่ฉันควรเรียก recyclerView.addItemDecoration
Weibo

1
ขอบคุณมันใช้งานได้ดี! ข้อเสียเพียงอย่างเดียวของการใช้วิธีนี้คือคุณต้องมีอย่างน้อย 1 แถวใน RecyclerView
Philip Giuliani

3
วิธีการ onClickListener สำหรับส่วนหัว?
Prashant Kedia

1
ฉันได้รับข้อยกเว้นดังต่อไปนี้: java.lang.NullPointerException: ความพยายามที่จะเรียกใช้วิธีเสมือน 'บูลีน android.support.v7.widget.RecyclerView $ ViewHolder.shouldIgnore ()' ในการอ้างอิงวัตถุโมฆะ
Makalele

1
มีวิธีเข้าถึงมุมมองในการตกแต่งไหม? ฉันต้องการตั้งค่าข้อความแบบไดนามิก
SMahdiS

44

อย่าลังเลที่จะใช้ห้องสมุดของฉันที่นี่ที่นี่

มันช่วยให้คุณสร้างส่วนหัวViewสำหรับสิ่งRecyclerViewที่ใช้LinearLayoutManagerหรือGridLayoutManagerเพียงแค่เรียกเมธอดอย่างง่าย

ป้อนคำอธิบายรูปภาพที่นี่


วิธีการเปลี่ยนความสูงของส่วนหัว?
ingsaurabh

ฉันใช้สิ่งนี้เพื่อแสดงส่วนหัวที่ด้านล่างหรือคุณสามารถพูดได้ว่าเป็นส่วนท้าย แต่ฉันได้รับปัญหาว่าเมื่อโหลดการดูครั้งแรกมันแสดงตำแหน่งสุดท้ายและรายการทั้งหมดที่แสดงในลำดับย้อนกลับ @blipinsk
Ronak Joshi

คุณคิดว่าปัญหาที่คุณอธิบายอาจเกี่ยวข้องกับสิ่งนี้: github.com/blipinsk/RecyclerViewHeader/issues/16 ?
Bartek Lipinski

Lipinski ออกจากห้องสมุดนี้แล้วและแนะนำให้ใช้สิ่งนี้แทน: github.com/Karumi/HeaderRecyclerView
radley

1
ฉันจะเลิกห้องสมุดนี้เพื่อให้แน่นอน ฉันยังคงให้การสนับสนุนเล็กน้อย แต่จริงๆแล้วฉันขอแนะนำให้ใช้อะแดปเตอร์พิเศษแทน (เช่นจากคารุมิ)
Bartek Lipinski

31

จะแสดงให้คุณสร้างหัวข้อพร้อมรายการในมุมมอง Recyclerมุมมองรีไซเลอร์พร้อมส่วนหัว

ขั้นตอนที่ 1- เพิ่มการพึ่งพาลงในไฟล์ gradle ของคุณ

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

Cardview ใช้สำหรับการตกแต่ง

Step2- สร้างสามไฟล์ xml หนึ่งสำหรับกิจกรรมหลักสองสำหรับเค้าโครงส่วนหัวสามสำหรับเค้าโครงรายการ

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>

ขั้นตอนที่ 3 - สร้างคลาสถั่วสามคลาส

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;
    }
}

ขั้นตอนที่ 4- สร้างอะแดปเตอร์ชื่อ 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);
        }
    }
}

ขั้นตอนที่ 5 ใน MainActivity เพิ่มรหัสต่อไปนี้:

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;
    }

}

ฟังก์ชัน getList () กำลังสร้างข้อมูลแบบไดนามิกสำหรับส่วนหัวและสำหรับรายการ


หนึ่งในคำตอบที่ดีที่สุด ฉันใช้มันใน navigationView สำหรับเมนูการนำทาง
Saurabh Bhandari

นี่เป็นคำตอบที่ง่ายมากฉันไม่สามารถนึกถึงกลยุทธิ์ในการรวม 2 รายการข้อมูลไว้ใน ListItem เดียว - การสืบทอดทำให้มันง่ายและทำได้ - duh!
yura

8

คุณสามารถทำได้โดยใช้ห้องสมุดSectionedRecyclerViewAdapterมันมีแนวคิดของ "ส่วน" ซึ่งส่วนที่มีส่วนหัว, ท้ายกระดาษและเนื้อหา (รายการของรายการ) ในกรณีของคุณคุณอาจต้องการเพียงหนึ่งส่วน แต่คุณสามารถมีได้หลายส่วน

ป้อนคำอธิบายรูปภาพที่นี่

1) สร้างคลาสส่วนที่กำหนดเอง:

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) สร้าง ViewHolder ที่กำหนดเองสำหรับรายการ:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

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

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

3) ตั้งค่า ReclyclerView ของคุณด้วย 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);

นี่ใช้งานง่าย แต่จะใช้งานอย่างไรสำหรับการเลื่อนแนวนอน .. เมื่อใดก็ตามที่ฉันเปลี่ยนการวางแนวเป็นแนวนอนแล้ว recyclerview ทั้งหมดจะเปลี่ยนเป็นแนวนอน แต่ฉันต้องการให้ส่วนของรายการเป็นแนวนอนเท่านั้น .. โปรดช่วยฉันออกไปทำ
roshan posakya

6

คุณสามารถวางส่วนหัวและ RecyclerView ของคุณใน 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>

เพื่อให้การเลื่อนทำงานอย่างถูกต้องคุณต้องปิดใช้งานการเลื่อนแบบซ้อนบน RecyclerView ของคุณ:

myRecyclerView.setNestedScrollingEnabled(false);

3
หรือใช้ android: nestedScrollingEnabled = "false"
radley

android: nestedScrollingEnabled = "false" ต้องใช้ api ระดับ 21 ซึ่ง sucks อย่างไรก็ตามทางออกที่ดี! เช่นเดียวกันคุณต้องเพิ่มส่วนท้ายอีกหรืออาจ recyclerview ที่สอง?
Makalele

47
ไม่เคยทำเช่นนี้ ดูเหมือนว่าจะทำงานได้ดี แต่สิ่งที่จริง ๆ แล้วคือมันผ่านการเลื่อนเพื่อ srollView ดังนั้น recyclerView จะไม่ทำอะไรด้วยตัวเอง ไม่มีมุมมองการรีไซเคิล แอปจะหยุดทำงานหากคุณได้รับรายการมากเกินไปใน recyclerView ของคุณเพราะตอนนี้มันทำหน้าที่เหมือน scrollView แบบธรรมดา
VipulKumar

2
@ ความคิดเห็นของ VipulKumar นั้นเป็นความจริงโดยสมบูรณ์ เมื่อคุณใช้ recyclerview ภายใน scrollview อื่นจะไม่มีการรีไซเคิลและมันจะสร้างรายการทั้งหมด
VolkanSahin45

คุณสามารถทำได้ถ้ารายการของคุณใน recyclerView น้อยกว่า 10 😄
JIE WANG

5

Native API ไม่มีคุณลักษณะ "addHeader" แต่มีแนวคิดของ "addItem"

ฉันสามารถรวมคุณลักษณะเฉพาะของส่วนหัวและขยายสำหรับส่วนท้ายได้เช่นกันในโครงการFlexibleAdapterของฉัน ฉันเรียกมันว่าส่วนหัวและท้ายกระดาษแบบเลื่อนได้ได้

ที่นี่วิธีการทำงาน:

ส่วนหัวและท้ายกระดาษที่เลื่อนได้เป็นรายการพิเศษที่เลื่อนไปพร้อมกับคนอื่น ๆ ทั้งหมด แต่พวกเขาไม่ได้เป็นของรายการหลัก (รายการธุรกิจ) และอะแดปเตอร์เหล่านั้นจะถูกจัดการโดยด้านข้างของรายการหลัก รายการเหล่านั้นจะอยู่อย่างต่อเนื่องที่ตำแหน่งแรกและสุดท้าย

ป้อนคำอธิบายรูปภาพที่นี่

มีจำนวนมากที่จะพูดเกี่ยวกับพวกเขาดีกว่าที่จะอ่านรายละเอียดเป็นหน้าวิกิพีเดีย

นอกจากนี้ FlexibleAdapter ช่วยให้คุณสามารถสร้างส่วนหัว / ส่วนคุณยังสามารถมีได้ เหนียวและคุณสมบัติอื่น ๆ เช่นรายการขยายขยายเลื่อนไม่มีที่สิ้นสุดส่วนขยาย UI ฯลฯ ... ทั้งหมดในห้องสมุดเดียว!


4

อ้างอิงจากโพสต์นี้ฉันได้สร้างคลาสย่อยของ RecyclerView.Adapter ที่รองรับจำนวนส่วนหัวและส่วนท้ายโดยพลการ

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

แม้ว่ามันจะเป็นทางออก แต่ฉันคิดว่าสิ่งนี้ควรได้รับการจัดการโดย LayoutManager แต่น่าเสียดายที่ฉันต้องการตอนนี้และฉันไม่มีเวลาที่จะใช้ StaggeredGridLayoutManager ตั้งแต่เริ่มต้น

ฉันยังคงทดสอบมันอยู่ แต่คุณสามารถลองใช้ได้ถ้าคุณต้องการ โปรดแจ้งให้เราทราบหากคุณพบปัญหาใด ๆ กับมัน


2

มีอีกหนึ่งโซลูชันที่ครอบคลุมกรณีการใช้งานทั้งหมดด้านบน: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

คุณสามารถสร้าง AdapterGroup ที่มีอะแดปเตอร์ของคุณตามเดิมพร้อมกับอะแดปเตอร์ที่มีรายการเดียวเพื่อแสดงส่วนหัว รหัสนั้นง่ายและอ่านได้:

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

recyclerView.setAdapter(adapterGroup);

AdapterGroup อนุญาตให้ซ้อนเช่นกันดังนั้นสำหรับอะแดปเตอร์ที่มีส่วนต่างๆคุณสามารถสร้าง AdapterGroup ต่อส่วนได้ จากนั้นใส่หัวข้อทั้งหมดลงใน Root AdapterGroup


1

HeaderView ขึ้นอยู่กับ LayoutManager LayoutManagers ค่าเริ่มต้นไม่รองรับสิ่งนี้และอาจไม่เป็นเช่นนั้น HeaderView ใน ListView สร้างความซับซ้อนมากมายโดยไม่ได้รับประโยชน์ใด ๆ

ฉันขอแนะนำให้สร้างคลาสอะแดปเตอร์ฐานที่เพิ่มรายการสำหรับส่วนหัวถ้ามีให้ อย่าลืมแทนที่การแจ้งเตือน * วิธีการเพื่อชดเชยพวกเขาอย่างถูกต้องขึ้นอยู่กับว่าส่วนหัวมีอยู่หรือไม่


มีตัวอย่างที่คุณสามารถชี้ให้ฉันเห็นว่าใช้มุมมองรีไซเคิลจากอะแดปเตอร์ฐานหรือไม่? ขอบคุณ!
ViciDroid

มีประโยชน์สำคัญอย่างหนึ่งสำหรับส่วนหัว / ท้ายกระดาษในรายการ: คุณสามารถเลื่อนออกจากมุมมอง ดูตัวอย่างนี้เมื่อจำนวนแถวที่มองเห็นเกือบสองเท่าเมื่อ pinguins หมดฉันไม่ทราบวิธีอื่นในการทำเช่นนี้ แต่ListView.addHeaderViewหรือคำตอบสำหรับคำถามนี้
TWiStErRob

ฉันไม่คิดว่าฉันพูดถูก หากเป็นรายการแรกในอะแดปเตอร์เหตุใดจึงไม่เลื่อนเหมือนรายการในตัวอย่าง
yigit

2
คุณไม่สามารถแทนที่เมธอดการแจ้งเตือน * เนื่องจากถูกทำเครื่องหมายเป็นขั้นสุดท้าย
mato

อืมฉันไม่ได้ตรวจสอบว่าขอโทษ แต่คุณสามารถสร้าง wrapper ของอะแดปเตอร์ซึ่งจะเพิ่มสิ่งที่สังเกตได้ให้กับอะแดปเตอร์ที่ถูกห่อและส่งเหตุการณ์ที่เปลี่ยนจากตัวมันเอง
yigit

1
First - extends RecyclerView.Adapter<RecyclerView.ViewHolder>

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

After - แทนที่เมธอด getItemViewTpe *** สำคัญกว่า

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

วิธีการในการสร้าง ViewHolder

@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);
    }
}

วิธีการใน BindViewHolder

@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]);
    }
}

เมื่อเสร็จสิ้นการดำเนินการคงที่ระดับ ViewHolders

public static class MenuViewHolder extends RecyclerView.ViewHolder 

public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 

1

ที่นี่การตกแต่งบางรายการสำหรับ 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();
    }
}
}      

1

ฉันใช้งานตาม@ hister'sเพื่อวัตถุประสงค์ส่วนตัวของฉัน แต่ใช้การสืบทอด

ฉันซ่อนรายละเอียดการดำเนินกลไก (เช่นเพิ่ม 1 ถึงitemCountลบจาก 1 position) ในชั้นซุปเปอร์นามธรรมHeadingableRecycleAdapterโดยการใช้วิธีการที่จำเป็นจากอะแดปเตอร์เหมือนonBindViewHolder, getItemViewTypeและgetItemCountทำให้ว่าวิธีสุดท้ายและวิธีการใหม่ให้กับตรรกะที่ซ่อนอยู่ให้กับลูกค้า:

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

นี่คือHeadingableRecycleAdapterคลาสและไคลเอนต์ ฉันออกจากส่วนหัวเค้าโครงบิตยากเพราะมันเหมาะกับความต้องการของฉัน

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)));
        }
    }
}

1

อาจจะตัดส่วนหัวและ recyclerview เป็นผู้ประสานงานออก :

<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" />


ปัญหาเกี่ยวกับสิ่งนี้คือมันจะอนุญาตให้เลื่อนความสูงของ AppBarLayout เสมอแม้ว่ารายการจะสั้นกว่าหน้าจอก็ตาม เพื่อแก้ไขว่าคุณสามารถทำตามที่อธิบายไว้ที่นี่
Daniel López Lacalle

0

อาจเป็นhttp://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/จะช่วย ใช้เฉพาะ RecyclerView และ CardView นี่คืออะแดปเตอร์:

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);
        }
    }
}

และนี่คือเอนทิตี:

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;
    }
}

0

คุณสามารถสร้าง addHeaderView และใช้งานได้

adapter.addHeaderView(View).

รหัสนี้สร้างaddHeaderViewสำหรับส่วนหัวมากกว่าหนึ่ง ส่วนหัวควรมี:

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();
        ...
    }
}

0

ไม่กี่ปีที่ผ่านมา แต่ในกรณีที่ทุกคนอ่านในภายหลัง ...

ใช้รหัสด้านบนเฉพาะเค้าโครงส่วนหัวเท่านั้นที่จะแสดงเป็น viewType ที่เป็น 0 เสมอ

ปัญหาอยู่ในการประกาศอย่างต่อเนื่อง:

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

หากคุณประกาศให้ทั้งคู่เป็นศูนย์คุณจะได้ศูนย์เสมอ!


0

ฉันใช้วิธีการเดียวกันกับที่เสนอโดยEC84B4คำตอบแต่ฉันสรุป RecycleViewAdapter และทำให้สามารถ resuable ได้อย่างง่ายดายโดยใช้อินเทอร์เฟซ

ดังนั้นเพื่อใช้แนวทางของฉันคุณควรเพิ่มคลาสพื้นฐานและอินเทอร์เฟซต่อไปนี้ในโครงการของคุณ:

1) ส่วนต่อประสานที่ให้ข้อมูลสำหรับอะแดปเตอร์ (การรวบรวมประเภท T ทั่วไปและพารามิเตอร์เพิ่มเติม (ถ้าจำเป็น) ประเภททั่วไป P)

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

2) โรงงานสำหรับผูกรายการของคุณ (ส่วนหัว / รายการ):

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

3) โรงงานสำหรับ viewHolders (header / items):

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

4) คลาสฐานสำหรับอะแด็ปเตอร์ที่มีส่วนหัว:

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;
    }
}

ตัวอย่างการใช้งาน :

1) การใช้ 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) การใช้ 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) การใช้ 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) อะแดปเตอร์ที่ได้รับ

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) ยกระดับอะแดปเตอร์:

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 ฯลฯ โครงสร้างแอปพลิเคชันของฉันเองที่ควรเปลี่ยนด้วยตัวคุณเองเพื่อวัตถุประสงค์ของคุณเอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.