Android-ListView-Kopfzeilen

Ich habe ListView, die eine Art von Ereignissen hat. Die Ereignisse sind nach Tagen sortiert, und ich möchte für jeden Tag Header mit Datum darauf haben, und dann Ereignisse unten zu hören.

Hier ist, wie ich diese Liste bevölke:

ArrayList crs = new ArrayList(); 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); 

Und so sieht meine class TwoText aus:

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

Und so sieht meine TwoTextArrayAdapter-class aus:

 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 { private ArrayList classes; private Activity con; TextView seperator; public TwoTextArrayAdapter(Activity context, int textViewResourceId, ArrayList 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; } } 

und das ist my_list_item.xml

         

Was ich im Moment mache, ist, dass ich Header als normales Listenobjekt hinzufüge, aber ich möchte, dass es als Header ist und in meinem Fall ein Datum hat.

Ich habe diesen Code in meinem XML-Header:

  

und ich habe versucht, es zu verstecken, wenn es unnötig ist und es bei Bedarf zu zeigen, aber ich habe nur den Rest meines Codes durcheinander gebracht. Ich habe ein paar mehr Tutorials ausprobiert, aber sie hatten auch den gleichen Effekt.

Könnte mir jemand erklären, wie man das so einfach macht?

Solutions Collecting From Web of "Android-ListView-Kopfzeilen"

Hier ist, wie ich es mache, die Schlüssel sind getItemViewType und getViewTypeCount in der Adapter class. getViewTypeCount zurück, wie viele Arten von Elementen wir in der Liste haben, in diesem Fall haben wir ein Kopfzeilenelement und ein Ereigniselement, also zwei. getItemViewType sollte den Typ von View wir an der Eingabeposition haben.

Android sorgt dann dafür, dass Sie automatisch den richtigen View Typ in convertView .

Hier sieht das Ergebnis des folgenden Codes aus: Beispiel

Zuerst haben wir eine Schnittstelle, die unsere zwei Listenelementtypen implementieren

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

Dann haben wir einen Adapter, der eine Liste von Item aufnimmt

 public class TwoTextArrayAdapter extends ArrayAdapter { private LayoutInflater mInflater; public enum RowType { LIST_ITEM, HEADER_ITEM } public TwoTextArrayAdapter(Context context, List 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 Besser für performance .. kann beim Scrollen bemerkt werden

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

Dann haben wir classn die Item und pumpen die richtigen Layouts auf. In Ihrem Fall haben Sie etwas wie eine Header class und eine ListItem class.

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

Und dann die ListItem class

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

Und eine einfache Activity , um es anzuzeigen

 public class MainActivity extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List items = new ArrayList(); 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); } } 

Layout für R.layout.header

 < ?xml version="1.0" encoding="utf-8"?>    

Layout für R.layout.my_list_item

 < ?xml version="1.0" encoding="utf-8"?>     

Layout für R.layout.activity_main.xml

    

Du kannst auch ViewHolders und ViewHolders benutzen, Sachen asynchron laden oder was immer du willst.

Sie suchen wahrscheinlich nach einer ExpandableListView, die Header (Gruppen) hat, um Elemente (Childs) zu trennen.

Nettes Tutorial zum Thema: hier .

Als Alternative gibt es eine nette 3rd-Party-Bibliothek, die nur für diesen Anwendungsfall entwickelt wurde. Wobei Sie Header basierend auf den Daten generieren müssen, die im Adapter gespeichert sind. Sie werden Rolodex-Adapter genannt und werden mit ExpandableListViews . Sie können einfach angepasst werden, um sich wie eine normale Liste mit Kopfzeilen zu verhalten.

Wenn Sie die Event Objekte des OP verwenden und wissen, dass die Header auf dem zugehörigen Date basieren, sieht der Code etwa so aus:

Die Aktivität

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

Der Adapter

 private class EventDateAdapter extends NFRolodexArrayAdapter { public EventDateAdapter(Context activity, Collection 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; } } 

Was ich getan habe, um das Datum (zB 1. Dezember 2016) als Überschrift zu setzen. Ich habe die StickyHeaderListView-Bibliothek verwendet

https://github.com/emilsjolander/StickyListHeaders

Wandle das Datum in millis in [long] um (ohne Zeitangabe) und mache es als Header-ID.

 @Override public long getHeaderId(int position) { return ; } 

Hier ist ein Beispielprojekt , basierend auf der detaillierten und hilfreichen Antwort von ListView , die eine ListView mit mehreren Headern implementiert, die View ListView , um die ListView zu verbessern.

In diesem Projekt sind die in der ListView dargestellten Objekte Instanzen der class HeaderItem oder der class RowItem , die beide Unterklassen der abstrakten class Item . Jede Unterklasse von Item entspricht einem anderen Ansichtstyp im benutzerdefinierten Adapter ItemAdapter . Die Methode getView() auf ItemAdapter delegiert das Erstellen der Ansicht für jedes Listenelement an eine individualisierte getView() -Methode entweder für HeaderItem oder RowItem , abhängig von der Item Unterklasse, die an der Position verwendet wird, die an die getView() -Methode des Adapters übergeben wird. Jede Item Unterklasse stellt ihren eigenen View-Halter zur Verfügung.

Die Ansichtshalter sind wie folgt implementiert. Die Methoden getView() für die Item Unterklassen überprüfen, ob das View Objekt, das an die Methode getView() für ItemAdapter null ist. Ist dies der View.setTag() , wird das entsprechende Layout aufgebläht, und ein View-Halter-Objekt wird instanziiert und über View.setTag() mit der aufgeblähten Ansicht View.setTag() . Wenn das View Objekt nicht null ist, wurde der Ansicht bereits ein Objekt des Ansichtshalters zugeordnet, und der View-Holder wird über View.getTag() abgerufen. Die Art und Weise, in der die Ansichtshalter verwendet werden, ist im folgenden Codefragment von HeaderItem :

 @Override View getView(LayoutInflater i, View v) { ViewHolder h; if (v == null) { v = i.inflate(R.layout.header, null); h = new ViewHolder(v); v.setTag(h); } else { h = (ViewHolder) v.getTag(); } h.category.setText(text()); return v; } private class ViewHolder { final TextView category; ViewHolder(View v) { category = v.findViewById(R.id.category); } } 

Die vollständige Implementierung des ListView folgt. Hier ist der Java-Code:

 import android.app.ListActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class MainActivity extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setListAdapter(new ItemAdapter(getItems())); } class ItemAdapter extends ArrayAdapter { final private List> viewTypes; ItemAdapter(List items) { super(MainActivity.this, 0, items); if (items.contains(null)) throw new IllegalArgumentException("null item"); viewTypes = getViewTypes(items); } private List> getViewTypes(List items) { Set> set = new HashSet<>(); for (Item i : items) set.add(i.getClass()); List> list = new ArrayList<>(set); return Collections.unmodifiableList(list); } @Override public int getViewTypeCount() { return viewTypes.size(); } @Override public int getItemViewType(int position) { Item t = getItem(position); return viewTypes.indexOf(t.getClass()); } @Override public View getView(int position, View v, ViewGroup unused) { return getItem(position).getView(getLayoutInflater(), v); } } abstract private class Item { final private String text; Item(String text) { this.text = text; } String text() { return text; } abstract View getView(LayoutInflater i, View v); } private class HeaderItem extends Item { HeaderItem(String text) { super(text); } @Override View getView(LayoutInflater i, View v) { ViewHolder h; if (v == null) { v = i.inflate(R.layout.header, null); h = new ViewHolder(v); v.setTag(h); } else { h = (ViewHolder) v.getTag(); } h.category.setText(text()); return v; } private class ViewHolder { final TextView category; ViewHolder(View v) { category = v.findViewById(R.id.category); } } } private class RowItem extends Item { RowItem(String text) { super(text); } @Override View getView(LayoutInflater i, View v) { ViewHolder h; if (v == null) { v = i.inflate(R.layout.row, null); h = new ViewHolder(v); v.setTag(h); } else { h = (ViewHolder) v.getTag(); } h.option.setText(text()); return v; } private class ViewHolder { final TextView option; ViewHolder(View v) { option = v.findViewById(R.id.option); } } } private List getItems() { List t = new ArrayList<>(); t.add(new HeaderItem("Header 1")); t.add(new RowItem("Row 2")); t.add(new HeaderItem("Header 3")); t.add(new RowItem("Row 4")); t.add(new HeaderItem("Header 5")); t.add(new RowItem("Row 6")); t.add(new HeaderItem("Header 7")); t.add(new RowItem("Row 8")); t.add(new HeaderItem("Header 9")); t.add(new RowItem("Row 10")); t.add(new HeaderItem("Header 11")); t.add(new RowItem("Row 12")); t.add(new HeaderItem("Header 13")); t.add(new RowItem("Row 14")); t.add(new HeaderItem("Header 15")); t.add(new RowItem("Row 16")); t.add(new HeaderItem("Header 17")); t.add(new RowItem("Row 18")); t.add(new HeaderItem("Header 19")); t.add(new RowItem("Row 20")); t.add(new HeaderItem("Header 21")); t.add(new RowItem("Row 22")); t.add(new HeaderItem("Header 23")); t.add(new RowItem("Row 24")); t.add(new HeaderItem("Header 25")); t.add(new RowItem("Row 26")); t.add(new HeaderItem("Header 27")); t.add(new RowItem("Row 28")); t.add(new RowItem("Row 29")); t.add(new RowItem("Row 30")); t.add(new HeaderItem("Header 31")); t.add(new RowItem("Row 32")); t.add(new HeaderItem("Header 33")); t.add(new RowItem("Row 34")); t.add(new RowItem("Row 35")); t.add(new RowItem("Row 36")); return t; } } 

Es gibt auch zwei Layouts für Listenelemente, eines für jede Item-Unterklasse. Hier ist der Layout- header , der von HeaderItem verwendet wird:

 < ?xml version="1.0" encoding="utf-8"?>    

Und hier ist die Layout- row , die von RowItem verwendet wird:

    

Hier ist ein Bild eines Teils der resultierenden ListView:

ListView mit mehreren Headern