無限の「タイムピッカーカルーセル」にScrollview
を使用していますが、これは最良のアプローチではないことがわかりました( last question )
今、私は Recycler View を見つけましたが、recyclerViewのX方向の現在のスクロールオフセットを取得できませんか? (各アイテムが100ピクセルの幅で、2番目のアイテムは50%にしか見えないため、スクロールオフセットは150ピクセルです)
recyclerView.getScrollX()
は0
を返します(ドキュメントによると:初期スクロール値)LayoutManager
にはfindFirstVisibleItemPosition
がありますが、それでXオフセットを計算できませんUPDATE
onScrolled
コールバックで値を更新しながら、X-Positionを追跡し続ける方法を見つけましたが、常に追跡するのではなく、実際の値を取得する方がいいでしょう!
private int overallXScroll = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
Log.i("check","overallXScroll->" + overallXScroll);
}
});
解決策1:setOnScrollListener
X-Positionをクラス変数として保存し、onScrollListener
内の各変更を更新します。 overallXScroll
(f.e. onScreenRotationChange
)をリセットしないでください。
private int overallXScroll = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
Log.i("check","overall X = " + overallXScroll);
}
});
解決策2:現在の位置を計算します。
私の場合、時間値(0:00、0:30、1:00、1:30 ... 23:00、23:30)で満たされた水平リストがあります。画面の中央にある時間項目(計算ポイント)から時間を計算しています。だからこそ、RecycleView
の正確なXスクロール位置が必要です。
最初の項目(ヘッダー項目)には追加のパディングがあり、中央に0分を設定します
private int mScreenWidth = 0;
private int mHeaderItemWidth = 0;
private int mCellWidth = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init recycle views
//...
LinearLayoutManager mLLM = (LinearLayoutManager) getLayoutManager();
DisplayMetrics displaymetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
this.mScreenWidth = displaymetrics.widthPixels;
//calculate value on current device
mCellWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources()
.getDisplayMetrics());
//get offset of list to the right (gap to the left of the screen from the left side of first item)
final int mOffset = (this.mScreenWidth / 2) - (mCellWidth / 2);
//HeaderItem width (blue rectangle in graphic)
mHeaderItemWidth = mOffset + mCellWidth;
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//get first visible item
View firstVisibleItem = mLLM.findViewByPosition(mLLM.findFirstVisibleItemPosition());
int leftScrollXCalculated = 0;
if (firstItemPosition == 0){
//if first item, get width of headerview (getLeft() < 0, that's why I Use Math.abs())
leftScrollXCalculated = Math.abs(firstVisibleItem.getLeft());
}
else{
//X-Position = Gap to the right + Number of cells * width - cell offset of current first visible item
//(mHeaderItemWidth includes already width of one cell, that's why I have to subtract it again)
leftScrollXCalculated = (mHeaderItemWidth - mCellWidth) + firstItemPosition * mCellWidth + firstVisibleItem.getLeft();
}
Log.i("asdf","calculated X to left = " + leftScrollXCalculated);
}
});
}
RecyclerViewにはすでに水平および垂直スクロールオフセットを取得するメソッドがあります
mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()
これは、同じ高さ(垂直スクロールオフセット用)と同じ幅(水平スクロールオフセット用)のセルを含むRecyclerViewで機能します。
正しいリードをくれた@ umar-qureshiに感謝します! Offset
、Extent
、およびRange
を使用してスクロール率を決定できるようです。
percentage = 100 * offset / (range - extent)
例(OnScrollListener
に入れる):
int offset = recyclerView.computeVerticalScrollOffset();
int extent = recyclerView.computeVerticalScrollExtent();
int range = recyclerView.computeVerticalScrollRange();
int percentage = (int)(100.0 * offset / (float)(range - extent));
Log.i("RecyclerView, "scroll percentage: "+ percentage + "%");
public class CustomRecyclerView extends RecyclerView {
public CustomRecyclerView(Context context) {
super(context);
}
public CustomRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public int getHorizontalOffset() {
return super.computeHorizontalScrollOffset();
}
}
オフセットを取得する必要がある場所より:
recyclerView.getHorizontalOffset()
RecyclerViewの幅と比較して現在のページとそのオフセットを知る必要がある場合は、次のようなものを試してみてください。
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final int scrollOffset = recyclerView.computeHorizontalScrollOffset();
final int width = recyclerView.getWidth();
final int currentPage = scrollOffset / width;
final float pageOffset = (float) (scrollOffset % width) / width;
// the following lines just the example of how you can use it
if (currentPage == 0) {
leftIndicator.setAlpha(pageOffset);
}
if (adapter.getItemCount() <= 1) {
rightIndicator.setAlpha(pageOffset);
} else if (currentPage == adapter.getItemCount() - 2) {
rightIndicator.setAlpha(1f - pageOffset);
}
}
});
currentPage
のインデックスが0の場合、leftIndicator
(矢印)は透明になり、rightIndicator
が1つだけの場合はpage
(この例では、アダプターは常に空の値をチェックしないように、少なくとも1つのアイテムを表示します)。
このようにすると、コールバックpageScrolled
from ViewPager.OnPageChangeListener
を使用した場合と基本的にほぼ同じ機能を使用できます。
recyclerView onScrolled(int dx,int dy)
をオーバーライドすることにより、スクロールオフセットを取得できます。ただし、アイテムを追加または削除すると、値が間違っている可能性があります。
public class TempRecyclerView extends RecyclerView {
private int offsetX = 0;
private int offsetY = 0;
public TempRecyclerView(Context context) {
super(context);
}
public TempRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TempRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle){
super(context, attrs, defStyle);
}
/**
* Called when the scroll position of this RecyclerView changes. Subclasses
should usethis method to respond to scrolling within the adapter's data
set instead of an explicit listener.
* <p>This method will always be invoked before listeners. If a subclass
needs to perform any additional upkeep or bookkeeping after scrolling but
before listeners run, this is a good place to do so.</p>
*/
@Override
public void onScrolled(int dx, int dy) {
// super.onScrolled(dx, dy);
offsetX+=dx;
offsetY+=dy;
}
}