博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【译】RecyclerView+ItemDecorations实现带指示器ViewPager效果
阅读量:7038 次
发布时间:2019-06-28

本文共 5402 字,大约阅读时间需要 18 分钟。

原文地址 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 colorrecyclerView.setBackgroundColor(backgroundColor);MyAdapter adapter = ...recyclerView.setAdapter(adapter);recyclerView.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL, false));// 添加PagerSnapHelperPagerSnapHelper snapHelper = new PagerSnapHelper();snapHelper.attachToRecyclerView(recyclerView);复制代码

我们现在有了个简单的可以翻页滚动的RecyclerView,当我们添加一个background color 在这里的话,在底部画白色的decorations就会比较好看。

添加Pager Indicator

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

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

// pager indicatorrecyclerView.addItemDecoration(newLinePagerIndicatorDecoration());复制代码

我们的decoration要关注2个方法

getItemOffsets:给每个ItemView添加padding,不会出现overlaying

onDrawOver:在上层画decoration ,绘制的内容在itemview上层。

我喜欢用getItemOffsets方法来确保我的items没有overdraw,如果你的indicator 倾向overdraw,你可以忽略这个getItemOffsets方法。我们做 的一切是需要indicatorHeight的偏移在每个View的底部。如果你使用 GridLayoutManager,你需要确保你的items仅仅只是在底部偏移了

@Overridepublic void getItemOffsets(Rect outRect, View view,RecyclerView parent, RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);outRect.bottom = indicatorHeight;}复制代码

这个在底部的偏移也是我为什么设置了一个RecyclerViewbackground而不是pages上面, 这个偏移让我们的decorationcontent留有一点距离,所以设置一个background colorpagesitem上面的话会没有效果。如果你不偏移你的itemsoverlay他们的话,你也就不需要给RecyclerView 设置background

接下来我们把indicators画给我们的pages。我们把indicator置于RecyclerView的底部中间,并且给每个item画直线,每个直线之间有一些padding

@Overridepublic 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的宽度包含paddingfinal 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的下划线是包括paddingfinal 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的话要确保有足够的测试。

转载地址:http://yenal.baihongyu.com/

你可能感兴趣的文章
Testlink安装使用
查看>>
Android系统性能调优工具介绍
查看>>
数值类型与字节数组之间的相互转换
查看>>
uWSGI + Django @ Ubuntu
查看>>
iOS微信分享功能简单实现
查看>>
传入参数方法,参数值变化
查看>>
Mongodb Sharding Cluster 三台
查看>>
C Primer Plus 第7章 C控制语句:分支和跳转 7.2 if语句中添加 else 关键字
查看>>
ORACLE下删除当前用户下所有对象
查看>>
获取宽度高度常用javascript代码总结
查看>>
远程无法复制文件解决办法
查看>>
Spring boot with Oracle
查看>>
运算符重载的另一个解决方案:类型转换
查看>>
Linux下排查JVM的CPU偏高问题
查看>>
Alamofire4.x开源代码分析(二)请求参数和编码
查看>>
Android设计模式系列(10)--SDK源码之原型模式
查看>>
Eclipse中开启java的assert选项
查看>>
Centos7部署springboot.jar项目
查看>>
内存溢出与内存泄露
查看>>
对象的共享
查看>>