笔者要实现的效果描述如下:
一组图片横向排列,焦点图片在屏幕中间展示,两端要显示上个图片和下个图片的部分,具体效果如下面图片所示:
图一. 图片滑到最右边时的效果
图二. 图片滑到最左边时的效果
图三. 图片滑到中间时的效果
最终采用RecyclerView + PagerSnapHelper来实现此种效果,这里只记录几个要点。
1、自定义PagerSnapHelper,使得要显示在屏幕中央的图片可以居中显示,主要实现方法 calculateDistanceToFinalSnap()
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) targetView.getLayoutParams();
int position = params.getViewAdapterPosition();
int left = targetView.getLeft();
int right = targetView.getRight();
int top = targetView.getTop();
int bottom = targetView.getBottom();
ViewGroup viewGroup = (ViewGroup) targetView.getParent();
int[] out = new int[]{0, 0};
boolean isLastItem;
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && right == viewGroup.getMeasuredWidth();
if (layoutManager.canScrollHorizontally()) {
out[0] = distanceToCenter(layoutManager, targetView,
OrientationHelper.createHorizontalHelper(layoutManager));//***这里让图片居于屏幕的中间***
out[1] = 0;
}
} else {
isLastItem = position == layoutManager.getItemCount() - 1/*最后一个*/ && bottom == viewGroup.getMeasuredHeight();
out[0] = 0;
out[1] = top;
}
mCurrentPosition = position;
mOnPageListener.onPageSelector(mCurrentPosition);
mOnPageListener.onPageSelector(currentPosition, mCurrentPosition);
}
return out;
}
private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView, OrientationHelper helper) {
//找到targetView的中心坐标
final int childCenter = helper.getDecoratedStart(targetView) +
(helper.getDecoratedMeasurement(targetView) / 2);
final int containerCenter;
//找到容器(RecyclerView)的中心坐标
if (layoutManager.getClipToPadding()) {
containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
} else {
containerCenter = helper.getEnd() / 2;
}
//两个中心坐标的差值就是targetView需要滚动的距离
return childCenter - containerCenter;
}
2、除了第一步的操作之外,还需要进行额外操作,否则会发现图片两端是撑到屏幕边缘的,那么如何让图片两端可以露出上一个图片和下一个图片的一部分呢?这里可以在图片recyclerView的adapter中,设置下图片item的宽度来实现。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View convertView = LayoutInflater.from(mContext).inflate(R.layout.home_adapter_item, parent, false);
RelativeLayout rootRl = (RelativeLayout) convertView.findViewById(R.id.rl_root);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(scrrenWidth - DensityUtil.dp2px(40f), DensityUtil.dp2px(150f));//***这里设备图片item根布局的固定高度为150dp,宽度为屏幕宽度减去40dp,设置合适的宽度就可以让前一幅图片和后一幅图片露出一部分了;***
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
rootRl.setLayoutParams(layoutParams);
return new HomeRoomAdapter.RoomViewHolder(convertView);
}
3、实现文章开头展示的效果,还缺最后一个步,即如何让第一幅图片的左边距要大些,让最后一幅图片的右边距要大些?这里可以修改下自定义的RecyclerView.ItemDecoration,调整下第一个位置的左边距和最后一个位置的右边距来实现。
public class HomeSpaceItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public HomeSpaceItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int childCount = parent.getAdapter().getItemCount();
if (childCount > 1) {
if (position == 0) {
outRect.left = space * 4;//***第一个item距离屏幕左边距要设置的大些***
outRect.right = space;
} else if (position == childCount - 1) {
outRect.left = space;
outRect.right = space * 4;//***最后一个item距离屏幕右边距要设置的大些***
} else {
outRect.left = space;//中间item的左右边距
outRect.right = space;
}
}
}
}
至此,经过上面三个步骤,文章顶部的图示效果就可以实现啦。