Создание адаптера для CustomView

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

У меня есть LinearLayout, где я хочу добавить/удалить Views при изменении данных ArrayList.

Насколько я понимаю, единственный способ - создать CustomView, расширив AdapterView и используя ArrayAdapter.

К сожалению, я не понимаю правильный поток данных для решения этой проблемы.

Где указать в CustomView, какой вид является контейнером? Могу ли я просто нарисовать CustomView на LinearLayout, когда я его выполнил?

EDIT: Я подчеркиваю - мне не нужен ListView. Мне нужно это для CustomView

Ответы

Ответ 1

Вам не нужно расширять AdapterView для создания ваших представлений из адаптера в вашем пользовательском представлении. Вы можете расширить LinearLayout и обработать адаптер. Самое простое решение будет выглядеть так:

public class CustomAdapterView extends LinearLayout {

    private Adapter adapter;
    private final DataSetObserver observer = new DataSetObserver() {

        @Override
        public void onChanged() {
            refreshViewsFromAdapter();
        }

        @Override
        public void onInvalidated() {
            removeAllViews();
        }
    };

    public CustomAdapterView(Context context) {
        super(context);
    }

    public CustomAdapterView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomAdapterView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public Adapter getAdapter() {
        return adapter;
    }

    public void setAdapter(Adapter adapter) {
        if (this.adapter != null) {
            this.adapter.unregisterDataSetObserver(observer);
        }
        this.adapter = adapter;
        if (this.adapter != null) {
            this.adapter.registerDataSetObserver(observer);
        }
        initViewsFromAdapter();
    }

    protected void initViewsFromAdapter() {
        removeAllViews();
        if (adapter != null) {
            for (int i = 0; i < adapter.getCount(); i++) {
                addView(adapter.getView(i, null, this), i);
            }
        }
    }

    protected void refreshViewsFromAdapter() {
        int childCount = getChildCount();
        int adapterSize = adapter.getCount();
        int reuseCount = Math.min(childCount, adapterSize);

        for (int i = 0; i < reuseCount; i++) {
            adapter.getView(i, getChildAt(i), this);
        }

        if (childCount < adapterSize) {
            for (int i = childCount; i < adapterSize; i++) {
                addView(adapter.getView(i, null, this), i);
            }
        } else if (childCount > adapterSize) {
            removeViews(adapterSize, childCount);
        }
    }
}

Поскольку приведенный выше код является лишь простым примером, он не обрабатывает ситуацию, когда адаптер возвращает представления разных типов (например, Adapter#getViewTypeCount() возвращает число больше 1).

Конечно, все методы, определенные LinearLayout для добавления/удаления представлений, доступны, чтобы они могли столкнуться с обработкой вашего адаптера. Вы можете отключить их, сбросив UnsupportedOperationException:

    @Override
    public void addView(View child) {
        throw new UnsupportedOperationException(
                "You cannot add views directly without adapter!");
    }

(и т.д. для всех других методов добавления/удаления) или путем переопределения их для манипулирования набором данных поддержки адаптера (который должен быть принудительно реализован с помощью пользовательского интерфейса, допускающего такие модификации рядом с интерфейсом Adapter). В обоих случаях не забудьте вызвать добавить методы удаления из суперкласса в свой код для обработки адаптера.

EDIT: И простая реализация, расширяющая LinearLayout с поддержкой Adapter viewTypesCount:

class CustomAdapterViewTypedImpl extends LinearLayout {

    private Adapter adapter;
    private SparseArray<List<View>> typedViewsCache = new SparseArray<List<View>>();
    private final DataSetObserver observer = new DataSetObserver() {

        @Override
        public void onChanged() {
            refreshViewsFromAdapter();
        }

        @Override
        public void onInvalidated() {
            removeAllViews();
        }
    };

    public CustomAdapterViewTypedImpl(Context context) {
        super(context);
    }

    public CustomAdapterViewTypedImpl(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomAdapterViewTypedImpl(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public Adapter getAdapter() {
        return adapter;
    }

    public void setAdapter(Adapter adapter) {
        if (this.adapter != null) {
            this.adapter.unregisterDataSetObserver(observer);
        }
        this.adapter = adapter;
        if (this.adapter != null) {
            this.adapter.registerDataSetObserver(observer);
        }
        initViewsFromAdapter();
    }

    protected void initViewsFromAdapter() {
        typedViewsCache.clear();
        removeAllViews();
        View view;
        if (adapter != null) {
            for (int i = 0; i < adapter.getCount(); i++) {
                view = adapter.getView(i, null, this);
                addToTypesMap(adapter.getItemViewType(i), view, typedViewsCache);
                addView(view, i);
            }
        }
    }

    protected void refreshViewsFromAdapter() {
        SparseArray<List<View>> typedViewsCacheCopy = typedViewsCache;
        typedViewsCache = new SparseArray<List<View>>();
        removeAllViews();
        View convertView;
        int type;
        for (int i = 0; i < adapter.getCount(); i++) {
            type = adapter.getItemViewType(i);
            convertView = shiftCachedViewOfType(type, typedViewsCacheCopy);
            convertView = adapter.getView(i, convertView, this);
            addToTypesMap(type, convertView, typedViewsCache);
            addView(convertView, i);
        }
    }

    private static void addToTypesMap(int type, View view, SparseArray<List<View>> typedViewsCache) {
        List<View> singleTypeViews = typedViewsCache.get(type);
        if(singleTypeViews == null) {
            singleTypeViews = new ArrayList<View>();
            typedViewsCache.put(type, singleTypeViews);
        }
        singleTypeViews.add(view);
    }

    private static View shiftCachedViewOfType(int type, SparseArray<List<View>> typedViewsCache) {
        List<View> singleTypeViews = typedViewsCache.get(type);
        if(singleTypeViews != null) {
            if(singleTypeViews.size() > 0) {
                return singleTypeViews.remove(0);
            }
        }
        return null;
    }
}

Ответ 2

Взгляните на AdapterView. Для вас он в основном расширяет строку DataSetobserver 794.

Просто, чтобы дать вам приблизительную идею,
1) Создайте класс, расширяющий DataSetObserver внутри класса Custom View.

2) Объявите свой собственный интерфейс из своего пользовательского представления для получения сведений о представлении/данных и пусть ваш CustomAdapter реализует его.

3) Всякий раз, когда вы изменяете свой вызов Array onChanged()/onInvalidated() DatSetObserver в своем обычае.

Если вы действительно серьезно относитесь к AdapterView, следуйте этой ссылке.