概要:カスタム アダプター ラッパーを介して ListView に見出し行を動的に追加しようとしています。ListView で、スクロール位置の同期を維持するのに問題があります。実行可能なデモ プロジェクトが提供されます。
CursorAdapter の値に基づいてリストにアイテムを動的に追加したいと思います。ユーザーが現在表示している位置よりも数個先です。これを行うために、CursorAdapter をラップし、新しいコンテンツのインデックスを SparseArray に保持するアダプターがあります。項目がカスタム アダプターに追加されたときに ListView を更新する必要がありますが、それを機能させようとして多くの落とし穴に遭遇したので、アドバイスをいただければ幸いです。
デモ プロジェクトは、 DynamicSectionedList.zipからダウンロードできます。
デモでは、10 か所先を見て、リスト項目が次の文字に切り替わる正しい位置を見つけることによって、見出しが動的に追加されます。notifyDataSetChanged の各実装には、次のような問題があります。
デモ 1
このデモは、notifyDataSetChanged() の重要性を示しています。何かをクリックすると、アプリがクラッシュします。これは ListView... での健全性チェックによるものですmItemCount != adapter.getItemCount()
。道徳的には、データが変更されたことをリストに通知する必要があります。
デモ 2
自然な次のステップは、変更が発生したときに ListView に変更を通知することです。残念ながら、ListView がしっかりとスクロールしているときにこれを行うと、アプリがタッチ モードから切り替わるまで、すべてのタッチ操作が中断されます。これに気付くには、新しい見出しを生成するのに十分な距離まで「フリング スクロール」する必要があります。画面をタップしてもスクロールは停止せず、停止するとリスト項目はクリックできなくなります。これはif (!mDataChanged) { /* do very important stuff */ }
、AbsListView.onTouchEvent() の一部のコードが原因です。
デモ 3 これを修正するために、デモ 3 では pendingChanges フラグが導入され、カスタム アダプタは、変更に対して「安全な」状態になったときに ListView から呼び出すことができる notifyDataSetChangedIfNeeded() を取得します。変更を通知する必要がある最初のポイントは ListView.layoutChildren() にあるため、このメソッドをオーバーライドして、必要に応じて最初に変更を通知し、次に呼び出すようにしました。少なくとも 1 つの見出しを通り過ぎてから、リスト項目をクリックします。
理由は完全にはわかりませんが、これは正しく機能しません。キーボード/トラックボールで項目をタップまたは選択すると、古い位置が適切に同期されずにリストが更新されます。受け入れられないリストの一番上までスクロールします。
デモ 4 デモ 3 のスクロールの問題は、少なくともタッチ モードでは克服できます。タッチダウン時に notifyDataSetChangedIfNeeded() への呼び出しを追加することで、すべてのタッチ操作が期待どおりに機能し、リストの位置が適切に同期されるようなときに、データの変更が発生します。
ただし、デバイスがタッチモードでない場合、そのアナログを見つけることができません。もちろん、ハックのように見えることは言うまでもありません。ほとんどの場合、リストは一番上にスクロールして戻りますが、時折正しい位置を維持する原因がわかりません。
Android はあらゆる段階で私と戦っているので、より良いアプローチが必要だと感じています。修正を適用して動作させることができる場合は、デモをお試しください。
これを調べることができる人に感謝します。コードを機能させることができれば、見出し付きのリストに対して同じ最適化を達成しようとしている他の人にとって役立つことを願っています。