Gibt es ein addHeaderView-Äquivalent für RecyclerView?

290

Ich suche nach einem Äquivalent zu addHeaderView für eine Recycler-Ansicht. Grundsätzlich möchte ich, dass ein Bild mit 2 Schaltflächen als Kopfzeile zur Listenansicht hinzugefügt wird. Gibt es eine andere Möglichkeit, einer Recycler-Ansicht eine Header-Ansicht hinzuzufügen? Ein Beispiel für eine Anleitung wäre hilfreich

EDIT 2 (Fragmentlayouts hinzugefügt):

Nach dem Hinzufügen von Protokollanweisungen scheint getViewType immer nur die Position 0 zu erhalten. Dies führt dazu, dass onCreateView nur das eine Layout lädt:

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

Der Fragmentübergang zum Laden des CommentFragments:

@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 des Fragments:

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

Das Fragment mit der Recyclingansicht:

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

Das Zeilenlayout der Kommentare:

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

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

Der Adaptercode (danke an hister für den Einstieg):

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

Mit dem obigen Code wird nur das Header-Layout angezeigt, da viewType immer 0 ist. Es sieht so aus . Wenn ich das andere Layout erzwingen sieht es aus wie diese :

ViciDroid
quelle
Mögliches Duplikat von Android 5.0 - Kopf- / Fußzeile zu einem RecyclerView
hinzufügen
Da es sich um ein Duplikat der Frage ist hier , posted ich meine Antwort gibt :
seb
Eine elegante Lösung: stackoverflow.com/questions/33579800/…
Ignacio Hagopian

Antworten:

457

Es gibt keinen einfachen Weg, listview.addHeaderView()aber Sie können dies erreichen, indem Sie Ihrem Adapter einen Typ für den Header hinzufügen.

Hier ist ein Beispiel

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

Link zum Kern -> hier

EC84B4
quelle
2
Alles scheint in Ordnung zu sein und es sollte funktionieren, aber lassen Sie die Recycler-Ansicht MATCH_PARENT sehen, ob sich etwas ändert. Auch wenn dies möglich ist, sollte der Recycler die Wurzel dieses Layouts anzeigen (dies ist gut für die Leistung).
EC84B4
1
Die Sonne einer Waffe, das Aufräumen des Recyclerview und das Hinzufügen eines Match-Elternteils haben funktioniert ... Vielen Dank für die Hilfe! Ich werde dich so schnell wie möglich unterstützen.
ViciDroid
14
private String getItem(int position) { return data[position + 1]; }Verursacht eine NPE. Da unsere Position aufgrund des Headers um eins erhöht wurde, müssen wir 1 subtrahieren, um das richtige Element aus unseren Daten zu erhalten []. private String getItem(int position) { return data[position - 1]; }
Tim Malseed
2
Wenn Ihr Raster nur ein einfaches Raster ist, verwenden Sie einfach LinearLayoutManager und zeigen Sie es dem Element in einer Zeile an. Ich habe gesehen, dass Google es in einigen seiner Apps verwendet.
EC84B4
4
@nsL können Sie diesen Ansatz verwenden und zusätzlich setSpanSizeLookupfür hinzufügen GridLayoutManager, so dass Ihr Header alle Spalten nimmt
Dmitry Zaytsev
62

Einfach und wiederverwendbar ItemDecoration

Statische Header können einfach mit ItemDecorationund ohne weitere Änderungen hinzugefügt werden .

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

Die Dekoration ist auch wiederverwendbar, da weder der Adapter noch der Adapter geändert werden müssen RecyclerView.

Für den unten angegebenen Beispielcode muss eine Ansicht oben hinzugefügt werden, die wie alles andere einfach aufgeblasen werden kann. Es kann so aussehen:

HeaderDecoration-Beispiel

Warum statisch ?

Wenn Sie nur Text und Bilder anzeigen müssen, ist diese Lösung genau das Richtige für Sie. Es gibt keine Möglichkeit für Benutzerinteraktionen wie Schaltflächen oder Pager anzeigen, da sie nur ganz oben auf Ihrer Liste angezeigt werden.

Leere Listenbehandlung

Wenn keine Ansicht zum Dekorieren vorhanden ist, wird die Dekoration nicht gezeichnet. Sie müssen immer noch selbst eine leere Liste bearbeiten. (Eine mögliche Problemumgehung besteht darin, dem Adapter ein Dummy-Element hinzuzufügen.)

Der Code

Den vollständigen Quellcode finden Sie hier auf GitHub, einschließlich a Builder, um bei der Initialisierung des Dekorators zu helfen, oder verwenden Sie einfach den folgenden Code und geben Sie Ihre eigenen Werte an den Konstruktor weiter.

Bitte stellen Sie sicher, dass Sie eine korrekte layout_heightAnsicht für Ihre Ansicht festlegen . zB match_parentfunktioniert möglicherweise nicht richtig.

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

Bitte beachten Sie: Das GitHub-Projekt ist mein persönlicher Spielplatz. Es ist nicht gründlich getestet, weshalb keine Bibliothek gibt es noch .

Was tut es?

Eine ItemDecorationzusätzliche Zeichnung zu einem Element einer Liste. In diesem Fall wird eine Dekoration oben auf den ersten Artikel gezeichnet.

Die Ansicht wird gemessen und angelegt, dann wird sie oben auf das erste Element gezeichnet. Wenn ein Parallaxeeffekt hinzugefügt wird, wird dieser ebenfalls an die richtigen Grenzen gekürzt.

David Medenjak
quelle
1
sieht nach einer eleganten Lösung aus, aber ich habe eine Frage: Wann sollte ich recyclerView.addItemDecoration aufrufen?
Weibo
1
Danke, es hat gut funktioniert! Der einzige Nachteil bei der Verwendung dieser Methode ist, dass mindestens 1 Zeile in der RecyclerView vorhanden sein muss.
Philip Giuliani
3
Wie onClickListener für den Header?
Prashant Kedia
1
Ich erhalte folgende Ausnahme: java.lang.NullPointerException: Versuch, die virtuelle Methode 'boolean android.support.v7.widget.RecyclerView $ ViewHolder.shouldIgnore ()' für eine Nullobjektreferenz
aufzurufen
1
Gibt es eine Möglichkeit, auf die Ansichten in der Dekoration zuzugreifen? Ich möchte Text dynamisch einstellen.
SMahdiS
44

Fühlen Sie sich frei, meine Bibliothek zu nutzen, die hier verfügbar ist .

Hiermit können Sie einen Header Viewfür jeden erstellen RecyclerView, der LinearLayoutManageroder GridLayoutManagernur einen einfachen Methodenaufruf verwendet.

Geben Sie hier die Bildbeschreibung ein

Bartek Lipinski
quelle
Wie ändere ich die Höhe des Headers?
Ingsaurabh
Ich habe dies verwendet, um die Kopfzeile unten anzuzeigen, oder Sie können das als Fußzeile sagen, aber ich habe das Problem, dass beim Laden meiner Ansicht zum ersten Mal die letzte Position und alle Listenelemente in umgekehrter Reihenfolge angezeigt werden. @blinsinsk
Ronak Joshi
Denken Sie, dass das von Ihnen beschriebene Problem damit zusammenhängen kann: github.com/blipinsk/RecyclerViewHeader/issues/16 ?
Bartek Lipinski
Lipinski hat diese Bibliothek eingestellt und schlägt stattdessen vor, diese zu verwenden: github.com/Karumi/HeaderRecyclerView
radley
1
Ich ziehe diese Bibliothek zurück, um genau zu sein. Ich biete immer noch eine milde Unterstützung dafür an, aber in der Tat schlage ich vor, stattdessen den speziellen Adapter zu verwenden (z. B. den von Karumi).
Bartek Lipinski
31

Zeigen Sie, wie Sie in einer Recycler-Ansicht eine Kopfzeile mit Elementen erstellen.Recycler-Ansicht mit Header

Schritt 1 - Fügen Sie Ihrer Gradle-Datei eine Abhängigkeit hinzu.

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

Cardview wird zu Dekorationszwecken verwendet.

Schritt 2: Erstellen Sie drei XML-Dateien. Eine für die Hauptaktivität. Zweitens für das Header-Layout. Drittens für das Listenelement-Layout.

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>

Schritt 3 - Erstellen Sie drei Bean-Klassen.

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

Schritt 4: Erstellen Sie einen Adapter mit dem Namen 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);
        }
    }
}

Schritt 5- Fügen Sie in MainActivity den folgenden Code hinzu:

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

}

Die Funktion getList () generiert dynamisch die Daten für die Header und für Listenelemente.

Anshul Aggarwal
quelle
Eine der besten Antworten. Ich habe es in navigationView für das Navigationsmenü verwendet
Saurabh Bhandari
Dies ist eine gute einfache Antwort - ich konnte mir keinen Trick vorstellen, um zwei Datenelemente einfach zu einem einzigen ListItem zu kombinieren - die Vererbung machte es einfach und erreichbar - duh!
Yura
8

Sie können dies mit der Bibliothek SectionedRecyclerViewAdapter erreichen . Sie hat das Konzept "Abschnitte", wobei dieser Abschnitt eine Kopfzeile, eine Fußzeile und einen Inhalt (Liste der Elemente) enthält. In Ihrem Fall benötigen Sie möglicherweise nur einen Abschnitt, aber Sie können viele haben:

Geben Sie hier die Bildbeschreibung ein

1) Erstellen Sie eine benutzerdefinierte Abschnittsklasse:

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) Erstellen Sie einen benutzerdefinierten ViewHolder für die Elemente:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

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

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

3) Richten Sie Ihre ReclyclerView mit dem SectionedRecyclerViewAdapter ein

// 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
quelle
Dies ist einfach zu bedienen, aber wie man es für horizontales Scrollen verwendet. Wenn ich die Ausrichtung auf horizontal ändere, ändert sich die gesamte Recycling-Ansicht auf horizontales Scrollen, aber ich möchte, dass der
Elementteil nur horizontales Scrollen ist
6

Sie können einfach Ihren Header und Ihre RecyclerView in einer NestedScrollView platzieren:

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

Damit das Scrollen ordnungsgemäß funktioniert, müssen Sie das verschachtelte Scrollen in Ihrem RecyclerView deaktivieren:

myRecyclerView.setNestedScrollingEnabled(false);
Cristan
quelle
3
oder verwenden Sie Android: nestedScrollingEnabled = "false"
Radley
android: nestedScrollingEnabled = "false" erfordert API-Level 21, was scheiße ist. Auf jeden Fall tolle Lösung! Das gleiche müssen Sie eine weitere Fußzeile oder vielleicht sogar eine zweite Recycling-Ansicht hinzufügen?
Makalele
47
Mach das NIEMALS. Dies scheint gut zu funktionieren. Aber was es tatsächlich tut, ist, dass es den Bildlauf an srollView übergibt. RecyclerView macht also nichts alleine. Keine Sicht Recycling. Die App stürzt ab, wenn Sie zu viele Elemente in Ihrer recyclerView haben, da sie sich jetzt wie eine einfache scrollView verhält.
VipulKumar
2
@ VipulKumars Kommentar ist völlig wahr. Wenn Sie die Recycling-Ansicht in einer anderen Bildlaufansicht verwenden, erfolgt kein Recycling, und es werden alle Elemente erstellt.
VolkanSahin45
Sie können dies tun, wenn Ihre Artikel in recyclerView kleiner als 10 sind. 😄
JIE WANG
5

Die native API verfügt nicht über eine solche Funktion "addHeader", sondern über das Konzept "addItem".

Ich konnte diese spezielle Funktion von Kopf- und Fußzeilen auch in mein FlexibleAdapter- Projekt aufnehmen. Ich nannte es scrollbare Kopf- und Fußzeilen .

Hier, wie sie funktionieren:

Bildlaufbare Kopf- und Fußzeilen sind spezielle Elemente, die zusammen mit allen anderen Elementen gescrollt werden. Sie gehören jedoch nicht zu den Hauptelementen (Geschäftselementen) und werden immer vom Adapter neben den Hauptelementen verwaltet. Diese Elemente befinden sich dauerhaft an der ersten und letzten Position.

Geben Sie hier die Bildbeschreibung ein

Es gibt viel zu sagen, besser die detaillierte Wiki-Seite zu lesen .

Darüber hinaus können Sie mit dem FlexibleAdapter Überschriften / Abschnitte erstellen. Außerdem können Sie sie klebrig machen und viele andere Funktionen wie erweiterbare Elemente, endloses Scrollen, UI-Erweiterungen usw. in einer Bibliothek!

Davideas
quelle
4

Basierend auf diesem Beitrag habe ich eine Unterklasse von RecyclerView.Adapter erstellt, die eine beliebige Anzahl von Kopf- und Fußzeilen unterstützt.

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

Obwohl es eine Lösung zu sein scheint, denke ich auch, dass dieses Ding vom LayoutManager verwaltet werden sollte. Leider brauche ich es jetzt und ich habe keine Zeit, einen StaggeredGridLayoutManager von Grund auf neu zu implementieren (oder sogar zu erweitern).

Ich teste es immer noch, aber Sie können es ausprobieren, wenn Sie wollen. Bitte lassen Sie mich wissen, wenn Sie Probleme damit haben.

mato
quelle
2

Es gibt eine weitere Lösung, die alle oben genannten Anwendungsfälle abdeckt: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

Sie können eine Adaptergruppe erstellen, die Ihren Adapter unverändert enthält, zusammen mit einem Adapter mit einem einzelnen Element, das den Header darstellt. Der Code ist einfach und lesbar:

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

recyclerView.setAdapter(adapterGroup);

AdapterGroup ermöglicht auch das Verschachteln. Für einen Adapter mit Abschnitten können Sie daher eine AdapterGroup pro Abschnitt erstellen. Fügen Sie dann alle Abschnitte in eine Root-Adaptergruppe ein.

blurkidi
quelle
1

HeaderView hängt vom LayoutManager ab. Keiner der Standard-LayoutManager unterstützt dies und wird dies wahrscheinlich nicht tun. HeaderView in ListView schafft viel Komplexität ohne nennenswerten Nutzen.

Ich würde vorschlagen, eine Basisadapterklasse zu erstellen, die Elemente für Header hinzufügt, falls vorhanden. Vergessen Sie nicht, notify * -Methoden zu überschreiben, um sie richtig zu versetzen, je nachdem, ob ein Header vorhanden ist oder nicht.

Yigit
quelle
Gibt es ein Beispiel, auf das Sie mich hinweisen könnten, das eine Recycler-Ansicht mit einem Basisadapter verwendet? Vielen Dank!
ViciDroid
Es gibt einen wirklich bedeutenden Vorteil für Kopf- / Fußzeile in einer Liste: Sie können sie aus der Ansicht scrollen. In diesem Beispiel, in dem sich die Anzahl der sichtbaren Zeilen fast verdoppelt, sobald die Pinguine heraus sind, ist mir keine andere Möglichkeit bekannt, dies zu tun, sondern ListView.addHeaderViewdie Antwort auf diese Frage.
TWiStErRob
Ich glaube nicht, dass ich es richtig verstehe. Wenn es das erste Element im Adapter ist, warum kann es nicht wie im Beispiel gescrollt werden?
Yigit
2
Sie können notify * -Methoden nicht überschreiben, da sie als endgültig markiert sind.
Mato
hmm ich habe das leider nicht überprüft. Stattdessen können Sie einen Adapter-Wrapper erstellen, der dem umschlossenen Adapter ein Observable hinzufügt und verschobene Ereignisse von sich aus auslöst.
Yigit
1
First - extends RecyclerView.Adapter<RecyclerView.ViewHolder>

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

After - Überschreiben Sie die Methode getItemViewTpe *** Wichtiger

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

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

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

In Finish wird die ViewHolders-Klasse static implementiert

public static class MenuViewHolder extends RecyclerView.ViewHolder 

public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 
vrbsm
quelle
1

hier einige artikeldekoration für 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
quelle
1

Ich habe eine Implementierung basierend auf @ hister's gemacht für meine persönlichen Zwecke erstellt, aber unter Verwendung der Vererbung.

Ich verstecke die Mechanismen Implementierungsdetails (wie Add 1 bis itemCountsubtrahieren 1 von position) in einer abstrakten Superklasse HeadingableRecycleAdapter, die von erforderlichen Methoden von Adapter Implementierung wie onBindViewHolder, getItemViewTypeund getItemCount, was das Verfahren endgültig und neue Methoden mit versteckter Logik - Client bereitstellt:

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

Hier sind die HeadingableRecycleAdapterKlasse und ein Client. Ich habe das Header-Layout etwas fest codiert, weil es meinen Anforderungen entspricht.

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

Vielleicht packen Sie Header und Recyclerview in ein 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
quelle
Das Problem dabei ist, dass immer ein Bildlauf für die Höhe des AppBarLayout möglich ist, auch wenn die Liste kürzer als der Bildschirm ist. Um das zu beheben, können Sie wie hier
Daniel López Lacalle
0

Wahrscheinlich hilft http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ . Es werden nur RecyclerView und CardView verwendet. Hier ist ein Adapter:

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

Und hier ist eine Entität:

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

Sie können addHeaderView erstellen und verwenden

adapter.addHeaderView(View).

Dieser Code erstellt den addHeaderViewfür mehr als einen Header. Die Header sollten haben:

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();
        ...
    }
}
iftach barshem
quelle
0

Es ist ein paar Jahre her, aber nur für den Fall, dass jemand dies später liest ...

Mit dem obigen Code wird nur das Header-Layout angezeigt, da viewType immer 0 ist.

Das Problem liegt in der ständigen Deklaration:

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

Wenn Sie beide als Null deklarieren, erhalten Sie immer Null!

Kiwi
quelle
0

Ich habe den gleichen von EC84B4 vorgeschlagenen Ansatz umgesetzt Antwort , aber ich habe RecycleViewAdapter abstrahiert und ihn mithilfe von Schnittstellen leicht resuierbar gemacht.

Um meinen Ansatz zu verwenden, sollten Sie Ihrem Projekt folgende Basisklassen und Schnittstellen hinzufügen:

1) Schnittstelle, die Daten für den Adapter bereitstellt (Sammlung des generischen Typs T und zusätzliche Parameter (falls erforderlich) des generischen Typs P)

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

2) Fabrik zum Binden Ihrer Artikel (Header / Artikel):

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

3) Factory für viewHolders (Header / Elemente):

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

4) Basisklasse für Adapter mit 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;
    }
}

Anwendungsbeispiel :

1) Implementierung von 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-Implementierung:

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

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) Adapter ableiten

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) Adapterklasse instanziieren:

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 usw. Meine Anwendung besitzt Strukturen, die von Ihnen für Ihre eigenen Zwecke ausgetauscht werden sollten.

Sergei Yendiyarov
quelle