14

その方法に関する解決策や例をオンラインで探してみましたが、私の問題に似たものを見つけることができませんでした。

データが変更されたときLinearLayoutに追加/削除したい場所があります。ViewsArrayList

私が理解している限り、唯一の方法は をCustomView拡張AdapterViewして使用することArrayAdapterです。

残念ながら、この問題を解決するための適切なデータフローがわかりません。

CustomViewどのビューがコンテナであるかはどこで指定できますか? CustomView実装したLinearLayoutときにキャストできますか?

編集:私は強調します - 私は必要ありません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 メソッドを呼び出すことを忘れないでください。

編集:アダプタのviewTypesCountをサポートするLinearLayoutを拡張する簡単な実装:

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) Custom View クラス内に DataSetObserver を拡張するクラスを作成します。

2) ビュー/データの詳細を取得するためにカスタム ビューから独自のインターフェイスを宣言し、CustomAdapter にそれを実装させます。

3) 配列呼び出しに変更があるたびに、カスタムの DatSetObserver の onChanged()/onInvalidated() を呼び出します。

AdapterView について真剣に考えている場合は、このリンクをたどってください。

于 2013-01-31T18:15:25.217 に答える