J'ai ListView qui a une sorte d'événements sur elle. Les événements sont triés par jour, et j'aimerais avoir l'en-tête avec la date pour chaque jour, puis les événements sont écoutés ci-dessous.
Voici comment je remplis cette liste:
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);
et voici à quoi ressemble ma classe TwoText:
public class TwoText {
public String classID;
public String state;
public TwoText(String classID, String state) {
this.classID = classID;
this.state = state;
}
}
et voici à quoi ressemble ma classe 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;
}
}
et voici 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>
ce que je fais en ce moment, c’est que j’ajoute l’en-tête comme objet de liste ordinaire, mais j’aime bien que ce soit comme en-tête et, dans mon cas, une date.
J'ai ce code dans mon xml pour en-tête:
<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" />
et j'ai essayé de le cacher quand ce n'est pas nécessaire et de le montrer lorsque c'est nécessaire, mais je me suis juste trompé avec le reste de mon code. J'ai essayé quelques autres tutoriels mais ils ont également eu le même effet.
Quelqu'un pourrait-il me guider sur la façon de procéder de cette manière simple?
Voici comment je le fais, les clés sont getItemViewType et getViewTypeCount dans la classe Adapter
. getViewTypeCount
renvoie combien de types d'éléments nous avons dans la liste, dans ce cas nous avons un élément d'en-tête et un élément d'événement, donc deux. getItemViewType
devrait renvoyer le type de View
que nous avons à l'entrée position
.
Android se chargera ensuite de vous transmettre automatiquement le bon type de View
dans convertView
.
Voici à quoi ressemble le résultat du code ci-dessous:
Nous avons d’abord une interface que nos deux types d’éléments de liste implémenteront
public interface Item {
public int getViewType();
public View getView(LayoutInflater inflater, View convertView);
}
Ensuite, nous avons un adaptateur qui prend une liste de 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 Meilleur pour les performances .. peut être remarqué lors du défilement
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; }
}
Ensuite, nous avons les classes implémentation Item
et gonflons les présentations correctes. Dans votre cas, vous aurez quelque chose comme une classe Header
et une classe 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;
}
}
Et puis la classe 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;
}
}
Et un simple Activity
pour l'afficher
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);
}
}
Mise en page pour 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>
Mise en page pour 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>
Mise en page pour 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>
Vous pouvez également devenir plus sophistiqué et utiliser ViewHolders
, charger des choses de manière asynchrone, ou faire ce que vous voulez.
Vous recherchez probablement un ExpandableListView qui possède des en-têtes (groupes) pour séparer les éléments (enfants).
Beau tutoriel sur le sujet: ici .
En guise d'alternative, il existe une bibliothèque Nice tierce partie conçue uniquement pour ce cas d'utilisation. Vous devez alors générer des en-têtes en fonction des données stockées dans l'adaptateur. Ils s'appellent des adaptateurs Rolodex et sont utilisés avec ExpandableListViews
. Ils peuvent facilement être personnalisés pour se comporter comme une liste normale avec des en-têtes.
En utilisant les objets Event
de l'OP et en sachant que les en-têtes sont basés sur le Date
qui lui est associé ... le code ressemblerait à ceci:
L'activité
//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);
L'adaptateur
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;
}
}
Ce que j'ai fait pour que la date (par exemple le 1er décembre 2016) soit en-tête. J'ai utilisé la bibliothèque StickyHeaderListView
https://github.com/emilsjolander/StickyListHeaders
Convertissez la date en long en millis [n'incluez pas l'heure] et transformez-la en id d'en-tête.
@Override
public long getHeaderId(int position) {
return <date in millis>;
}
Voici un exemple de projet , basé sur la réponse détaillée et utile d’antew, qui implémente un ListView
avec plusieurs en-têtes, qui intègre des détenteurs de vues pour améliorer les performances de défilement.
Dans ce projet, les objets représentés dans la ListView
sont des instances de la classe HeaderItem
ou de la classe RowItem
, qui sont toutes deux des sous-classes de la classe abstraite Item
. Chaque sous-classe de Item
correspond à un type de vue différent dans l'adaptateur personnalisé, ItemAdapter
. La méthode getView()
sur ItemAdapter
délègue la création de la vue de chaque élément de la liste à une méthode personnalisée getView()
sur HeaderItem
ou RowItem
, en fonction de Item
sous-classe utilisée à la position transmise à la méthode getView()
de l'adaptateur. Chaque sous-classe Item
fournit son propre détenteur de vue.
Les détenteurs de vue sont implémentés comme suit. Les méthodes getView()
sur les sous-classes Item
vérifient si l'objet View
transmis à la méthode getView()
sur ItemAdapter
est nul. Si tel est le cas, la présentation appropriée est gonflée et un objet détenteur de vue est instancié et associé à la vue gonflée via View.setTag()
. Si l'objet View
n'est pas null, un objet détenteur de vue était déjà associé à la vue et le détenteur de la vue est récupéré via View.getTag()
. La manière dont les vues sont utilisées peut être vue dans l'extrait de code suivant tiré de 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);
}
}
L’implémentation complète de ListView est la suivante. Voici le code Java:
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<Item> {
final private List<Class<?>> viewTypes;
ItemAdapter(List<Item> items) {
super(MainActivity.this, 0, items);
if (items.contains(null))
throw new IllegalArgumentException("null item");
viewTypes = getViewTypes(items);
}
private List<Class<?>> getViewTypes(List<Item> items) {
Set<Class<?>> set = new HashSet<>();
for (Item i : items)
set.add(i.getClass());
List<Class<?>> 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<Item> getItems() {
List<Item> 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;
}
}
Il existe également deux dispositions d’éléments de liste, une pour chaque sous-classe d’éléments. Voici la disposition header
, utilisée par HeaderItem:
<?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="wrap_content"
Android:background="#FFAAAAAA"
>
<TextView
Android:id="@+id/category"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_margin="4dp"
Android:textColor="#FF000000"
Android:textSize="20sp"
Android:textStyle="bold"
/>
</LinearLayout>
Et voici la disposition row
, utilisée par RowItem:
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:minHeight="?android:attr/listPreferredItemHeight"
>
<TextView
Android:id="@+id/option"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:textSize="15sp"
/>
</LinearLayout>
Voici une image d'une partie du ListView résultant: