studio recyclerview make how ejemplo create card and android android-view android-recyclerview

android - make - ¿Hay un equivalente addHeaderView para RecyclerView?



recyclerview android studio ejemplo (16)

Estoy buscando un equivalente a addHeaderView para una vista de reciclador. Básicamente, quiero que se agregue una imagen con 2 botones como encabezado a la vista de lista. ¿Hay una forma diferente de agregar una vista de encabezado a una vista de reciclador? Un ejemplo de orientación sería útil.

EDITAR 2 (diseños de fragmentos añadidos):

Después de agregar declaraciones de registro, parece que getViewType solo recibe una posición de 0. Esto lleva a que OnCreateView solo cargue el diseño:

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

La transición del fragmento para cargar el 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(); } }

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

El fragmento que contiene el reciclaje:

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

El diseño de la fila de comentarios:

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

El encabezado

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

El código del adaptador (gracias al historial por iniciarme):

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

Usando el código anterior, solo se muestra el diseño del encabezado ya que viewType siempre es 0. Se ve así. Si fuerzo el otro diseño se ve así:


Fácil y reutilizable ItemDecoration

Los encabezados estáticos se pueden agregar fácilmente con una ItemDecoration y sin ningún cambio adicional.

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

La decoración también es reutilizable, ya que no es necesario modificar el adaptador o RecyclerView en absoluto.

El código de muestra que se proporciona a continuación requerirá una vista para agregar a la parte superior que se puede inflar como todo lo demás. Puede verse así:

¿Por qué estática ?

Si solo tiene que mostrar texto e imágenes, esta solución es para usted: no hay posibilidad de interacción con el usuario, como botones o ver paginadores, ya que solo se colocará en la parte superior de la lista.

Manejo de lista vacía

Si no hay vistas para decorar, la decoración no será dibujada. Usted todavía tendrá que manejar una lista vacía a ti mismo. (Una posible solución sería agregar un elemento ficticio al adaptador).

El código

Puede encontrar el código fuente completo aquí en GitHub incluyendo un Builder para ayudar con la inicialización del decorador, o simplemente use el código a continuación y proporcione sus propios valores al constructor.

Asegúrese de establecer un layout_height correcto para su vista. por ejemplo, match_parent podría no funcionar correctamente.

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

Tenga en cuenta: El proyecto GitHub es mi área de juegos personal. No está completamente probado, por lo que aún no existe una biblioteca.

¿Qué hace?

Un ItemDecoration es un dibujo adicional a un elemento de una lista. En este caso, se dibuja una decoración en la parte superior del primer elemento.

La vista se mide y se presenta, luego se dibuja en la parte superior del primer elemento. Si se agrega un efecto de paralaje, también se recortará a los límites correctos.


Aquí algunos elementos de decoración para el 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(); } } }


Basándome en esta publicación , creé una subclase de RecyclerView.Adapter que admite un número arbitrario de encabezados y pies de página.

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

Aunque parece ser una solución, también creo que esto debería ser administrado por el LayoutManager. Desafortunadamente, lo necesito ahora y no tengo tiempo para implementar un StaggeredGridLayoutManager desde cero (ni siquiera extenderlo).

Todavía lo estoy probando, pero puedes probarlo si quieres. Por favor, hágamelo saber si encuentra algún problema con él.


Hay una solución más que cubre todos los casos de uso anteriores: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

Puede crear un AdapterGroup que mantenga su adaptador tal como está, junto con un adaptador con un solo elemento para representar el encabezado. El código es fácil y legible:

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

AdapterGroup también permite el anidamiento, así que para un adaptador con secciones, puede crear un AdapterGroup por sección. Luego coloque todas las secciones en un grupo de adaptadores raíz.


HeaderView depende del LayoutManager. Ninguno de los LayoutManagers predeterminados admite esto y probablemente no lo haga. HeaderView en ListView crea mucha complejidad sin ningún beneficio significativo.

Sugeriría crear una clase de adaptador base que agregue elementos para los encabezados, si se proporciona. No olvide anular los métodos de notificación * para compensarlos adecuadamente dependiendo de si el encabezado está presente o no.


Hice una implementación basada en @hister''s para mis propósitos personales, pero utilizando la herencia.

itemCount mecanismos de detalles de implementación (como agregar 1 a itemCount , restar 1 de la position ) en una súper clase abstracta HeadingableRecycleAdapter , implementando los métodos requeridos del Adaptador como onBindViewHolder , getItemViewType y getItemCount , haciendo que los métodos sean definitivos y que proporcione nuevos métodos con lógica oculta. al cliente:

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

Aquí están la clase HeadingableRecycleAdapter y un cliente. Dejé el diseño del encabezado un poco rígido porque se ajusta a mis necesidades.

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


La API nativa no tiene tal característica "addHeader", pero tiene el concepto de "addItem".

Pude incluir esta característica específica de encabezados y extensiones para pies de página también en mi proyecto FlexibleAdapter . Lo llamé Encabezados y pies de página desplazables .

Aquí cómo funcionan:

Los encabezados y pies de página desplazables son elementos especiales que se desplazan junto con todos los demás, pero no pertenecen a los elementos principales (elementos comerciales) y siempre están a cargo del adaptador que se encuentra junto a los elementos principales. Esos artículos se ubican persistentemente en la primera y última posición.

Hay mucho que decir sobre ellos, mejor leer la página wiki detallada.

Además, FlexibleAdapter le permite crear encabezados / secciones, también puede tenerlos pegajosos y decenas de otras características como elementos expandibles, desplazamiento sin fin, extensiones de IU, etc. ¡todo en una sola biblioteca!


Le mostraré cómo hacer encabezado con elementos en una vista de reciclador.

Paso 1- Agrega dependencia en tu archivo de gradle.

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

Cardview se utiliza para fines de decoración.

Paso2- Haz tres archivos xml. Uno para la actividad principal. Segundo para el diseño del encabezado. Tercero para el diseño del elemento de la lista.

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>

Paso 3- Crea tres clases de frijoles.

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

Paso 4- Crea un adaptador llamado 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); } } }

Paso 5- En MainActivity agrega el siguiente código:

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 función getList () está generando dinámicamente los datos para los encabezados y para los elementos de la lista.


No hay una forma fácil como listview.addHeaderView() pero puede lograrlo agregando un tipo a su adaptador para el encabezado.

Aquí hay un ejemplo

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

enlace a gist -> aquí


Puede lograrlo utilizando la biblioteca SectionedRecyclerViewAdapter , tiene el concepto de "Secciones", donde la Sección tiene un encabezado, pie de página y contenido (lista de elementos). En su caso, es posible que solo necesite una Sección, pero puede tener muchas:

1) Crear una clase de sección personalizada:

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) Crea un ViewHolder personalizado para los artículos:

class MyItemViewHolder extends RecyclerView.ViewHolder { private final TextView tvItem; public MyItemViewHolder(View itemView) { super(itemView); tvItem = (TextView) itemView.findViewById(R.id.tvItem); } }

3) Configure su ReclyclerView con el SectionedRecyclerViewAdapter

// Create an instance of SectionedRecyclerViewAdapter SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter(); MySection mySection = new MySection(); // Add your Sections sectionAdapter.addSection(mySection); // Set up your RecyclerView with the SectionedRecyclerViewAdapter RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(sectionAdapter);


Siéntase libre de usar mi biblioteca, disponible here .

Le permite crear una View encabezado para cualquier RecyclerView que use LinearLayoutManager o GridLayoutManager con solo una simple llamada de método.


Simplemente puede colocar su encabezado y su RecyclerView en 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>

Para que el desplazamiento funcione correctamente, debe desactivar el desplazamiento anidado en su RecyclerView:

myRecyclerView.setNestedScrollingEnabled(false);


puedes crear addHeaderView y usar

adapter.addHeaderView(View) .

Este código se construye addHeaderViewpara más de un encabezado. los encabezados deben tener:

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


Probablemente http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ ayudará. Utiliza solo RecyclerView y CardView. Aquí hay un adaptador:

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

Y aquí hay una entidad:

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


Tal vez envuelva el encabezado y recyclerview en 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" />


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

Después: anular el método getItemViewTpe *** Más importante

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

método 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); } }

método 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]); } }

en el final implementa la clase de ViewHolders estática

public static class MenuViewHolder extends RecyclerView.ViewHolder public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder