14

我一直在尝试在网上寻找有关如何做到这一点的任何解决方案或示例,但无法找到任何类似于我的问题的东西。

当数据更改时,我有一个LinearLayout想要添加/删除的地方。ViewsArrayList

据我了解,唯一的方法是CustomView通过扩展AdapterView和使用来创建ArrayAdapter.

不幸的是,我不了解解决此问题的正确数据流。

我在哪里指定CustomView哪个 View 是容器?我可以CustomViewLinearLayout何时实施它吗?

编辑:我强调 - 我不需要ListView. 我需要它CustomView

4

2 回答 2

21

您无需扩展 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!");
    }

(对于所有其他添加/删除方法等等),或者通过覆盖它们来操作适配器的支持数据集(应该强制实现您的自定义接口,允许在适配器接口旁边进行此类修改)。在这两种情况下,请记住在代码中从超类调用 add remove 方法来处理适配器。

编辑:以及扩展 LinearLayout 并支持适配器的 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;
    }
}
于 2013-02-05T01:43:20.717 回答
1

看看AdapterView的实现。对你来说,它主要是扩展 DataSetobserver 第 794 行。

只是给您一个粗略的想法,
1)在您的自定义视图类中创建扩展 DataSetObserver 的类。

2)从您的自定义视图中声明您自己的接口以获取视图/数据详细信息,并让您的 CustomAdapter 实现它。

3) 每当您的自定义中的 DatSetObserver 的 Array 调用 onChanged()/onInvalidated() 发生变化时。

如果您对 AdapterView 非常认真,请点击此链接

于 2013-01-31T18:15:25.217 回答