リバースエンドレススクロールでListViewに追加するときにスクロール位置を維持する

StackOverflow https://stackoverflow.com//questions/22051556

質問

ハングアウトと同様に、チャットのようなAndroidアプリケーションを構築しています。この目的のために、stackFromBottom=truetranscriptMode="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);
.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top