RecyclerViewはGalleryギャラリー効果を実現
7558 ワード
RecyclerViewを使用してギャラリー効果を実現し、主にsupportライブラリに新しく追加されたPagerSnapHelperクラスを使用して、スライドオフセットを計算することでscaleの値を計算します.
基本的な実装
まず、RecyclerViewにスクロールリスニングを追加し、RecyclerViewの最初のitemViewと最後のitemViewにItemDecorationを追加して、最初のitemViewと最後のitemViewの位置を中央に揃えます.
ScalableCardItemDecorationは、itemViewの左右の空き領域を計算し、最初のitemViewと最後のitemViewにオフセット量を追加するために使用されます.
itemにItemDecorationを追加する場合は、両方のスペースを計算する必要があります.ここでは、itemViewの幅を手動で測定し、最初のitemViewの左側のオフセットと最後のitemViewの右側のオフセットを計算する必要があります.ここで問題があります.LayoutMangaerの側面が垂直または水平で、RecyclerViewに対応する高さまたは幅がwrap_に設定されている場合contentの場合、ここで計算した値は正しくありません.
ItemDecorationを追加した後、RecyclerViewがスクロールするたびに左、中、右の3つのitemViewのスケールを計算する必要があります.
スケーリングの計算は、左、中、右の3つのitemViewの中間点とRecyclerViewの中間点との距離から計算します.
プロジェクトのアドレス:https://github.com/yjwfn/recyclerview-gallery
基本的な実装
まず、RecyclerViewにスクロールリスニングを追加し、RecyclerViewの最初のitemViewと最後のitemViewにItemDecorationを追加して、最初のitemViewと最後のitemViewの位置を中央に揃えます.
public void attachToRecyclerView(final RecyclerView recyclerView) {
this.recyclerView = recyclerView;
snapHelper.attachToRecyclerView(recyclerView);
recyclerView.addOnScrollListener(scrollListener);
recyclerView.addItemDecoration(new ScalableCardItemDecoration());
recyclerView.post(new Runnable() {
@Override
public void run() {
pageScrolled();
}
});
}
ScalableCardItemDecorationは、itemViewの左右の空き領域を計算し、最初のitemViewと最後のitemViewにオフセット量を追加するために使用されます.
private static class ScalableCardItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
int position = holder.getAdapterPosition() == RecyclerView.NO_POSITION ? holder.getOldPosition() : holder.getAdapterPosition();
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int itemCount = layoutManager.getItemCount();
if(position != 0 && position != itemCount - 1){
return;
}
int peekWidth = getPeekWidth(parent, view);
boolean isVertical = layoutManager.canScrollVertically();
// item adapter position -1。
if (isVertical) {
if (position == 0) {
outRect.set(0, peekWidth, 0, 0);
} else if (position == itemCount - 1) {
outRect.set(0, 0, 0, peekWidth);
} else {
outRect.set(0, 0, 0, 0);
}
} else {
if (position == 0) {
outRect.set(peekWidth, 0, 0, 0);
} else if (position == itemCount - 1) {
outRect.set(0, 0, peekWidth, 0);
} else {
outRect.set(0, 0, 0, 0);
}
}
}
}
itemにItemDecorationを追加する場合は、両方のスペースを計算する必要があります.ここでは、itemViewの幅を手動で測定し、最初のitemViewの左側のオフセットと最後のitemViewの右側のオフセットを計算する必要があります.ここで問題があります.LayoutMangaerの側面が垂直または水平で、RecyclerViewに対応する高さまたは幅がwrap_に設定されている場合contentの場合、ここで計算した値は正しくありません.
public static int getPeekWidth(RecyclerView recyclerView, View itemView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
boolean isVertical = layoutManager.canScrollVertically();
int position = recyclerView.getChildAdapterPosition(itemView);
//TODO RecyclerView wrap_content , 0。
int parentWidth = recyclerView.getMeasuredWidth();
int parentHeight = recyclerView.getMeasuredHeight(); // 0
parentWidth = parentWidth == 0 ? recyclerView.getWidth() : parentWidth;
parentHeight = parentHeight == 0 ? recyclerView.getHeight() : parentHeight;
int parentEnd = isVertical ? parentHeight : parentWidth;
int parentCenter = parentEnd / 2;
int itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth();
if (itemSize == 0) {
ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
int widthMeasureSpec =
RecyclerView.LayoutManager.getChildMeasureSpec(parentWidth,
layoutManager.getWidthMode(),
recyclerView.getPaddingLeft() + recyclerView.getPaddingRight(),
layoutParams.width, layoutManager.canScrollHorizontally());
int heightMeasureSpec =
RecyclerView.LayoutManager.getChildMeasureSpec(parentHeight,
layoutManager.getHeightMode(),
recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(),
layoutParams.height, layoutManager.canScrollVertically());
itemView.measure(widthMeasureSpec, heightMeasureSpec);
itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth();
}
/*
ItemDecoration , view start + itemSize / 2 parentCenter。
*/
int startOffset = parentCenter - itemSize / 2;
int endOffset = parentEnd - (startOffset + itemSize);
return position == 0 ? startOffset : endOffset;
}
ItemDecorationを追加した後、RecyclerViewがスクロールするたびに左、中、右の3つのitemViewのスケールを計算する必要があります.
private void pageScrolled() {
if (recyclerView == null || recyclerView.getChildCount() == 0)
return;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
View snapingView = snapHelper.findSnapView(layoutManager);
int snapingViewPosition = recyclerView.getChildAdapterPosition(snapingView);
View leftSnapingView = layoutManager.findViewByPosition(snapingViewPosition - 1);
View rightSnapingView = layoutManager.findViewByPosition(snapingViewPosition + 1);
float leftSnapingOffset = calculateOffset(recyclerView, leftSnapingView);
float rightSnapingOffset = calculateOffset(recyclerView, rightSnapingView);
float currentSnapingOffset = calculateOffset(recyclerView, snapingView);
if (snapingView != null) {
snapingView.setScaleX(currentSnapingOffset);
snapingView.setScaleY(currentSnapingOffset);
}
if (leftSnapingView != null) {
leftSnapingView.setScaleX(leftSnapingOffset);
leftSnapingView.setScaleY(leftSnapingOffset);
}
if (rightSnapingView != null) {
rightSnapingView.setScaleX(rightSnapingOffset);
rightSnapingView.setScaleY(rightSnapingOffset);
}
if(snapingView != null && currentSnapingOffset >= 1){
OnPageChangeListener listener = pageChangeListenerRef != null ? pageChangeListenerRef.get(): null;
if(listener != null)
listener.onPageSelected(snapingViewPosition);
}
Log.d(TAG, String.format("left: %f, right: %f, current: %f", leftSnapingOffset, rightSnapingOffset, currentSnapingOffset));
}
スケーリングの計算は、左、中、右の3つのitemViewの中間点とRecyclerViewの中間点との距離から計算します.
private float calculateOffset(RecyclerView recyclerView, View view) {
if (view == null)
return -1;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
boolean isVertical = layoutManager.canScrollVertically();
int viewStart = isVertical ? view.getTop() : view.getLeft();
int viewEnd = isVertical ? view.getBottom() : view.getRight();
int centerX = isVertical ? recyclerView.getHeight() / 2 : recyclerView.getWidth() / 2;
int childCenter = (viewStart + viewEnd) / 2;
int distance = Math.abs(childCenter - centerX);
if (distance > centerX)
return STAY_SCALE;
float offset = 1.f - (distance / (float) centerX);
return (1.f - STAY_SCALE) * offset + STAY_SCALE;
}
プロジェクトのアドレス:https://github.com/yjwfn/recyclerview-gallery