Android ListView заголовки

У меня есть 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" />

и я попытался спрятать его, когда он не нужен, и показывая его, когда это необходимо, но я просто испортил остальную часть своего кода. Я попробовал несколько учебных пособий, но они также имели такой же эффект.

Может ли кто-нибудь помочь мне в том, как сделать этот простой способ?

4b9b3361

Здесь, как я это делаю, клавиши getItemViewType и getViewTypeCount в классе Adapter. getViewTypeCount возвращает количество типов элементов, которые у нас есть в списке, в этом случае у нас есть элемент заголовка и элемент события, поэтому два. getItemViewType должен возвращать тип View на входе position.

Android затем позаботится о том, чтобы автоматически выбрать нужный тип View в convertView.

Вот как выглядит результат кода ниже: Example

Сначала у нас есть интерфейс, который реализует наши два типа элементов списка

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

ИЗМЕНИТЬ Лучше для производительности.. можно заметить при прокрутке

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, загрузить материал асинхронно или что угодно.

327
ответ дан 30 нояб. '12 в 0:11
источник

Вероятно, вы ищете ExpandableListView, у которого есть заголовки (группы) для разделения элементов (childs).

Хороший учебник по теме: здесь.

9
ответ дан 27 нояб. '12 в 21:29
источник

В качестве альтернативы, есть хорошая сторонняя библиотека, разработанная именно для этого варианта использования. В связи с этим вам необходимо создать заголовки на основе данных, хранящихся в адаптере. Они называются адаптерами Rolodex и используются с ExpandableListViews. Их можно легко настроить, чтобы вести себя как обычный список с заголовками.

Использование объектов OP Event и знание заголовков основано на Date, связанных с ним... код будет выглядеть примерно так:

Активность

    //There 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;
    }
}
2
ответ дан 13 февр. '15 в 3:58
источник

Что я сделал, чтобы сделать Date (например, 01 декабря 2016 года) в качестве заголовка. Я использовал библиотеку StickyHeaderListView

https://github.com/emilsjolander/StickyListHeaders

Преобразовать дату в миллисекунду [не включать время] и сделать ее как идентификатор заголовка.

@Override
public long getHeaderId(int position) {
    return <date in millis>;
}
1
ответ дан 08 дек. '16 в 12:21
источник