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 :
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);
}
}
}
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:
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.
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.)
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.
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.
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.
Hiermit zeigen Sie, wie Sie einen Header mit Elementen in einer Recycler-Ansicht erstellen .
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.
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:
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));
}
}
class MyItemViewHolder extends RecyclerView.ViewHolder {
private final TextView tvItem;
public MyItemViewHolder(View itemView) {
super(itemView);
tvItem = (TextView) itemView.findViewById(R.id.tvItem);
}
}
// 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);
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);
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.
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.
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!
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.
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();
}
}
}
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.
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
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)));
}
}
}
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" />
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;
}
}
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();
...
}
}
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!