フィルタリング後に ListView が更新されない
-
26-09-2019 - |
質問
ListView (setTextFilterEnabled(true) 付き) とカスタム アダプター (ArrayAdapter を拡張) があり、新しい項目が追加/挿入されるたびにメイン UI スレッドから更新します。最初はすべて正常に動作し、新しい項目がすぐにリストに表示されます。ただし、これはリストをフィルタリングしようとした瞬間に停止します。
フィルタリングは機能しますが、一度フィルタリングを行うと、それ以降にリストの内容を変更 (追加、削除) しようとしても表示されなくなります。ログを使用して、アダプタのリスト データが適切に更新されるかどうかを確認しました。実際には更新されていますが、表示されている ListView と同期していません。
これの原因と、問題に対処する最善の方法はありますか?
解決
私はArrayAdapterの実際のソースコードを経て、それが実際にそのように動作するように書かれていたように見えます。
mObjectsとmOriginalValues:ArrayAdapterで始まるするには、2つのリストを持っています。 mObjectsは、アダプタと連携することを主要なデータセットです。例えば、追加()関数を取る
public void add(T object) {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.add(object);
if (mNotifyOnChange) notifyDataSetChanged();
}
} else {
mObjects.add(object);
if (mNotifyOnChange) notifyDataSetChanged();
}
}
すべての操作(追加、挿入、削除、クリアな)は、デフォルトでmObjectsに標的にされているので、mOriginalValuesは、最初はnullです。これは、リスト上でフィルタリングを有効にして、実際には1つを実行することを決定した時点までのすべての罰金です。初めてのフィルタリングはどんな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)である - フィルタ処理が完了した場合、コピー単に[もBennySkogbergによって示唆されるように]すべての操作の後にフィルタを強制的に.getFilter()フィルタ(「」)*アダプタのフィルタオブジェクトを呼び出して、空の文字列を渡すmObjectsにmOriginalValuesの内容、または(2)上。
誰もが私は何をしたか、この問題または確認の上、いくつかのより多くの光を当てることができれば、これは非常に高く評価されるだろう。ありがとうございます!
他のヒント
- あなたはnotifyDataSetChanged()メソッドを呼び出していますか?それは、それ自体を再描画するリストビューを強制します。
listAdapter.notifyDataSetChanged();
あなたが(例えば:画像の遅延ロード)何かをする時はいつでもちょうどこれを使用しています。リストビューを更新するために、
は、問題は私の<のhref =「http://code.google.com/p/android/issues/detail?id=9666#c5」のrel = "nofollowのうち、2010年7月以来チェック欠陥として報告されます「>コメント#5 の
まず第一に、@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()では、正規表現の使用法を簡単に実装したり、ケースに敏感なフィルターとして使用したりできます。
の代わりに 通知データセット変更 あなたは電話するでしょう updateList() .
の代わりに myListView.getFilter().filter('フィルタテキスト') あなたは電話するでしょう CustomFilterList('フィルターテキスト')
現時点では、これは 1 つの ListView でのみ機能します。おそらく、少し作業すれば、これをカスタム配列アダプターとして実装できるでしょう。
私は同じ問題に苦しんでいました。それから私は、 StackOverflowの上でこののポストます。
が見つかり回答@MattDavis投稿では、入ってくるのArrayListは、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元のArrayListは、フィルタリングされたデータは、同様にgetCountとのgetItemが呼び出されるときに使用されるデータセットであるが、渡されたことを記憶されている場合
私自身のArrayAdapter IではのgetItemが呼び出されたとき、元のデータを返す続けました。したがって、リストビューは、基本的には同様にgetCountを(呼び出し)やアイテムの量は(ないフィルタリングされたデータ)私の元のデータと一致していることを見てのgetItemは、したがって、完全に新しいフィルタデータセットを無視して、元のデータから要求された索引付きアイテムになるだろうでしょう。
実際には、だけではなく、フィルターセットで、オリジナルのArrayListから更新を保管するときます。 notifyDatasetChanged呼び出しは、リストビューを更新していなかったようにそれが見えた理由これはあります
ホープ、これは将来的に他の誰かのために、この問題を明確にするのに役立ちます。
私が間違っている場合は、私はまだ毎日学んでおりますので、正しい私にはお気軽にどうぞ!