Заголовков ListView в Android
у меня есть ListView, который имеет какие-то события на ней. События сортируются по дням, и я хотел бы иметь заголовок с датой на нем для каждого дня, а затем события слушать ниже.
вот как я заполняю этот список:
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);
и вот как выглядит мой класс TwoText:
public class TwoText {
public String classID;
public String state;
public TwoText(String classID, String state) {
this.classID = classID;
this.state = state;
}
}
и вот как выглядит мой класс 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;
}
}
а это 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>
то, что я делаю в данный момент, это то, что я я добавляю заголовок так же, как обычный объект списка, но Id, как и заголовок, и в моем случае есть дата на нем.
у меня есть этот код в моем xml для заголовка:
<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" />
и я пытался скрыть его, когда это не нужно и показывать его, когда это необходимо, но я просто испортил остальную часть моего кода. Я попробовал еще несколько учебников, но они также имели тот же эффект.
может ли кто-нибудь направить меня на то, как сделать это простым способом?
5 ответов:
вот как я это делаю, ключи getItemViewType и getViewTypeCount на
Adapter
класса.getViewTypeCount
возвращает, сколько типов элементов у нас есть в списке, в этом случае у нас есть элемент заголовка и элемент события, так что два.getItemViewType
должен вернуть какой типView
у нас на входеposition
.Android будет заботиться о передаче вам правильный тип
View
наconvertView
автоматически.вот что результате код выглядит так:
сначала у нас есть интерфейс, который наши два типа элементов списка будут реализовывать
public interface Item { public int getViewType(); public View getView(LayoutInflater inflater, View convertView); }
затем у нас есть адаптер, который принимает список
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 Лучше Для Производительности.. можно заметить при прокрутке
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; } }
тогда у нас есть классы реализации
Item
и надуть правильные макеты. В вашем случае у вас будет что-то вродеHeader
и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; } }
а то
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; } }
и просто
Activity
для отображения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); } }
макет для
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>
макет для
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>
макет для
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>
вы также можете получить fancier и использовать
ViewHolders
, загружать вещи асинхронно, или что вам нравится.
вы, вероятно, ищете ExpandableListView который имеет заголовки (группы) для разделения элементов (детей).
хороший учебник по теме: здесь.
в качестве альтернативы, есть хороший 3-й партии библиотека предназначен только для этого случая использования. При этом вам необходимо генерировать заголовки на основе данных, хранящихся в адаптере. Они называются адаптерами Rolodex и используются с
ExpandableListViews
. Они могут быть легко настроены вести себя как обычный список с заголовками.используя ОП
Event
объекты и зная заголовки на основеDate
связанные с ним...код будет выглядеть примерно так это:Активность
//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);
Адаптер
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; } }
что я сделал, чтобы сделать дату (например, 01 декабря 2016 года) в качестве заголовка. Я использовал библиотеку StickyHeaderListView
https://github.com/emilsjolander/StickyListHeaders
преобразуйте дату в long в миллисе [не включайте время] и сделайте ее идентификатором заголовка.
@Override public long getHeaderId(int position) { return <date in millis>; }
вот пример проекта, основываясь на подробном и полезном ответе antew, который реализует
ListView
С несколькими заголовками, которые включают в себя держатели вида для повышения производительности прокрутки.в этом проекте, объекты, представленные в разделе
ListView
являются экземплярами либо классаHeaderItem
или классRowItem
, оба из которых являются подклассами абстрактного классаItem
. Каждый подклассItem
соответствует другому типу представления в пользовательском адаптер,ItemAdapter
. МетодgetView()
onItemAdapter
делегирует создание представления для каждого элемента списка индивидуализированномуgetView()
метод либоHeaderItem
илиRowItem
в зависимости отItem
подкласс, используемый в позиции, переданнойgetView()
метод на адаптере. КаждыйItem
подкласс предоставляет свой собственный держатель вид.держатели вида реализованы следующим образом. Элемент
getView()
методыItem
подклассы проверить, является ли