web-dev-qa-db-ja.com

android:smoothScrollToPosition()が正しく機能していません

リストビューに関連付けられたarrayadapterに要素を追加した後、リストの最後の要素までスムーズにスクロールしようとしています。問題は、ランダムな位置までスクロールするだけだということです

arrayadapter.add(item);
//DOES NOT WORK CORRECTLY:
listview.smoothScrollToPosition(arrayadapter.getCount()-1);

//WORKS JUST FINE:
listview.setSelection(arrayadapter.getCount()-1);
48
user1515520

おそらく、UIスレッドが処理できるときにListViewにスクロールを送信するように指示する必要があります(これが適切にスクロールしない理由です)。 SmoothScrollは、速度/時間などを無視した位置に移動するのではなく、多くの作業を行う必要があります。 (「アニメーション」に必要)。

したがって、次のようなことを行う必要があります。

    getListView().post(new Runnable() {
        @Override
        public void run() {
            getListView().smoothScrollToPosition(pos);
        }
    });
56

(私の答えからコピーされた: smoothScrollToPositionFromTop()は、いつものように常に動作するとは限りません

これは既知のバグです。https://code.google.com/p/Android/issues/detail?id=36062を参照してください。

ただし、発生する可能性のあるすべてのエッジケースを扱うこの回避策を実装しました。

最初にsmothScrollToPositionFromTop(position)を呼び出し、次にスクロールが終了したら、setSelection(position)を呼び出します。後者の呼び出しは、目的の位置に直接ジャンプすることにより、不完全なスクロールを修正します。そうすることで、ユーザーはまだこの位置にアニメーションスクロールされているような印象を受けます。

この回避策を2つのヘルパーメソッド内に実装しました。

smoothScrollToPosition()

public static void smoothScrollToPosition(final AbsListView view, final int position) {
    View child = getChildAtPosition(view, position);
    // There's no need to scroll if child is already at top or view is already scrolled to its end
    if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {
        return;
    }

    view.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(final AbsListView view, final int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                view.setOnScrollListener(null);

                // Fix for scrolling bug
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        view.setSelection(position);
                    }
                });
            }
        }

        @Override
        public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
                                 final int totalItemCount) { }
    });

    // Perform scrolling to position
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            view.smoothScrollToPositionFromTop(position, 0);
        }
    });
}

getChildAtPosition()

public static View getChildAtPosition(final AdapterView view, final int position) {
    final int index = position - view.getFirstVisiblePosition();
    if ((index >= 0) && (index < view.getChildCount())) {
        return view.getChildAt(index);
    } else {
        return null;
    }
}
12
Lars Blumberg

SetSelection()メソッドを使用する必要があります。

6
TaoZang

Larsが言及したセット選択方法は機能しますが、アニメーションは残されたものをすべてスキップするため、私たちの目的にはあまりにも飛び過ぎました。別の解決策は、最初の表示位置がインデックスになるまでメソッドを繰り返し呼び出すことです。それ以外の場合は、ビューをスクロールするユーザーと戦うため、これは迅速かつ制限付きで行うのが最適です。

private  void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) {
    if (listView.FirstVisiblePosition != index && attempts < 10) {
        attempts++;
        listView.SmoothScrollToPositionFromTop (index, 1, 100);
        listView.PostDelayed (() => {
            DeterminedScrollTo (listView, index, attempts);
        }, 100);
    }
}

解決策はC#経由です。 Xamarinですが、Javaに簡単に変換できるはずです。

3
Daniel Roberts

LayoutManagerを使用してスクロールをスムーズにする

layoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);
2
final Handler handler = new Handler();
//100ms wait to scroll to item after applying changes
handler.postDelayed(new Runnable() {
@Override
public void run() {
   listView.smoothScrollToPosition(selectedPosition);
}}, 100);
2
VSB

arrayadapter.notifyDataSetChanged()を呼び出した後にarrayadapter.add()を呼び出しますか?確かに、smoothScrollToPositionsetSelectionは、上記のListViewではなく、arrayadapterで使用できるメソッドです。

いずれにせよ、これが役立つかどうかを確認してください: smoothScrollToPosition after notifyDataSetChanged not working in Android

1
Code Poet