リバースエンドレススクロールでListViewに追加するときにスクロール位置を維持する
-
21-12-2019 - |
質問
ハングアウトと同様に、チャットのようなAndroidアプリケーションを構築しています。この目的のために、stackFromBottom=true
とtranscriptMode="normal"
を使用して垂直ListViewを使用しています。
リストは古いメッセージ(上)から若年メッセージ(下)にソートされています。通常の状態では、ListViewが最下位にスクロールされ、最年少のメッセージが表示されます。
ListViewは、ユーザーがリストの先頭にスクロールすると、逆エンドレススクロールアダプタを使用して(古い)メッセージを使用します。古いメッセージがロードされると、それらはリストの先頭に追加されます。
希望の動作は、ListViewがリストの上部に追加されたときに、リストビューがリストの下部にスクロール位置を維持することです。つまり、古いメッセージが先頭に追加されると、Scroll-Viewは古いメッセージを追加する前に表示されているのと同じメッセージを表示する必要があります。
残念ながら、これは機能しません。スクロール位置を下部に維持する代わりに、 ListViewはスクロール位置をリストの上部に維持します。つまり、リストに古いメッセージを追加した後、リストには最上位(すなわち、最も古い)メッセージが表示されます。
新しいアイテムを上に追加するときに、ListViewに下部にスクロール位置を維持するための簡単な方法はありますか?
更新
デモ用の目的で、私は使用症とコードをできるだけ最小限に抑えました。次の例では、単純な文字列配列リストを作成します。アイテムをクリックすると、リストの上部にある別の項目が追加されます。アイテムをロングクリックすると、リストの下部にある別の項目が追加されます。
リストの下部にあるアイテムを追加すると、すべてがうまく機能します(ロングクリック)。リストの上部にある項目を追加するとき(シンプルクリック)、3つのケースがあります。
- リストが下部にスクロールされた場合は、すべてがうまく機能します。上部に項目を追加した後、リストはまだ下部にスクロールされます。
- リストが下部にスクロールされていない場合(または上位)、リストはスクロールy位置を維持します(上から)。これにより、リストは1つの項目を「ジャンプ」しているように見えます。この場合、リストが「ジャンプアップ」しないようにしたいのですが、すなわち、下からスクロールyの位置を維持したいのですが。
- リストが上部にスクロールされている場合は、ケース2と同じようになります。推奨される動作はケース2と同じです。
(最後の2例は実際に同じです。ケース2で問題をよりよく説明できるため、私はそれらを分離しました。)
コード
public class MyListActivity extends Activity {
int topIndex = 1;
int bottomIndex = 20;
protected final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_list_activity);
final ListView list = (ListView) findViewById(R.id.list);
list.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
list.setStackFromBottom(true);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1);
for (int i = topIndex; i <= bottomIndex; i++) {
adapter.add(Integer.toString(i));
}
list.setAdapter(adapter);
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
adapter.insert(Integer.toString(--topIndex), 0);
}
});
list.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
adapter.add(Integer.toString(++bottomIndex));
return true;
}
});
}
}
. 解決
私はこれを考え出しました。どこでも、アダプタのデータを変更する前に、新しいデータを取得しているということです。
int firstVisibleItem = list.getFirstVisiblePosition();
int oldCount = adapter.getCount();
View view = list.getChildAt(0);
int pos = (view == null ? 0 : view.getBottom());
.
それから私は呼び出します:
adapter.setData(data);
list.setSelectionFromTop(firstVisibleItem + adapter.getCount() - oldCount + 1, pos);
. 他のヒント
最初にFirstVisiblePosition(または逆さまに最後のものを手に入れてください.YOが自分で試してチェックしてからチェックし、その後、リストに追加してからsetseを使用するビューの先頭を取得します。ICTROMTOP()メソッド、希望の場所までスクロールできます。
コード:
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0); //or upside down (list.size - 1)
int top = (v == null) ? 0 : v.getTop(); //returns the top of the view its Y co-ordinates. [Doc][1]
mList.setSelectionFromTop(index, top);
.
追加情報を考慮して編集する:
あなたの問題を見つけたと思います。XMLの唯一のビューとしてスタンドアロンリストを使用して、記載されているユースケースを複製しました:
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
.
レイアウトを親レイアウトにネストして、希望するユースケースに従って問題を修正しました:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</FrameLayout>
. アダプタにレコードを挿入したら、
を使ってみることができます。 yourListView.setSelection(yourAdapter.getCount() - 1);
.