15

ListView(setTextFilterEnabled(true)を使用)とカスタムアダプター(ArrayAdapterを拡張)があり、新しいアイテムが追加/挿入されるたびにメインUIスレッドから更新します。最初はすべて正常に機能します。新しいアイテムがすぐにリストに表示されます。ただし、リストをフィルタリングしようとすると、これは停止します。

フィルタリングは機能しますが、一度実行すると、リストの内容を変更(追加、削除)しようとした後のすべての試行が表示されなくなります。ログを使用して、アダプターのリストデータが適切に更新されているかどうかを確認しましたが、更新されていますが、表示されているListViewと同期していません。

これを引き起こしている原因と問題に対処するための最善の方法はありますか?

4

5 に答える 5

16

ArrayAdapterの実際のソースコードを調べたところ、実際にはそのように動作するように作成されているようです。

ArrayAdapterには、mObjectsとmOriginalValuesの2つのリストがあります。mObjectsは、アダプターが機能するプライマリデータセットです。add()関数を例にとると、次のようになります。

public void add(T object) {
    if (mOriginalValues != null) {
        synchronized (mLock) {
            mOriginalValues.add(object);
            if (mNotifyOnChange) notifyDataSetChanged();
        }
    } else {
        mObjects.add(object);
        if (mNotifyOnChange) notifyDataSetChanged();
    }
}

mOriginalValuesは最初はnullであるため、すべての操作(追加、挿入、削除、クリア)はデフォルトでmObjectsを対象としています。これは、リストでフィルタリングを有効にして実際に実行することを決定するまでは、すべて問題ありません。初めてフィルタリングすると、mObjectsが持つものでmOriginalValuesが初期化されます。

private class ArrayFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence prefix) {
        FilterResults results = new FilterResults();

        if (mOriginalValues == null) {
            synchronized (mLock) {
                mOriginalValues = new ArrayList<T>(mObjects);
                //mOriginalValues is no longer null
            }
        }

        if (prefix == null || prefix.length() == 0) {
            synchronized (mLock) {
                ArrayList<T> list = new ArrayList<T>(mOriginalValues);
                results.values = list;
                results.count = list.size();
            }
        } else {
            //filtering work happens here and a new filtered set is stored in newValues
            results.values = newValues;
            results.count = newValues.size();
        }

        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        //noinspection unchecked
        mObjects = (List<T>) results.values;
        if (results.count > 0) {
            notifyDataSetChanged();
        } else {
            notifyDataSetInvalidated();
        }
    }
}

mOriginalValuesには、元の値/アイテムのコピーが含まれるようになったため、アダプターは、事前にフィルター処理されたデータを失うことなく、mObjectsを介してフィルター処理されたリストを表示できます。

私の考えが間違っている場合は許してください(そして教えて説明してください)が、mOriginalValuesがnullでなくなったため、アダプター操作の後続のすべての呼び出しはmOriginalValuesのみを変更するため、これは奇妙だと思います。ただし、アダプターはmObjectsをプライマリデータセットとして参照するように設定されているため、何も起きていないことが画面に表示されます。それは、別のフィルタリングラウンドを実行するまでです。フィルタを削除すると、次のようになります。

if (prefix == null || prefix.length() == 0) {
            synchronized (mLock) {
                ArrayList<T> list = new ArrayList<T>(mOriginalValues);
                results.values = list;
                results.count = list.size();
            }
        }

最初のフィルターから変更してきたmOriginalValues(画面上では表示されていませんが)は別のリストに保存され、mObjectsにコピーされ、最終的に行われた変更が表示されます。それでも、この時点からは次のようになります。すべての操作はmOriginalValuesで実行され、変更はフィルタリング後にのみ表示されます。

解決策として、現時点で私が思いついたのは、(1)進行中のフィルタリングがあるかどうかをアダプターの操作に通知するブールフラグを設定することです。フィルタリングが完了したら、内容をコピーします。 mOriginalValuesをmObjectsに変換するか、(2)アダプターのFilterオブジェクトを呼び出し、空の文字列* .getFilter()。filter( "")を渡して、すべての操作の後にフィルターを強制します[BennySkogbergによっても提案されています]。

誰かがこの問題にもう少し光を当てるか、私が今何をしたかを確認することができれば非常にありがたいです。ありがとうございました!

于 2010-08-05T20:34:14.647 に答える
0

私があなたの問題をよく理解しているなら-あなたはnotifyDataSetChanged()メソッドを呼び出しますか?これにより、リストビューが強制的に再描画されます。

listAdapter.notifyDataSetChanged();

リストビューを更新するために何か(たとえば、画像の遅延読み込み)を行うときはいつでも、これを使用してください。

于 2010-08-05T12:45:13.637 に答える
0

この問題は2010年7月以降の欠陥として報告されています。私のコメント#5をチェックしてください。

于 2012-12-19T22:56:47.727 に答える
0

まず第一に、@jhieの答えはとても良いことです。しかし、それらの事実は私の問題を実際には解決しませんでした。しかし、内部で使用されるメカニズムがどのように機能するかについての知識の助けを借りて、私はそれを独自のフィルタリング関数で書くことができました:

public ArrayList<String> listArray = new ArrayList<>();
public ArrayList<String> tempListArray = new ArrayList<>();
public ArrayAdapter<String> tempAdapter;
public String currentFilter;

onCreateで:

// Fill my ORIGINAL listArray;
listArray.add("Cookies are delicious.");

// After adding everything to listArray, I add everything to tempListArray
for (String element : listArray){
    tempListArray.add(element);
}

// Setting up the Adapter...
tempAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, tempListArray);
myListView.setAdapter(tempAdapter);

フィルタリング:

// Defining filter functions
public void customFilterList(String filter){
        tempListArray.clear();
        for (String item : listArray){
            if (item.toLowerCase().contains(filter.toLowerCase())){
                tempListArray.add(item);
            }
        }
        currentFilter = filter;
        tempAdapter.notifyDataSetChanged();
    }
    public void updateList(){
        customFilterList(currentFilter);
    }

リストに検索機能を実装するために使用しました。

大きな利点:フィルタリング能力が大幅に向上します。customFilterList()を使用すると、正規表現の使用法を簡単に実装したり、大文字と小文字を区別するフィルターとして使用したりできます。

notifyDataSetChangedの代わりに、 updateList ()を呼び出します。

myListView.getFilter()。filter('filter text')の代わりに、 customFilterList('filter text')を呼び出します

現時点では、これは1つのListViewでのみ機能します。たぶん、少しの作業で、これをカスタムアレイアダプタとして実装できます。

于 2017-06-07T10:31:15.000 に答える
0

私は同じ問題に苦しんでいました。次に、StackOverflowでこの投稿を見つけました。

@MattDavisが投稿した回答では、着信配列リストは2つの異なる配列リストに割り当てられています。

 public PromotionListAdapter(Activity a, ArrayList<HashMap<String, String>> d) 
{
    activity = a;
    inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader = new ImageLoader(activity.getApplicationContext());

    //To start, set both data sources to the incoming data
    originalData = d;
    filteredData = d;
}

originalDataとfilteredData。

originalDataは、渡された元の配列リストが格納される場所です。一方、フィルター処理されたデータは、getCountおよびgetItemが呼び出されたときに使用されるデータセットです。

私自身のArrayAdapterでは、getItemが呼び出されたときに元のデータを返し続けました。したがって、ListViewは基本的にgetCount()を呼び出し、アイテムの量が元のデータ(フィルター処理されたデータではない)と一致することを確認し、getItemは元のデータから要求されたインデックス付きアイテムを取得するため、新しいフィルター処理されたデータセットを完全に無視します。

これが、notifyDatasetChanged呼び出しがListViewを更新していないように見えた理由ですが、実際には、フィルター処理されたセットではなく、元の配列リストから更新を続けていました。

これが将来誰かのためにこの問題を明らかにするのに役立つことを願っています。

私はまだ毎日学んでいるので、私が間違っている場合は、遠慮なく訂正してください!

于 2018-06-11T07:33:29.540 に答える