相关文章推荐
健身的莴苣  ·  sql server ...·  9 月前    · 
踢足球的草稿本  ·  java ...·  1 年前    · 
https://blog.davidmedenjak.com/android/2017/06/24/viewpager-recyclerview.html

先贴最后的效果图,所有完整的代码可以在 这里找到 ,觉得不错的可以给个赞

我们知道如何通过 ViewPager 来展示多页面,但从 support library24.2.0 推出后,你可以通过 SnapHelper 这个类轻松给 RecyclerView 加上类似 ViewPager 的效果,这篇文章是告诉你如何给你的 RecyclerView 添加 page indicators ,如果你有阅读过我的博客的话,你可能知道我接下来会怎么做:

Pager 初始化

第一步,初始化你的 RecyclerView ,确保你的 itemlayout 设置成了 layout_width="match_parent" ,否则的话没那么容易一页一页滚动。你的 RecyclerView 高度也应该设置成 match_parent ,如果是设置成 wrap_content 的话要确保你的 items 也要有相同的高度。

PagerSnapHelper 添加到你的 RecyclerView

// 给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 就会比较好看。

添加Pager Indicator

提示:如果你对于 decorations 没有任何了解的话你可以通过 introduction to decorations 入门我是如何简单在 items 之间画一个下划线 Indicator

下一步我们需要添加 decoration 来画 indicator ,我们创建一个 LinePagerIndicatorDecoration 并把它添加到RecyclerView

// 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) / 2 F ; // 在剩下的space垂直居中 float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2 F ; 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 == 0 F) { //百分比为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的话要确保有足够的测试。

分类:
Android
标签: