アダプターが更新された後、LinearLayoutManagerを備えたRecyclerViewに、特定のアイテムのスクロール位置を表示します。 (最初/最後の位置ではありません)最初の(再)レイアウトを意味します。この特定の位置は可視領域にある必要があります。位置0を上にしてレイアウトし、後で目的の位置までスクロールしないでください。
私のアダプターはitemCount = 0で始まり、そのデータをスレッドにロードして、後で実際のカウントを通知します。ただし、カウントがまだ0の間に開始位置を設定しておく必要があります。
現在のところ、ある種の_post Runnable
_ contains scrollToPosition
を使用しましたが、これには副作用があり(最初のposから始まり、すぐにターゲット位置(0-> target)にジャンプします)、DiffUtil( 0->ターゲット-> 0))
編集:明確にするために:layoutManager.setStackFromEnd(true);
のようなsetStackFrom(position)
の代わりが必要です。 ScrollToPositionは、itemCountがまだ0のときに呼び出すと機能しないため、無視されます。 itemCountが> 0になったことを通知したときにそれを呼び出すと、0からレイアウトされ、ターゲット位置にジャンプします。そして、DiffUtil.DiffResult.dispatchUpdatesTo(adapter) `を使用すると、完全に失敗します。 (0から表示され、次にターゲット位置までスクロールしてから、再び位置0に戻ります)
私は自分で解決策を見つけました:
LayoutManagerを拡張しました。
_ class MyLayoutManager extends LinearLayoutManager {
private int mPendingTargetPos = -1;
private int mPendingPosOffset = -1;
@Override
public void onLayoutChildren(Recycler recycler, State state) {
if (mPendingTargetPos != -1 && state.getItemCount() > 0) {
/*
Data is present now, we can set the real scroll position
*/
scrollToPositionWithOffset(mPendingTargetPos, mPendingPosOffset);
mPendingTargetPos = -1;
mPendingPosOffset = -1;
}
super.onLayoutChildren(recycler, state);
}
@Override
public void onRestoreInstanceState(Parcelable state) {
/*
May be needed depending on your implementation.
Ignore target start position if InstanceState is available (page existed before already, keep position that user scrolled to)
*/
mPendingTargetPos = -1;
mPendingPosOffset = -1;
super.onRestoreInstanceState(state);
}
/**
* Sets a start position that will be used <b>as soon as data is available</b>.
* May be used if your Adapter starts with itemCount=0 (async data loading) but you need to
* set the start position already at this time. As soon as itemCount > 0,
* it will set the scrollPosition, so that given itemPosition is visible.
* @param position
* @param offset
*/
public void setTargetStartPos(int position, int offset) {
mPendingTargetPos = position;
mPendingPosOffset = offset;
}
}
_
目標位置を格納する場合があります。 onLayoutChildren
がRecyclerViewによって呼び出された場合、itemCountがすでに0より大きいかどうかを確認します。trueの場合、scrollToPositionWithOffset()
を呼び出します。
そのため、どの位置を表示する必要があるかすぐにわかりますが、Adapterに位置が存在する前にLayoutManagerに通知されません。
あなたはこれを試すことができます、それはあなたが望む位置にスクロールします:
rv.getLayoutManager().scrollToPosition(positionInTheAdapter).
上記の方法はどれも私にとってはうまくいきませんでした。子が追加/表示が変更されたときにトリガーされるViewTreeObserverを使用して、次のことを行いました。
recyclerView.apply {
adapter = ...
layoutManager = ...
val itemCount = adapter?.itemCount ?: 0
if(itemCount > 1) {
viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
viewTreeObserver.removeOnGlobalLayoutListener(this)
(layoutManager as? LinearLayoutManager)?.scrollToPosition(#PositionToStartAt)
}
}
}
次に、#PositionToStartAtを特定の値に設定します。また、特定の数の子が配置されたら、RecyclerViewの初期位置設定が確実にトリガーされ、正しく設定されていることを確認できます。
if(recyclerView.childCount() > #PositionCheck) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
(layoutManager as? LinearLayoutManager)?.scrollToPosition(#PositionToStartAt)
}
特定の位置にスクロールする必要があり、その位置がアダプターの位置である場合は、StaggeredGridLayoutManager
scrollToPosition
を使用できます。
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
staggeredGridLayoutManager.scrollToPosition(10);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
私が質問を理解している場合、特定の位置までスクロールしたいのですが、その位置はアダプターの位置であり、RecyclerViewのアイテムの位置ではありません。
これは、LayoutManager
を通じてのみ達成できます。
rv.getLayoutManager().scrollToPosition(youPositionInTheAdapter);