Pytanie Jak napisać niestandardowy ExpandableListAdapter


Szukam napisać własną ExpandableListAdapter który działa podobnie do ArrayAdapter. Mój model danych to:

public class Group {

    private String name;   

    private List<Child> children;
}

public class Child {

     private String name;
}

Dość proste. Jak mogę odwzorować tę relację na ExpandableListAdapter realizacja? Mam pracę SimpleExpandableListAdapter działa teraz, ale potrzebuję więcej niestandardowej kontroli nad przedmiotami (wyświetlanie ikon itp.). Co powinienem zrobić?

Najważniejsze, że potrzebuję add() metoda, aby móc dodawać grupy i unieważniać listę, gdy dzieci są dodawane i usuwane z adaptera. Jestem naprawdę zaskoczony, że nie ma implementacji w SDK (nawet abstrakcyjnym), która pomaga w osiągnięciu tego.


13
2018-03-04 00:03


pochodzenie




Odpowiedzi:


Oto implementacja, którą właśnie zbiłem. Nie mam pojęcia, czy to działa, czy nie, ale wydaje mi się "inteligentny" do mnie :) A tak przy okazji, jak powinieneś uzyskać łączny identyfikator dziecka lub połączony identyfikator grupy, więc po prostu trochę improwizowałem.

package example;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import android.content.Context;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListAdapter;

public abstract class AbstractExpandableListAdapter<A, B> implements ExpandableListAdapter {

    private final List<Entry<A, List<B>>> objects;

    private final DataSetObservable dataSetObservable = new DataSetObservable();

    private final Context context;

    private final Integer groupClosedView;

    private final Integer groupExpandedView;

    private final Integer childView;

    private final LayoutInflater inflater;

    public AbstractExpandableListAdapter(Context context, int groupClosedView, 
            int groupExpandedView, int childView, List<Entry<A, List<B>>> objects) {
        this.context = context;
        this.objects = objects;
        this.groupClosedView = new Integer(groupClosedView);
        this.groupExpandedView = new Integer(groupExpandedView);
        this.childView = new Integer(childView);

        this.inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public void add(Entry<A, List<B>> group) {
        this.getObjects().add(group);
        this.notifyDataSetChanged();
    }

    public void remove(A group) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                this.getObjects().remove(group);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void remove(Entry<A, List<B>> entry) {
        remove(entry.getKey());
    }

    public void addChild(A group, B child) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                if (entry.getValue() == null) 
                    entry.setValue(new ArrayList<B>());

                entry.getValue().add(child);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void removeChild(A group, B child) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                if (entry.getValue() == null)
                    return;

                entry.getValue().remove(child);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void notifyDataSetChanged() {
        this.getDataSetObservable().notifyChanged();
    }

    public void notifyDataSetInvalidated() {
        this.getDataSetObservable().notifyInvalidated();
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        this.getDataSetObservable().registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        this.getDataSetObservable().unregisterObserver(observer);
    }

    public int getGroupCount() {
        return getObjects().size();
    }

    public int getChildrenCount(int groupPosition) {
        return getObjects().get(groupPosition).getValue().size();
    }

    public Object getGroup(int groupPosition) {
        return getObjects().get(groupPosition).getKey();
    }

    public Object getChild(int groupPosition, int childPosition) {
        return getObjects().get(groupPosition).getValue().get(childPosition);
    }

    public long getGroupId(int groupPosition) {
        return ((Integer)groupPosition).longValue();
    }

    public long getChildId(int groupPosition, int childPosition) {
        return ((Integer)childPosition).longValue();
    }

    public boolean hasStableIds() {
        return true;
    }

    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        if (convertView != null && convertView.getId() != 
                (isExpanded ? getGroupExpandedView() : getGroupClosedView())) {
//          do nothing, we're good to go, nothing has changed.
        } else {
//          something has changed, update.
            convertView = inflater.inflate(isExpanded ? getGroupExpandedView() :
                    getGroupClosedView(), parent, false);
            convertView.setTag(getObjects().get(groupPosition));
        }

        return convertView;
    }

    public View getChildView(int groupPosition, int childPosition,
            boolean isLastChild, View convertView, ViewGroup parent) {

        if (convertView != null) {
//          do nothing 
        } else {
//          create
            convertView = inflater.inflate(getChildView(), parent, false);
            convertView.setTag(getObjects().get(groupPosition).getValue().get(childPosition));
        }

        return convertView;
    }

    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEmpty() {
        return getObjects().size() == 0;
    }

    public void onGroupExpanded(int groupPosition) {

    }

    public void onGroupCollapsed(int groupPosition) {

    }

    public long getCombinedChildId(long groupId, long childId) {
        return groupId * 10000L + childId;
    }

    public long getCombinedGroupId(long groupId) {
        return groupId * 10000L;
    }

    protected DataSetObservable getDataSetObservable() {
        return dataSetObservable;
    }

    protected List<Entry<A, List<B>>> getObjects() {
        return objects;
    }

    protected Context getContext() {
        return context;
    }

    protected Integer getGroupClosedView() {
        return groupClosedView;
    }

    protected Integer getGroupExpandedView() {
        return groupExpandedView;
    }

    protected Integer getChildView() {
        return childView;
    }
}

Wszelkie uwagi lub uwagi są mile widziane.


26
2018-03-04 22:05



Przy okazji, po prostu przetestowałem to i wydaje się, że działa świetnie. :) - Naftuli Kay
Po prostu wybitne, dzięki! - myforums
Raczej imponujące do bicia go na miejscu. - Jay Soyer
Jeśli rozszerzysz BaseExpandableListAdapter zamiast implementować ExpandableListAdapter, nie powinieneś potrzebować implementacji metod dla połączonych identyfikatorów - Phil
Android SDK proste i minimalne wbudowane zasoby widoku dla adaptera listy rozwijanej, do której można przekazać AbstractExpandableListAdapter konstruktor jest android.R.layout.simple_expandable_list_item_1dla obu groupClosedView i groupExpandedView, i android.R.layout.simple_list_item_1 dla childView - Eido95


Byłem dość zaskoczony, że nie znalazłem lepszej dokumentacji na ten temat. Jeśli ją znajdziesz, opublikuj ją tutaj. Najlepszym przykładem wdrożenia, jaki znalazłem, był ApiDemos. Tam jest ExpandableListActivity który implementuje a BaseExpandableListAdapter. Klasa nazywa się ExpandableList1.java.

Będziesz musiał stworzyć swój własny add() metoda, która dodaje twoje Group i Child klasy do adaptera. Nie sądzę, że na pierwszy rzut oka będzie to trudne. W rzeczywistości możesz po prostu tworzyć referencje do obiektów klasy. Kiedy zaimplementowałem mój, mój zbiór danych był mały i nie zmienił się, więc musiałem tylko odwołać się do mojego pliku array.xml.


4
2018-03-04 00:22





   public class CustomExpandableAdapter extends BaseExpandableListAdapter {
        private Context mContext;
        private List<Group> mData;
        private int mSelectedPosition = -1;

        public CustomExpandableAdapter(Context context, List<Group> data ) {
            mData = data;
            mContext = context;

        }

        @Override
        public int getGroupCount() {
            return mData.size();
        }

        @Override
        public int getChildrenCount(int groupPosition) {
            return mData.get(groupPosition).children.size();
        }

        @Override
        public Object getGroup(int groupPosition) {
            return mData.get(groupPosition);
        }

        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return mData.get(groupPosition).children.get(childPosition);
        }

        @Override
        public long getGroupId(int groupPosition) {
            return groupPosition;
        }

        @Override
        public long getChildId(int groupPosition, int childPosition) {
            return childPosition;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
            HeaderViewHolder headerViewHolder = null;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_header_text_layout, null);
                headerViewHolder = new HeaderViewHolder(convertView);
                convertView.setTag(headerViewHolder);
            }
            headerViewHolder = (HeaderViewHolder) convertView.getTag();

            headerViewHolder.mGroupHeader.setText(mData.get(groupPosition).name);
            return convertView;
        }

        @Override
        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
            ChildViewHolder childViewHolder = null;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_textview_layout, null);
                childViewHolder = new ChildViewHolder(convertView);
                convertView.setTag(childViewHolder);
            }
            childViewHolder = (ChildViewHolder) convertView.getTag();

                        childViewHolder.mChildTitle.setText(mData.get(groupPosition).children.get(childPosition));
            return convertView;
        }

        @Override
        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return false;
        }

        private static class HeaderViewHolder {
            final TextView mGroupHeader;

            private HeaderViewHolder(View group) {
                mGroupHeader = (TextView) group.findViewById(R.id.txv_faq_header_text_layout);
            }
        }

        private static class ChildViewHolder {
            final TextView mChildTitle;

            private ChildViewHolder(View group) {
                mChildTitle = (TextView) group.findViewById(R.id.txv_faq_textview_layout);
            }
        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
            if (observer != null) {
                super.unregisterDataSetObserver(observer);
            }
        }

        public void setSelectedPosition(int selectedPosition) {
            mSelectedPosition = selectedPosition;
        }
    }

1
2017-11-10 17:12





Widząc, ile lat ma ten post i odpowiedzi, pomyślałem, że wskażę, że jest bardzo miły Biblioteka innych firm ten rodzaj wypełnia tę brakującą lukę. Chociaż opublikowane niestandardowe rozwiązania są dobre, wciąż brakuje im pewnych rzeczy i postępują zgodnie z nieporęcznym projektem wymagającym od programisty wygenerowania struktury danych struktur danych. Czasami po prostu chcesz uporządkować jedną listę w miłe małe grupy bez kłopotów z robieniem tego samemu.

To się nazywa RolodexArrayAdapter i mogą być łatwo wykorzystane w tworzeniu niestandardowych ExpandableListAdapters... bez obaw o wszystkie problemy i funkcje zarządzania danymi. Obsługuje takie metody, jak dodawanie, dodawanie, usuwanie, usuwanie, usuwanie, przechowywanie, sortowanie itp. Obsługuje również bardziej zaawansowane funkcje, takie jak tryb wyboru, filtrowanie i automatyczne rozwijanie grup.

Przykład:

class MovieAdapter extends RolodexArrayAdapter<Integer, MovieItem> {
    public MovieAdapter(Context activity, List<MovieItem> movies) {
        super(activity, movies);
    }

    @Override
    public Integer createGroupFor(MovieItem childItem) {
        //Lets organize our movies by their release year
        return childItem.year;
    }

    @Override
    public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition,
                             boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            //Inflate your view
        }
        //Fill view with data
        return convertView;
    }

    @Override
    public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        if (convertView == null) {
            //Inflate your view
        }
        //Fill view with data
        return convertView;
    }

    @Override
    public boolean hasAutoExpandingGroups() {
        return true;
    }

    @Override
    protected boolean isChildFilteredOut(MovieItem movie, CharSequence constraint) {
        //Lets filter by movie title
        return !movie.title.toLowerCase(Locale.US).contains(
                constraint.toString().toLowerCase(Locale.US));
    }

    @Override
    protected boolean isGroupFilteredOut(Integer year, CharSequence constraint) {
        //Lets filter out everything whose year does not match the numeric values in the constraint.
        return TextUtils.isDigitsOnly(constraint) && !year.toString().contains(constraint);
    }
}

1
2018-02-08 22:26