原文地址 https://blog.davidmedenjak.com/android/2017/06/24/viewpager-recyclerview.htmlhtml
先贴最后的效果图,全部完整的代码能够在这里找到,以为不错的能够给个赞java
咱们知道如何经过ViewPager
来展现多页面,但从 support library24.2.0推出后,你能够经过SnapHelper
这个类轻松给RecyclerView
加上相似ViewPager
的效果,这篇文章是告诉你如何给你的RecyclerView
添加page indicators
,若是你有阅读过个人博客的话,你可能知道我接下来会怎么作:android
第一步,初始化你的RecyclerView
,确保你的itemlayout
设置成了layout_width="match_parent"
,不然的话没那么容易一页一页滚动。你的RecyclerView
高度也应该设置成match_parent
,若是是设置成wrap_content
的话要确保你的items
也要有相同的高度。git
把PagerSnapHelper
添加到你的RecyclerView
github
// 给recyclerview添加background color
recyclerView.setBackgroundColor(backgroundColor);
MyAdapter adapter = ...
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(context,
LinearLayoutManager.HORIZONTAL, false));
// 添加PagerSnapHelper
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
复制代码
咱们如今有了个简单的能够翻页滚动的RecyclerView
,当咱们添加一个background color
在这里的话,在底部画白色的decorations
就会比较好看。bash
提示:若是你对于decorations
没有任何了解的话你能够经过 introduction to decorations 入门我是如何简单在items
之间画一个下划线Indicator
。app
下一步咱们须要添加decoration
来画indicator
,咱们建立一个LinePagerIndicatorDecoration
并把它添加到RecyclerViewide
// pager indicator
recyclerView.addItemDecoration(newLinePagerIndicatorDecoration());
复制代码
咱们的decoration
要关注2个方法测试
getItemOffsets
:给每一个ItemView添加padding,不会出现overlaying动画
onDrawOver
:在上层画decoration
,绘制的内容在itemview上层。
我喜欢用getItemOffsets
方法来确保个人items
没有overdraw
,若是你的indicator
倾向overdraw
,你能够忽略这个getItemOffsets
方法。咱们作 的一切是须要indicatorHeight
的偏移在每一个View
的底部。若是你使用 GridLayoutManager
,你须要确保你的items
仅仅只是在底部偏移了
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.bottom = indicatorHeight;
}
复制代码
这个在底部的偏移也是我为何设置了一个RecyclerView
的background
而不是pages
上面, 这个偏移让咱们的decoration
在content
留有一点距离,因此设置一个background color
在pages
的item
上面的话会没有效果。若是你不偏移你的items
和 overlay
他们的话,你也就不须要给RecyclerView
设置background
。
接下来咱们把indicators
画给咱们的pages
。咱们把indicator
置于RecyclerView
的底部中间,而且给每一个item
画直线,每一个直线之间有一些padding
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int itemCount = parent.getAdapter().getItemCount();
//水平居中, 计算宽度减去距离中间的一半
float totalLength = mIndicatorItemLength * itemCount;
float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;
float indicatorTotalWidth = totalLength + paddingBetweenItems;
float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;
// 在剩下的space垂直居中
float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2F;
drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);
}
private void drawInactiveIndicators(Canvas c, float indicatorStartX,
float indicatorPosY, int itemCount) {
mPaint.setColor(colorInactive);
// item indicator的宽度包含padding
final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;
float start = indicatorStartX;
for (int i = 0; i < itemCount; i++) {
// 给每一个item画下划线
c.drawLine(start, indicatorPosY,
start + mIndicatorItemLength, indicatorPosY, mPaint);
start += itemWidth;
}
}
复制代码
这个地方给了咱们机会给每一个item
画一个标记,可是这些标记在page
是选中后的时候还不是高亮的。接下来咱们计算咱们滚动了多远来实现一个水平滚动的动画而且把高亮的indicator
画出来。
咱们经过LayoutManager
找出当前活动的page
,而后计算滑动距离的百分比。这个计算方法在你的Views
宽度设置成了match_parent
的时候会颇有效简单,不然的话可能会有不肯定的状况。为了改善体验我使用了AccelerateDecelerateInterpolator
来处理这个获得的百分比progress
的值,让它看起来更加天然。
//找到活动的page,它这时候的下划线应该是高亮的
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
int activePosition = layoutManager.findFirstVisibleItemPosition();
if (activePosition == RecyclerView.NO_POSITION) {
return;
}
// 找到活动的page偏移的距离 (若是用户滑动了)
final View activeChild = layoutManager.findViewByPosition(activePosition);
int left = activeChild.getLeft();
int width = activeChild.getWidth();
// 滑动时候活动的item位置在[-width, 0]
// 滑动时候加入平滑动画
float progress = mInterpolator.getInterpolation(left * -1 / (float) width);
复制代码
经过这个百分比progress
咱们就能够画这个高亮的indicator
,它表明着用户滚动到了哪一个page
.咱们使用这个百分比progress
来画这个局部高亮选中的indicator
它表明咱们滚动到了哪一个page
。
public void onDrawOver(Canvas c, RecyclerView parent,
RecyclerView.State state) {
super.onDrawOver(c, parent, state);
// 画正常状态下的下划线
// ...计算百分比 ...
// 画高亮状态下的下划线
drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress, itemCount);
}
private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,
int highlightPosition, float progress, int itemCount) {
mPaint.setColor(colorActive);
// 每一个item的下划线是包括padding
final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;
if (progress == 0F) {
//百分比为0没有滑动的话第一个画高亮下划线
float highlightStart = indicatorStartX + itemWidth * highlightPosition;
c.drawLine(highlightStart, indicatorPosY,
highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);
} else {
float highlightStart = indicatorStartX + itemWidth * highlightPosition;
// 计算局部高亮下划线的长度
float partialLength = mIndicatorItemLength * progress;
// 画断开的下划线
c.drawLine(highlightStart + partialLength, indicatorPosY,
highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);
// 画高亮的下划线 覆盖在下一个item
if (highlightPosition < itemCount - 1) {
highlightStart += itemWidth;
c.drawLine(highlightStart, indicatorPosY,
highlightStart + partialLength, indicatorPosY, mPaint);
}}
}
复制代码
经过上面全部步骤,咱们达到了预期的indicator
指示器,在RecyclerView
正确的实现page
效果
全部完整的代码能够在这里找到
你可能发现了,我选择线条来代替圆圈作indicator
指示器,可是画圆圈经过动画来设置他们的alpha
也是能够轻松实现相似的效果的。经过使用相似的方法你能够在decorations
作不少事,你不须要修改代码就能够拿来重复使用。
这个解决方案只是一个尝试,可能还有一些潜在的错误。正如文中提到的,肯定这个progress
的方法在不一样宽度的时候可能就不许确,更好的方法多是须要在SnapHelper
内部去作处理。若是你选择使用这个在你的APP的话要确保有足够的测试。