Android ListView-headers

Ik heb ListView met een soort gebeurtenissen erop. Gebeurtenissen worden per dag gesorteerd en ik zou graag een kop met datum erop willen hebben voor elke dag, en dan gebeurtenissen hieronder beluisteren.

Zo vul ik die lijst in:

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

en zo ziet mijn klas TwoText eruit:

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

en zo ziet mijn TwoTextArrayAdapter-klasse eruit:

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

en dit is 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>

Wat ik op dit moment doe, is dat ik koptekst toevoeg als gewoon lijstobject, maar ik zou het leuk vinden als koptekst en in mijn geval een datum erop.

Ik heb deze code in mijn xml voor header:

<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" />

en ik heb geprobeerd het te verbergen wanneer het niet nodig is en het te laten zien wanneer dat nodig is, maar ik heb de rest van mijn code gewoon verprutst. Ik heb nog een paar tutorials geprobeerd, maar ze hadden ook hetzelfde effect.

Kan iemand me helpen hoe ik dat op een gemakkelijke manier kan doen?


Antwoord 1, Autoriteit 100%

Hier is hoe ik het doe, de sleutels zijn getitemviewtpe en GetvrouwTypeCount in de AdapterKlasse. getViewTypeCountRetourneert hoeveel soorten items die we in de lijst hebben, in dit geval hebben we een header-item en een evenementitem, dus twee. getItemViewTypemoet retourneren welk type ViewWe hebben op de ingang position.

Android zorgt dan voor het passeren van het juiste type Viewin convertViewautomatisch.

Hier ziet het resultaat van de onderstaande code eruit als:

Eerst hebben we een interface die onze typen van twee lijststukken

implementeren

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

Dan hebben we een adapter die een lijst met Item

kost

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

BEWERKEN
Better For Performance.. kan worden opgemerkt tijdens het scrollen

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

Dan hebben we klassen die Itemimplementeren en de juiste lay-outs opblazen. In jouw geval heb je zoiets als een klasse headeren een klasse 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;
    }
}

En dan de klasse 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;
    }
}

En een simpele Activityom het weer te geven

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

Lay-out voor 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>

Indeling voor 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>

Lay-out voor 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>

Je kunt ook liefhebber worden en ViewHoldersgebruiken, dingen asynchroon laden of wat je maar wilt.


Antwoord 2, autoriteit 3%

U bent waarschijnlijk op zoek naar een expandesabelistview die headers heeft (groepen ) om items (Childs) te scheiden.

Mooie tutorial over het onderwerp: .


Antwoord 3

Als alternatief is er een leuke 3e partij bibliotheek speciaal ontworpen voor dit gebruik Case. Waarbij u wilt genereren op basis van de gegevens die in de adapter worden opgeslagen. Ze worden Rolodex-adapters genoemd en worden gebruikt met ExpandableListViews. Ze kunnen gemakkelijk worden aangepast om zich als een normale lijst met headers te gedragen.

Gebruik van de OO OP’s EventObjecten en kennen van de koptekers zijn gebaseerd op de Dategeassocieerd met het … De code ziet er zoiets uit:

de activiteit

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

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

Antwoord 4

wat ik deed om de datum te maken (bijvoorbeeld 01 december 2016) als koptekst.
Ik heb de StickyHeaderlistView Library gebruikt

https://github.com/emilsjolander/stickylistheaders

Converteer de datum tot lang in Millis [neemt u niet op] en maak het als de kop-ID.

@Override
public long getHeaderId(int position) {
    return <date in millis>;
}

Antwoord 5

Hier is een voorbeeldproject, gebaseerd op het gedetailleerde en nuttige antwoord van antew, dat een ListViewmet meerdere headers die weergavehouders bevatten om de scrollprestaties te verbeteren.

In dit project zijn de objecten die worden weergegeven in de ListViewinstanties van de klasse HeaderItemof de klasse RowItem, die beide subklassen van de abstracte klasse Item. Elke subklasse van Itemkomt overeen met een ander weergavetype in de aangepaste adapter, ItemAdapter. De methode getView()op ItemAdapterdelegeert het maken van de weergave voor elk lijstitem aan een geïndividualiseerde getView()-methode op een van beide HeaderItemof RowItem, afhankelijk van de Item-subklasse die wordt gebruikt op de positie die is doorgegeven aan de getView()-methode op de adapter. Elke subklasse Itembiedt zijn eigen weergavehouder.

De weergavehouders zijn als volgt geïmplementeerd. De getView()methoden op de Itemsubklassen controleren of het Viewobject dat is doorgegeven aan de getView()methode op ItemAdapteris null. Als dat het geval is, wordt de juiste lay-out opgeblazen en wordt er een weergavehouderobject geïnstantieerd en gekoppeld aan de opgeblazen weergave via View.setTag(). Als het View-object niet null is, was er al een viewholder-object aan de view gekoppeld en wordt de viewhouder opgehaald via View.getTag(). De manier waarop de weergavehouders worden gebruikt, is te zien in het volgende codefragment uit 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);
    }
}

De volledige implementatie van de ListView volgt. Hier is de 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<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;
    }
}

Er zijn ook twee lay-outs voor lijstitems, één voor elke subklasse Item. Hier is de lay-out header, gebruikt door 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>

En hier is de lay-out row, gebruikt door 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>

Hier is een afbeelding van een deel van de resulterende ListView:

Other episodes