Existe-t-il un équivalent addHeaderView pour RecyclerView?

je cherche un équivalent à addHeaderView pour une vue recycleur. Fondamentalement, je veux avoir une image avec 2 boutons-être ajouté comme en-tête de la liste. Est-il un autre moyen d'ajouter un en-tête de la vue à un recycleur? Un exemple d'orientation serait utile

EDIT 2 (ajouté fragment de mises en page):

après avoir ajouté des instructions log, il semble que getViewType ne reçoive qu'une position 0. Cela ne conduit qu'à une seule révisionde la demande chargement de la mise en page:

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

Le fragment de transition pour charger le 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();
    }
}

le Fragment de La onCreateView:

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

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

le fragment contenant le recycleview:

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

La ligne commentaires du modèle:

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

L'en-tête

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

le code Adaptateur (merci à hister pour m'avoir fait commencer):

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

en utilisant le code ci-dessus, seule la disposition de l'en-tête est affichée puisque viewType est toujours 0. On dirait ce . Si je force l'autre disposition il ressemble à ce :

244
demandé sur ViciDroid 2014-10-23 18:39:59

16 réponses

il n'y a pas un moyen facile comme listview.addHeaderView() , mais vous pouvez y arriver en ajoutant un type à votre adaptateur pour header.

voici un exemple

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

lien vers gist -> ici

407
répondu EC84B4 2017-12-15 13:40:02

facile et réutilisable ItemDecoration

Statique en-têtes peuvent facilement être ajoutées avec un ItemDecoration et sans changement.

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

La décoration est également réutilisable car il n'est pas nécessaire de modifier la carte ou le RecyclerView .

le code d'échantillon fourni ci-dessous exigera une vue pour ajouter au dessus qui peut juste être gonflé comme tout le reste. Il peut ressembler à ceci:

HeaderDecoration sample

pourquoi statique ?

si vous avez juste à afficher du texte et des images Cette solution est pour vous-il n'y a aucune possibilité d'interaction de l'utilisateur comme les boutons ou les téléavertisseurs, car il sera juste attiré en haut de votre liste.

Vide de la gestion des listes

S'il n'y a pas de vue pour décorer, la décoration ne sera pas dessinée. Vous aurez toujours à gérer une liste vide vous-même. (Une solution possible serait d'ajouter un mannequin élément de la carte.)

le code

vous pouvez trouver le code source complet ici sur GitHub y compris un Builder pour aider à l'initialisation du décorateur, ou tout simplement utiliser le code ci-dessous et fournir vos propres valeurs au constructeur.

s'il vous plaît assurez-vous de définir un layout_height correct pour votre vue. par exemple match_parent pourrait ne pas fonctionner correctement.

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

s'il vous Plaît note: Le projet GitHub est mon terrain de jeu personnel. Il n'est pas thorougly testé, c'est pourquoi il n'y a pas de bibliothèque encore .

Que fait-il?

Un ItemDecoration est complémentaire de dessin à un élément d'une liste. Dans ce cas, un la décoration est dessinée au sommet du premier objet.

la vue est mesurée et exposée, puis elle est dessinée vers le haut du premier article. Si un effet parallax est ajouté, il sera aussi clippé sur les limites correctes.

51
répondu David Medenjak 2016-04-15 15:35:41

N'hésitez pas à utiliser Ma bibliothèque, disponible ici .

il vous suffit de créer l'en-tête View pour n'importe quel RecyclerView qui utilise LinearLayoutManager ou GridLayoutManager avec un simple appel de méthode.

enter image description here

41
répondu Bartek Lipinski 2015-04-02 11:07:42

va vous montrer de faire en-tête avec des éléments dans une vue Recycler. Recycler view with Header

Étape 1-Ajouter une dépendance dans votre fichier gradle.

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

Cardview est utilisé pour la décoration.

Étape 2-Créez trois fichiers xml. Une activité principale.Deuxième pour la disposition de L'en-tête.Troisième pour la mise en page des éléments de liste.

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>

en-tête.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>

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

Étape 3-Créer trois classes de haricots.

en-tête.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;
}
}

Étape 4-Créer un adaptateur nommé 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);
    }
}
}

Étape 5- Dans activité principale, ajouter le code suivant:

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

}

la fonction getList() génère dynamiquement les données pour les en-têtes et les éléments de liste.

10
répondu Anshul Aggarwal 2017-03-23 12:28:22

vous pouvez simplement placer votre en-tête et votre RecyclerView dans un NestedScrollView:

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

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

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

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

    </LinearLayout>

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

pour que le défilement fonctionne correctement, vous devez désactiver le défilement emboîté sur votre RecyclerView:

myRecyclerView.setNestedScrollingEnabled(false);
8
répondu Cristan 2017-02-22 10:33:57

vous pouvez le réaliser en utilisant la bibliothèque SectionedRecyclerViewAdapter , il a le concept de" Sections", où quelle Section a un en-tête, pied de page et le contenu (liste des éléments). Dans votre cas, vous pourriez n'avoir besoin que D'une Section, mais vous pouvez en avoir plusieurs:

enter image description here

1) Créer une classe de section personnalisée:

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) Créer un visualiseur personnalisé pour les éléments:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

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

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

3) configurer votre ReclyclerView avec la Sectionrecyclerviewadapter

// 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);
6
répondu Gustavo 2016-06-11 17:23:32

basé sur ce post , j'ai créé une sous-classe de RecyclerView.Adaptateur qui supporte un nombre arbitraire d'entêtes et de pieds de page.

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

bien que cela semble être une solution, je pense aussi que cette chose devrait être gérée par le LayoutManager. Malheureusement, j'en ai besoin maintenant et je n'ai pas le temps de mettre en œuvre un gestionnaire de de rien (ni même s'étendre de lui).

je le teste toujours, mais vous pouvez l'essayer si vous voulez. S'il vous plaît laissez-moi savoir si vous trouvez des problèmes avec elle.

4
répondu mato 2017-05-23 12:10:26

L'API Native n'a pas cette fonctionnalité "addHeader", mais a le concept de "addItem".

j'ai pu inclure cette caractéristique spécifique des en-têtes et s'étend pour les pieds de page aussi bien dans mon FlexibleAdapter projet. Je l'ai appelé en-têtes et pieds de page .

Voici comment ils fonctionnent:

les en-têtes et les pieds de page escamotables sont des articles spéciaux qui faites défiler avec tous les autres, mais ils n'appartiennent pas aux articles principaux (articles d'affaires) et ils sont toujours manipulés par l'adaptateur à côté des articles principaux. Ces articles sont constamment placés à la première et à la dernière position.

enter image description here

il y a beaucoup à dire à leur sujet, mieux vaut lire la page wiki détaillée .

de plus le FlexibleAdapter vous permet de créer des en-têtes / sections, aussi vous pouvez les avoir sticky et des dizaines d'autres caractéristiques comme des articles extensibles, rouleau sans fin, extensions UI, etc... tous dans la même bibliothèque!

3
répondu Davidea 2017-06-06 13:11:13

il y a une autre solution qui couvre tous les cas d'utilisation ci-dessus: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

vous pouvez créer un groupe D'adaptateurs qui tient votre adaptateur comme il est, avec un adaptateur avec un seul élément pour représenter l'en-tête. Le code est facile et lisible:

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

recyclerView.setAdapter(adapterGroup);

AdapterGroup permet la nidification aussi, donc pour un adaptateur avec des sections, vous pouvez créer un AdapterGroup par section. Ensuite, mettez toutes les sections dans un groupe D'Adaptateurs racine.

2
répondu blurkidi 2016-08-17 07:43:15

HeaderView dépend de la LayoutManager. Aucun des LayoutManagers par défaut ne supporte cela et ne le fera probablement pas. HeaderView dans ListView crée beaucoup de complexité sans aucun avantage significatif.

je suggérerais de créer une classe d'adaptateur de base qui ajoute des articles pour les en-têtes s'ils sont fournis. N'oubliez pas de surcharger les méthodes notify* pour les décaler correctement selon que l'en-tête est présent ou non.

1
répondu yigit 2014-10-23 17:06:31
First - extends RecyclerView.Adapter<RecyclerView.ViewHolder>

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

après-outrepasser la méthode getItemViewTpe *** Plus Important

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

method onreateviewholder

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

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

dans les instruments de finition de la classe de supports de vue statique

public static class MenuViewHolder extends RecyclerView.ViewHolder 

public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 
1
répondu vrbsm 2016-04-01 11:18:16

voici quelques pièces pour recyclerview

public class HeaderItemDecoration extends RecyclerView.ItemDecoration {

private View customView;

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

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

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (parent.getChildAdapterPosition(view) == 0) {
        customView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
        outRect.set(0, customView.getMeasuredHeight(), 0, 0);
    } else {
        outRect.setEmpty();
    }
}
}      
1
répondu Trunks ssj 2016-10-12 17:50:48

j'ai fait une implémentation basée sur @hister's à des fins personnelles, mais en utilisant l'héritage.

Je Cache Les détails de mise en œuvre des mécanismes (comme ajouter 1 à itemCount , soustraire 1 de position ) dans une super classe abstraite HeadingableRecycleAdapter , par mise en œuvre des méthodes requises à partir de L'adaptateur comme onBindViewHolder , getItemViewType et getItemCount , ce qui rend ces méthodes finales, et de fournir de nouvelles méthodes avec une logique cachée au client:

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

Voici la classe HeadingableRecycleAdapter et un client. J'ai laissé la disposition de l'en-tête un peu codée parce qu'elle correspond à mes besoins.

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
répondu alexpfx 2017-12-10 09:36:04

Probablement http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ vous aidera. Il utilise uniquement RecyclerView et CardView. Voici un adaptateur:

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

et voici une entité:

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
répondu CoolMind 2016-08-26 21:47:02

vous pouvez créer addHeaderView et utiliser

adapter.addHeaderView(View) .

ce code construit le addHeaderView pour plus d'un en-tête. les en-têtes devraient avoir:

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
répondu iftach barshem 2018-03-16 10:57:07

Peut-être enveloppement d'en-tête et recyclerview dans un coordinatorlayout :

<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
répondu lenhuy2106 2018-07-16 13:50:19