Encabezados ListView de Android

Tengo ListView que tiene algún tipo de eventos en él. Los eventos se ordenan por día, y me gustaría tener el encabezado con la fecha en él para cada día, y luego los eventos escuchar a continuación.

Aquí es cómo relleno esa lista:

ArrayList<TwoText> crs = new ArrayList<TwoText>(); crs.add(new TwoText("This will be header", event.getDate())); for (Event event : events) { crs.add(new TwoText(event.getStartString() + "-" + event.getEndString(), event.getSubject())); } arrayAdapter = new TwoTextArrayAdapter(this, R.layout.my_list_item, crs); lv1.setAdapter(arrayAdapter); 

Y así es como se ve mi clase TwoText:

 public class TwoText { public String classID; public String state; public TwoText(String classID, String state) { this.classID = classID; this.state = state; } } 

Y así es como se ve mi clase TwoTextArrayAdapter:

 import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; public class TwoTextArrayAdapter extends ArrayAdapter<TwoText> { private ArrayList<TwoText> classes; private Activity con; TextView seperator; public TwoTextArrayAdapter(Activity context, int textViewResourceId, ArrayList<TwoText> classes) { super(context, textViewResourceId, classes); this.con = context; this.classes = classes; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.my_list_item, null); } TwoText user = classes.get(position); if (user != null) { TextView content1 = (TextView) v.findViewById(R.id.list_content1); TextView content2 = (TextView) v.findViewById(R.id.list_content2); if (content1 != null) { content1.setText(user.classID); } if(content2 != null) { content2.setText(user.state); } } return v; } } 

Y esto es my_list_item.xml

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView style="?android:attr/listSeparatorTextViewStyle" android:id="@+id/separator" android:text="Header" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#757678" android:textColor="#f5c227" /> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/list_content1" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_margin="5dip" android:clickable="false" android:gravity="center" android:longClickable="false" android:paddingBottom="1dip" android:paddingTop="1dip" android:text="sample" android:textColor="#ff7f1d" android:textSize="17dip" android:textStyle="bold" /> <TextView android:id="@+id/list_content2" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_margin="5dip" android:clickable="false" android:gravity="center" android:linksClickable="false" android:longClickable="false" android:paddingBottom="1dip" android:paddingTop="1dip" android:text="sample" android:textColor="#6d6d6d" android:textSize="17dip" /> </LinearLayout> </LinearLayout> 

Lo que hago en este momento es que estoy añadiendo cabecera como objeto de lista regular, pero Id como que sea como encabezado y en mi caso tienen una fecha en él.

Tengo este código en mi xml para el encabezado:

 <TextView style="?android:attr/listSeparatorTextViewStyle" android:id="@+id/separator" android:text="Header" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#757678" android:textColor="#f5c227" /> 

Y traté de esconderlo cuando es innecesario y mostrarlo cuando sea necesario, pero me acaba de desordenar el resto de mi código. He intentado algunos tutoriales más pero también tenían el mismo efecto.

¿Podría alguien guiarme sobre cómo hacer esa manera fácil?

Aquí es cómo lo hago, las llaves son getItemViewType y getViewTypeCount en la clase del Adapter . getViewTypeCount devuelve cuántos tipos de elementos tenemos en la lista, en este caso tenemos un elemento de encabezado y un elemento de evento, por lo que dos. getItemViewType debe devolver qué tipo de View tenemos en la position entrada.

Android se encargará entonces de pasarle el tipo correcto de View en convertView automáticamente.

Aquí se ve el resultado del siguiente código: Ejemplo

Primero tenemos una interfaz que nuestros dos tipos de elementos de lista implementarán

 public interface Item { public int getViewType(); public View getView(LayoutInflater inflater, View convertView); } 

Entonces tenemos un adaptador que toma una lista del Item

 public class TwoTextArrayAdapter extends ArrayAdapter<Item> { private LayoutInflater mInflater; public enum RowType { LIST_ITEM, HEADER_ITEM } public TwoTextArrayAdapter(Context context, List<Item> items) { super(context, 0, items); mInflater = LayoutInflater.from(context); } @Override public int getViewTypeCount() { return RowType.values().length; } @Override public int getItemViewType(int position) { return getItem(position).getViewType(); } 
  @Override public View getView(int position, View convertView, ViewGroup parent) { return getItem(position).getView(mInflater, convertView); } 

EDIT Better For Performance .. se puede notar al desplazarse

 private static final int TYPE_ITEM = 0; private static final int TYPE_SEPARATOR = 1; public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; int rowType = getItemViewType(position); View View; if (convertView == null) { holder = new ViewHolder(); switch (rowType) { case TYPE_ITEM: convertView = mInflater.inflate(R.layout.task_details_row, null); holder.View=getItem(position).getView(mInflater, convertView); break; case TYPE_SEPARATOR: convertView = mInflater.inflate(R.layout.task_detail_header, null); holder.View=getItem(position).getView(mInflater, convertView); break; } convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } return convertView; } public static class ViewHolder { public View View; } } 

A continuación, tenemos clases de implementar el Item e inflar los diseños correctos. En tu caso tendrás algo así como una clase Header y una clase ListItem .

  public class Header implements Item { private final String name; public Header(String name) { this.name = name; } @Override public int getViewType() { return RowType.HEADER_ITEM.ordinal(); } @Override public View getView(LayoutInflater inflater, View convertView) { View view; if (convertView == null) { view = (View) inflater.inflate(R.layout.header, null); // Do some initialization } else { view = convertView; } TextView text = (TextView) view.findViewById(R.id.separator); text.setText(name); return view; } } 

Y luego la clase ListItem

  public class ListItem implements Item { private final String str1; private final String str2; public ListItem(String text1, String text2) { this.str1 = text1; this.str2 = text2; } @Override public int getViewType() { return RowType.LIST_ITEM.ordinal(); } @Override public View getView(LayoutInflater inflater, View convertView) { View view; if (convertView == null) { view = (View) inflater.inflate(R.layout.my_list_item, null); // Do some initialization } else { view = convertView; } TextView text1 = (TextView) view.findViewById(R.id.list_content1); TextView text2 = (TextView) view.findViewById(R.id.list_content2); text1.setText(str1); text2.setText(str2); return view; } } 

Y una Activity simple para mostrarlo

 public class MainActivity extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<Item> items = new ArrayList<Item>(); items.add(new Header("Header 1")); items.add(new ListItem("Text 1", "Rabble rabble")); items.add(new ListItem("Text 2", "Rabble rabble")); items.add(new ListItem("Text 3", "Rabble rabble")); items.add(new ListItem("Text 4", "Rabble rabble")); items.add(new Header("Header 2")); items.add(new ListItem("Text 5", "Rabble rabble")); items.add(new ListItem("Text 6", "Rabble rabble")); items.add(new ListItem("Text 7", "Rabble rabble")); items.add(new ListItem("Text 8", "Rabble rabble")); TwoTextArrayAdapter adapter = new TwoTextArrayAdapter(this, items); setListAdapter(adapter); } } 

Diseño de R.layout.header

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView style="?android:attr/listSeparatorTextViewStyle" android:id="@+id/separator" android:text="Header" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#757678" android:textColor="#f5c227" /> </LinearLayout> 

Diseño de R.layout.my_list_item

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/list_content1" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_margin="5dip" android:clickable="false" android:gravity="center" android:longClickable="false" android:paddingBottom="1dip" android:paddingTop="1dip" android:text="sample" android:textColor="#ff7f1d" android:textSize="17dip" android:textStyle="bold" /> <TextView android:id="@+id/list_content2" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_margin="5dip" android:clickable="false" android:gravity="center" android:linksClickable="false" android:longClickable="false" android:paddingBottom="1dip" android:paddingTop="1dip" android:text="sample" android:textColor="#6d6d6d" android:textSize="17dip" /> </LinearLayout> 

Diseño de R.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" tools:context=".MainActivity" > <ListView android:id="@android:id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout> 

También puede obtener más lujoso y utilizar ViewHolders , cargar cosas asincrónicamente, o lo que quieras.

Probablemente esté buscando un ExpandableListView que tenga encabezados (grupos) para separar elementos (childs).

Buen tutorial sobre el tema: aquí .

Como alternativa, hay una bonita biblioteca de terceros diseñada sólo para este caso de uso. Por lo que necesita generar encabezados basados ​​en los datos almacenados en el adaptador. Se llaman adaptadores Rolodex y se utilizan con ExpandableListViews . Se pueden personalizar fácilmente para comportarse como una lista normal con encabezados.

El uso de los objetos Event del OP y el conocimiento de los encabezados se basan en la Date asociada a él … el código se vería así:

La actividad

  //There's no need to pre-compute what the headers are. Just pass in your List of objects. EventDateAdapter adapter = new EventDateAdapter(this, mEvents); mExpandableListView.setAdapter(adapter); 

El adaptador

 private class EventDateAdapter extends NFRolodexArrayAdapter<Date, Event> { public EventDateAdapter(Context activity, Collection<Event> items) { super(activity, items); } @Override public Date createGroupFor(Event childItem) { //This is how the adapter determines what the headers are and what child items belong to it return (Date) childItem.getDate().clone(); } @Override public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { //Inflate your view //Gets the Event data for this view Event event = getChild(groupPosition, childPosition); //Fill view with event data } @Override public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { //Inflate your header view //Gets the Date for this view Date date = getGroup(groupPosition); //Fill view with date data } @Override public boolean hasAutoExpandingGroups() { //This forces our group views (headers) to always render expanded. //Even attempting to programmatically collapse a group will not work. return true; } @Override public boolean isGroupSelectable(int groupPosition) { //This prevents a user from seeing any touch feedback when a group (header) is clicked. return false; } } 

Lo que hice para que la fecha (por ejemplo, 01 de diciembre 2016) como encabezado. He utilizado la biblioteca StickyHeaderListView

https://github.com/emilsjolander/StickyListHeaders

Convierta la fecha a largo en milisegundos [no incluya la hora] y hágalo como la Id de encabezado.

 @Override public long getHeaderId(int position) { return <date in millis>; } 
  • Pasar la posición en el ListView al niño RecyclerView Adapter
  • SmoothScrollToPosition no funciona?
  • Android cómo hacer que resalte la vista cuando se hace clic?
  • Android: EfficientAdapter con dos vistas diferentes
  • Deshabilitar clic largo para encabezados (o pies de página) en un ListView
  • Implementación de reproductor de audio en listview en android
  • Obtener el elemento seleccionado de customadapter listview Android
  • Cómo configurar el color del elemento seleccionado en ListFragment en android?
  • Utilizar OnItemClickListener en ListView dentro de ViewPager
  • ListView personalizado cuando se desplaza cambia aleatoriamente el texto del botón o edittext
  • Tipface personalizado en android ListView con SimpleAdapter
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.