Google ハングアウトのチャット インターフェイスに似たインターフェイスを構築しています。新しいメッセージはリストの一番下に追加されます。リストの一番上までスクロールすると、以前のメッセージ履歴が読み込まれます。履歴がネットワークから受信されると、それらのメッセージがリストの一番上に追加され、ロードがトリガーされたときにユーザーが停止した位置から、いかなる種類のスクロールもトリガーされません。つまり、「読み込みインジケーター」がリストの上部に表示されます。
これは、ロードされた履歴にその場で置き換えられます。
私はこれをすべて機能させています... 達成するためにリフレクションに頼らなければならなかった1つのことを除いて. ListView にアタッチされたアダプターにアイテムを追加するときに、スクロール位置を保存して復元するだけの質問と回答がたくさんあります。私の問題は、次のようなことをするときです(簡略化されていますが、一目瞭然です):
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
@Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
次に、ユーザーに表示されるのは、ListView の上部へのクイック フラッシュであり、次に正しい場所へのクイック フラッシュです。この問題はかなり明白で、多くの人が発見してsetSelection()
います。そのため、ビューに描画の機会を与える必要があります。しかし、それはひどいようです。notifyDataSetChanged()
ListView
post()
リフレクションを使用して「修正」しました。私はそれが嫌いです。本質的に、私が達成したいのは、位置を設定した後ListView
まで、描画サイクルのリガマロールを通過せずに、 の最初の位置をリセットすることです。これを行うには、ListView: の便利なフィールドがあります。ところで、それはまさに私が調整する必要があるものです! 残念ながら、それはパッケージプライベートです。また残念ながら、プログラムで設定したり、無効化サイクルを伴わない方法で影響を与えたりする方法はないようです...醜い動作をもたらします。mFirstPosition
したがって、失敗時のフォールバックを伴うリフレクション:
try {
Field field = AdapterView.class.getDeclaredField("mFirstPosition");
field.setAccessible(true);
field.setInt(listView, positionToSave);
}
catch (Exception e) { // CATCH ALL THE EXCEPTIONS </meme>
e.printStackTrace();
listView.post(new Runnable() {
@Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
}
それは機能しますか?はい。それは恐ろしいですか?はい。将来的に機能しますか?知るか?より良い方法はありますか?それが私の質問です。
リフレクションなしでこれを達成するにはどうすればよいですか?
答えは、「これを処理できる独自のものを作成する」かもしれません。のコードを見たListView
かどうかだけお尋ねします。ListView
編集:Luksprogのコメント/回答に基づくリフレクションのない実用的なソリューション。
LuksprogはOnPreDrawListener()
. 魅力的な!以前に ViewTreeObservers をいじったことがありますが、これらのいずれかをいじったことはありません。いくつかいじった後、次のタイプのものが完全に機能するように見えます。
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
@Override
public void run() {
listView.setSelection(positionToSave);
}
});
listView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if(listView.getFirstVisiblePosition() == positionToSave) {
listView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
else {
return false;
}
}
});
}
とてもかっこいい。