webentwicklung-frage-antwort-db.com.de

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

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

EDIT 2 (hinzugefügte Fragmentlayouts):

Nach dem Hinzufügen von Protokollanweisungen scheint es, als würde getViewType nur eine Position von 0 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 von 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();
    }
}

Das 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, das die Recyclingansicht enthält:

<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 Layout der Kommentarzeile:

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

Die Kopfzeile

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

Wenn Sie den obigen Code verwenden, wird nur das Header-Layout angezeigt, da viewType immer 0 ist. Es sieht aus wie this . Wenn ich das andere Layout erzwinge, sieht es aus wie this :

257
ViciDroid

Es gibt keinen einfachen Weg wie 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 zu Gist -> hier

428
EC84B4

Einfach und wiederverwendbar ItemDecoration

Static -Header können mit ItemDecoration und ohne weitere Änderungen hinzugefügt werden.

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

Die Dekoration kann auch wiederverwendet werden, da der Adapter oder die RecyclerView-Karte nicht geändert werden muss.

Der Beispielcode, der unten bereitgestellt wird, erfordert eine Ansicht, die nach oben hinzugefügt werden kann und die wie alles andere aufgeblasen werden kann. Es kann so aussehen:

HeaderDecoration sample

Warum statisch?

Wenn Sie nur Text und Bilder anzeigen müssen, ist diese Lösung für Sie - es gibt keine Möglichkeit für Benutzerinteraktionen wie Schaltflächen oder Ansichts-Pager, da sie nur ganz oben in Ihre Liste eingetragen wird.

Leere Listenbehandlung

Wenn keine Ansicht zum Dekorieren vorhanden ist, wird die Dekoration nicht gezeichnet. Sie müssen immer noch eine leere Liste selbst behandeln. (Eine mögliche Problemumgehung wäre das Hinzufügen eines Dummy-Elements zum Adapter.)

Der Code

Sie finden den vollständigen Quellcode hier auf GitHub einschließlich eines Builder, um die Initialisierung des Dekorators zu erleichtern, oder verwenden Sie einfach den folgenden Code und geben Sie dem Konstruktor eigene Werte an.

Stellen Sie sicher, dass Sie einen korrekten layout_height für Ihre Ansicht festlegen. z.B. match_parent funktioniert 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 wird nicht gründlich getestet, weshalb es noch keine Bibliothek gibt.

Was tut es?

Ein ItemDecoration ist eine zusätzliche Zeichnung zu einem Element einer Liste. In diesem Fall wird eine Verzierung oben auf den ersten Artikel gezeichnet.

Die Ansicht wird gemessen und angeordnet, dann wird sie an den Anfang des ersten Elements gezogen. Wenn ein Parallax-Effekt hinzugefügt wird, wird er auch auf die richtigen Grenzen begrenzt.

52
David Medenjak

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

Sie können den Header View für jede RecyclerView erstellen, die LinearLayoutManager oder GridLayoutManager mit einem einfachen Methodenaufruf verwendet.

enter image description here

41
Bartek Lipinski

Hiermit zeigen Sie, wie Sie einen Header mit Elementen in einer Recycler-Ansicht erstellen .  Recycler view with 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 Hauptaktivität. Zweite für das Header-Layout.

activity_main.xml

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
>

<Android.support.v7.widget.RecyclerView
    Android:id="@+id/my_recycler_view"
    Android:scrollbars="vertical"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">
</Android.support.v7.widget.RecyclerView>
</RelativeLayout>

header.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
xmlns:app="http://schemas.Android.com/apk/res-auto">
<Android.support.v7.widget.CardView
    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>
</LinearLayout>

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

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:orientation="vertical">
        <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>
</LinearLayout>

Schritt 3: Erstellen Sie drei Bean-Klassen.

Header.Java

public class Header extends ListItem {
private String header;

public 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 ListItem(){}

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) {
    if(viewType==TYPE_HEADER)
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
        return  new VHHeader(v);
    }
    else
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
        return new VHItem(v);
    }
   // return null;
}

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

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 Kopfzeilen und für Listenelemente.

13
Anshul Aggarwal

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

 enter image description here

1) Erstellen Sie eine benutzerdefinierte Section-Klasse:

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 Ihr 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);
8
Gustavo

Sie können einfach Ihren Header und Ihre RecyclerView in einem 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 der Bildlauf ordnungsgemäß funktioniert, müssen Sie den verschachtelten Bildlauf auf Ihrem RecyclerView deaktivieren:

myRecyclerView.setNestedScrollingEnabled(false);
7
Cristan

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 sich scheinbar um eine Lösung handelt, denke ich auch, dass dies vom LayoutManager verwaltet werden sollte. Leider brauche ich es jetzt und ich habe keine Zeit, um einen StaggeredGridLayoutManager von Grund auf zu implementieren (noch zu erweitern).

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

4
mato

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

Ich konnte diese spezifische Funktion von Kopfzeilen und Fußzeilen in mein FlexibleAdapter - Projekt einfügen. Ich habe es Scrollable Headers and Footers genannt.

Hier wie sie arbeiten:

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

 enter image description here

Es gibt viel zu sagen über sie, besser die detaillierte Wiki-Seite .

Darüber hinaus können Sie mit dem FlexibleAdapter Kopfzeilen/Sektionen erstellen. Außerdem können Sie sticky und Dutzende andere Funktionen wie erweiterbare Elemente, endloses Scrollen, UI-Erweiterungen usw. in einer Bibliothek zusammenstellen!

3
Davideas

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

Sie können eine AdapterGroup erstellen, die Ihren Adapter so wie er ist, zusammen mit einem Adapter mit einem einzelnen Element, das den Header darstellt, erstellen. 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 Schachteln. Für einen Adapter mit Abschnitten können Sie pro Abschnitt eine AdapterGroup erstellen. Fügen Sie dann alle Abschnitte in eine Root-AdapterGroup ein.

2
blurkidi

hier einige Artikel zum Recycling 

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

HeaderView hängt vom LayoutManager ab. Keiner der Standard-LayoutManager unterstützt dies und wird es wahrscheinlich auch nicht. 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, sofern vorhanden. Vergessen Sie nicht, notify * -Methoden zu überschreiben, um sie korrekt zu versetzen.

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

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

Nach - Ü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 implementiert die ViewHolders-Klasse statisch 

public static class MenuViewHolder extends RecyclerView.ViewHolder 

public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 
1
vrbsm

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

Ich verstecke die Implementierungsdetailsmechanismen (wie Addiere 1 zu itemCount, subtrahiere 1 von position) in einer abstrakten Superklasse HeadingableRecycleAdapter, indem ich erforderliche Methoden von Adapter wie onBindViewHolder, getItemViewType und getItemCount, um diese Methoden endgültig zu machen und dem Client neue Methoden mit versteckter Logik bereitzustellen:

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

Hier sind die Klasse HeadingableRecycleAdapter und ein Client. Ich habe das Header-Layout etwas hartkodiert gelassen, weil es meinen Bedürfnissen 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)));
        }
    }
}
1
alexpfx

Vielleicht Header und Recyclerview in ein Coordinatorlayout einbinden :

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

0
lenhuy2106

Wahrscheinlich wird http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ helfen. Es verwendet nur RecyclerView und CardView . 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;
    }
}
0
CoolMind

sie können addHeaderView erstellen und verwenden

adapter.addHeaderView(View).

Dieser Code erstellt das addHeaderView für mehr als einen Header. Die Überschriften 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();
        ...
    }
}
0
iftach barshem

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 konstanten Deklaration:

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

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

0
Kiwi