android开发游记:酷炫的启动页面之如何实现两个ViewPager的联动
来源:互联网 发布:酷鱼网淘宝店 编辑:程序博客网 时间:2024/05/17 09:39
效果图:
这是在开源项目上看到的一个效果,感觉很不错就加入使用了,其中用到了两个ViewPager的联动做出了很不错的效果,分享一下。
分享一篇文章:
如何实现两个ViewPager的联动
这里是开源项目的下载地址,感谢jianghejie的分享:
LinkedViewPager
文章讲的很清楚,我总结一下重点,另外我下载了源码,并把它稍作修改封装成了一个控件方便在项目中使用:
封装成控件的LinkedViewPager
实现的思路其实很简单,由于ViewPager没有通过移动距离回调的接口,所以我们就只能修改v4包中的源码了,在原来的ViewPager中再内置一个ViewPager,跟踪ViewPager的事件处理流程,在滑动主ViewPager的时候也使内置的ViewPager滑动,这样我们不用作任何处理就能达到我们的目的了。实现过程很简单,下面是具体的修改:
在ViewPager中增加一个内置的ViewPager:
private ViewPager mFollowViewPager; public void setFlolwViewPager(ViewPager page){ mFollowViewPager = page;}
ViewPager在手指未松开的时候使用performDrag处理视图平移,在performDrag中的pageScrolled代码行后,插入我们的内置ViewPager滚动代码:
final float pageOffset = scrollX / width; if(mFollowViewPager!=null){ mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), mFollowViewPager.getScrollY()); }
在手指松开的时候,ViewPager还是会滚动一段距离所以这里也需要做相应处理,在MotionEvent.ACTION_UP事件中的setCurrentItemInternal代码行后加入我们的滚动事件代码:
if(mFollowViewPager!=null){ mFollowViewPager.setCurrentItemInternal(nextPage, true, true, initialVelocity);}
到这里,对ViewPager的改造就完成了,接下来就是使用了
在使用LinkedViewPager的地方给ViewPager设置内部的联动ViewPager:
mPager.setFollowViewPager(mFollowViewPager);
这样就完成了联动的ViewPager了。
如果你是使用的LZ封装成控件的代码,那么你在Activity中不要做任何事情,只需要在相应的布局文件中使用控件就OK了:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".UserGuideActivity" > <com.jcodecraeer.linkedviewpager.LinkedViewPager.MyLinkViewPager android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > </com.jcodecraeer.linkedviewpager.LinkedViewPager.MyLinkViewPager></RelativeLayout>
下面,给上原文:
联动ViewPager的意思就是当一个viewpager在滑动的时候,另外一个ViewPager也跟着滑动,而且两者是同步的。
如果ViewPager有关于移动距离的回调接口,这事儿就好办了,遗憾的是没有,只有一个OnPageChangeListener,我试过在OnPageChangeListener中根据onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的参数来做,但是失败了。
那就只有自定义ViewPager了。
我直接将ViewPager的源码冲v4中拿出来,去掉不必要的一些东西,直到不会再出现找不到类为止,
除了需要将ViewPager拿出来之外,还需要把相关的PagerAdapter类也拿出来,不然ViewPager使用的是自己的而adapter用的是v4中的,可能会出问题。
为了实现联动,在ViewPager中增加一个private变量mFollowViewPager(同时增加变量的set方法):
private ViewPager mFollowViewPager; public void setFlolwViewPager(ViewPager page){ mFollowViewPager = page;}
mFollowViewPager表示的是随着当前ViewPager滚动的另一个ViewPager。
我的想法是在当前ViewPager滚动的相关代码处,调用mFollowViewPager的scrollTo方法。 那么在哪里加入比较好呢,经过仔细跟踪ViewPager的行为,我发现当手指未松开的时候,performDrag方法处理相关的移动,他调用了自己的scrollTo来实现自身的平移,因此我们只需要在performDrag方法中加入如下代码:
//add by jcodecraeerfinal float pageOffset = scrollX / width;if(mFollowViewPager!=null){mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), >mFollowViewPager.getScrollY());}
注意,并不是主ViewPager移动了多远,mFollowViewPager就移动多远,因为两个ViewPager的宽度可能不一样,所以需要转换一下,上面的代码中final float pageOffset = scrollX / width;pageOffset
就只转换得到的值。改写后的performDrag如下:
private boolean performDrag(float x) { boolean needsInvalidate = false; final float deltaX = mLastMotionX - x; mLastMotionX = x; float oldScrollX = getScrollX(); float scrollX = oldScrollX + deltaX; final int width = getWidth(); float leftBound = width * mFirstOffset; float rightBound = width * mLastOffset; boolean leftAbsolute = true; boolean rightAbsolute = true; final ItemInfo firstItem = mItems.get(0); final ItemInfo lastItem = mItems.get(mItems.size() - 1); if (firstItem.position != 0) { leftAbsolute = false; leftBound = firstItem.offset * width; } if (lastItem.position != mAdapter.getCount() - 1) { rightAbsolute = false; rightBound = lastItem.offset * width; } if (scrollX < leftBound) { if (leftAbsolute) { float over = leftBound - scrollX; needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width); } scrollX = leftBound; } else if (scrollX > rightBound) { if (rightAbsolute) { float over = scrollX - rightBound; needsInvalidate = mRightEdge.onPull(Math.abs(over) / width); } scrollX = rightBound; } // Don't lose the rounded component mLastMotionX += scrollX - (int) scrollX; scrollTo((int) scrollX, getScrollY()); pageScrolled((int) scrollX); //add by jcodecraeer final float pageOffset = scrollX / width; if(mFollowViewPager!=null){ mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), mFollowViewPager.getScrollY()); } return needsInvalidate;}
光处理了手指未离开屏幕阶段的平移还不够,手指松开了,ViewPager还会自己继续一定一段距离,因此mFollowViewPager也应该跟着移动,我们想下,手指松开是不是该在 case MotionEvent.ACTION_UP中处理的呢?
我们找到相关代码:
case MotionEvent.ACTION_UP: if (mIsBeingDragged) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( velocityTracker, mActivePointerId); mPopulatePending = true; final int width = getWidth(); final int scrollX = getScrollX(); final ItemInfo ii = infoForCurrentScrollPosition(); final int currentPage = ii.position; final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor; final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final int totalDelta = (int) (x - mInitialMotionX); int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, totalDelta); setCurrentItemInternal(nextPage, true, true, initialVelocity); mActivePointerId = INVALID_POINTER; endDrag(); needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); }
其中,setCurrentItemInternal(nextPage, true, true, initialVelocity)是关键,他的代码如下:
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { if (mAdapter == null || mAdapter.getCount() <= 0) { setScrollingCacheEnabled(false); return; } if (!always && mCurItem == item && mItems.size() != 0) { setScrollingCacheEnabled(false); return; } if (item < 0) { item = 0; } else if (item >= mAdapter.getCount()) { item = mAdapter.getCount() - 1; } final int pageLimit = mOffscreenPageLimit; if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) { // We are doing a jump by more than one page. To avoid // glitches, we want to keep all current pages in the view // until the scroll ends. for (int i=0; i<mItems.size(); i++) { mItems.get(i).scrolling = true; } } final boolean dispatchSelected = mCurItem != item; populate(item); scrollToItem(item, smoothScroll, velocity, dispatchSelected);}
可以看到setCurrentItemInternal中调用了scrollToItem(item, smoothScroll, velocity, dispatchSelected);来实现手指松开后的继续平移效果。也就是说对于mFollowViewPager,如果我们也同样调用setCurrentItemInternal就可以使他也跟着移动了。照着这个思路我们改写case MotionEvent.ACTION_UP的代码段:
case MotionEvent.ACTION_UP: if (mIsBeingDragged) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( velocityTracker, mActivePointerId); mPopulatePending = true; final int width = getWidth(); final int scrollX = getScrollX(); final ItemInfo ii = infoForCurrentScrollPosition(); final int currentPage = ii.position; final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor; final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final int totalDelta = (int) (x - mInitialMotionX); int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, totalDelta); setCurrentItemInternal(nextPage, true, true, initialVelocity); //add by jcodecraeer if(mFollowViewPager!=null){ mFollowViewPager.setCurrentItemInternal(nextPage, true, true, initialVelocity); } mActivePointerId = INVALID_POINTER; endDrag(); needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); }
至此,我们完成了所有的修改,其实也没改几行。
那么在activity中如何使用改造后的ViewPager让两个ViewPager联动呢?假设有一个是mViewPager,有一个是mFollowViewPager,我想让mFollowViewPager随着mViewPager动,则:
mPager.setFollowViewPager(mFollowViewPager);
需要注意的是在我接下来给出的demo中,我屏蔽了followViewPager的所有触摸事件,让主ViewPager覆盖在followViewPager之上,这跟我要实现的效果稳合的。如果你要让followViewPager也能反过来使主ViewPager也能跟着移动不妨反过来调用:
mFollowViewPager.setFollowViewPager(mPager);
但是我不确定这种双向调用是否会出现问题,因为我并没有很严格的考虑从mFollowViewPager变量在移动过后本应该导致的一些状态变化(比如相关的变量)。读者可以试一试,然后改进。
关于ViewPager被改造的地方都用add by jcodecraeer 标注(不包括为了删除的那些不必要的代码)
- android开发游记:酷炫的启动页面之如何实现两个ViewPager的联动
- 如何实现两个ViewPager的联动
- android开发之两个ViewPager联动
- Android之TabLayout+ViewPager+Fragment实现标题栏与页面联动
- ViewPager实现APP的引导页面(小圆点联动)
- 最简单的两个ViewPager的联动
- Android开发之ViewPager结合Fragment实现滑动页面的效果(源代码分享)
- android开发游记:自定义实现图片轮播器和启动页面滚动
- 页面上对于两个selelct实现联动的效果
- android开发游记:DrawerLayout 实现抽屉效果的导航菜单
- Android开发之ViewPager实现多页面切换及动画效果(仿Android的Launcher效果)
- Android开发之ViewPager滑动页面效果实现(源代码分享)
- Android开发之用ViewPager实现欢迎引导页面
- Android开发笔记之ViewPager+Fragment简单列子的实现
- TabLayout+ViewPager实现联动的小问题
- TabLayout和ViewPager的联动效果实现
- Android实现两个ScrollView互相联动,同步滚动的效果
- Android : ViewPager+RecyclerView的联动效果
- 什么叫javaBean
- 洛谷1268树的重量(树)
- java中euals和==的区别
- Eclipse自动部署项目到Tomcat的webapps下的有效方法
- extjs官方实例收集
- android开发游记:酷炫的启动页面之如何实现两个ViewPager的联动
- 文件处理
- PHP中文乱码的常见解决方法总结
- 包装类
- 最大似然估计(Maximum likelihood estimation)
- 集合
- 机器学习(Machine Learning)&深度学习(Deep Learning)资料(Chapter 2)
- ubuntu12.04LTS系统升级到ubuntu14.04LTS后无法正常启动问题
- 驱动编程学习杂谈