与ViewPager完美结合的顺滑引导条系列之二
2018-01-30 22:44
260 查看
引言
在上一篇的最后提出了索引滑动时游标不动
一次完整的滑动必须是索引或游标,不能共存
频繁不规则地滑动ViewPager或引导条时,会引起NullPointerException,因为RecyclerView找不到内部的View(所以才重写了dispatchTouchEvent)
这三个需要修改的缺陷。
针对第一点:索引滑动是因为RecyclerView会滑动,游标是添加在RelativeLayout上的,所以游标在响应手势的情况下是不会滑动的
针对第二点:由于一次完整的滑动结束后才会重置状态,导致滑动过程中不能切换滑动对象
针对第三点:由于RecyclerView的回收复用机制,导致无法获得想要项的具体View
从这三个问题出发,这次我改用HorizontalScrollView和View来进行组合,封装控件GuideSlideView。
这里立即就爆出一个可优化的点,控件没有复用,先把他当作缺陷列出,后续改进。
正文
GuideSlideView的布局如下<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <HorizontalScrollView android:id="@+id/horizontalscrollview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:scrollbars="none"> <LinearLayout android:id="@+id/linearlayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" /> </HorizontalScrollView> <View android:id="@+id/lineView" android:layout_width="10dp" android:layout_height="3dp" android:layout_alignParentBottom="true" android:layout_marginBottom="7dp" android:background="#444444" /> </RelativeLayout> </merge>
索引项中的View全部inflate出来后添加到LinearLayout,通过HorizontalScrollView的scrollBy进行与ViewPager的联动。
重点代码分析
horizontalScrollView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (whichOne != 0) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = event.getX(); break; case MotionEvent.ACTION_MOVE: setScrollViewPager(event.getX() - lastX); lastX = event.getX(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: lastX = 0; break; } return true; } return false; } }); public ViewPager.OnPageChangeListener getOnPageChangeListener() { return new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { Log.i("onPageScrolled", "position-->" + position + " positionOffset-->" + positionOffset + " positionOffsetPixels-->" + positionOffsetPixels); setScroll(position, positionOffset); } }; } private void setScroll(int position, float offset) { Log.i("setScroll", "position-->" + position + " offset-->" + offset); if (!whetherOrNotToMove(position, offset)) { return; } if (!positiveDirection) {//根据方向设置偏移 offset = offset - 1; } int x = calculateMoveLength(position, offset); move(x); } private boolean whetherOrNotToMove(int position, float offset) { if (position > currentPosition) {//第一次正向滑动触发 positiveDirection = true; directionPrepare(position); return false; } if (position < currentPosition) {//第一次反向滑动触发 positiveDirection = false; directionPrepare(position); return false; } if (position == 0 && offset == 0) {//滑到第一个 初始状态 positiveDirection = true; directionPrepare(position); return false; } lineView.setVisibility(VISIBLE); ViewHolder viewHolder; if (positiveDirection) { viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition)); } else { viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition)); viewHolder.lineView.setVisibility(INVISIBLE); viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1)); } viewHolder.lineView.setVisibility(INVISIBLE); if (offset == 0) { viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition)); viewHolder.lineView.setVisibility(VISIBLE); lineView.setVisibility(INVISIBLE); } return true; } private void directionPrepare(int position) { resetState(position); if (whichOne != 1) { if (positiveDirection) { layoutLineView(position); } else { layoutLineView(position + 1); } } ViewHolder viewHolder; if (positiveDirection) { viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition)); } else { viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1)); } viewHolder.lineView.setVisibility(VISIBLE); lineView.setVisibility(INVISIBLE); } private void resetState(int position) {//重置状态 currentPosition = position; whichOne = 0; lastx = 0; } private void layoutLineView(int currentPosition) {//将游标布局到指定位置 View item = linearLayout.getChildAt(currentPosition); lineViewMarginLeft = item.getLeft() - horizontalScrollView.getScrollX() + (item.getWidth() - lineView.getWidth()) / 2; RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams(); params.leftMargin = lineViewMarginLeft; lineView.setLayoutParams(params); } private int calculateMoveLength(int position, float offset) { int temp = position; if (temp >= linearLayout.getChildCount()) { temp = linearLayout.getChildCount() - 1; } View currentView = linearLayout.getChildAt(temp);//当前所指view int lastPosition = position + 1; if (lastPosition >= linearLayout.getChildCount()) { lastPosition = linearLayout.getChildCount() - 1; } View nextView = linearLayout.getChildAt(lastPosition);//将要滑动到的view Log.i("viewnull", temp + " currentView-->" + currentView + "----------" + lastPosition + " nextView-->" + nextView); int width = (currentView.getWidth() + nextView.getWidth()) / 2;//计算两view之间的总的滑动距离 return Math.round(width * offset);//此次滑动的距离 } private void move(int x) { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams(); if (whichOne == 0) {//判断 一次完整的滑动 是否结束 一次滑动结束后 whichOne=0 if (positiveDirection) { if (params.leftMargin % screenWidth > screenWidth / 2 && horizontalScrollView.canScrollHorizontally(1)) {//游标在右半边且recyclerView可以右滑 则滑动recyclerView whichOne = whichOne | 1; } else { whichOne = whichOne | 2; } } else { if (params.leftMargin % screenWidth < screenWidth / 2 && horizontalScrollView.canScrollHorizontally(-1)) {//游标在左半边且recyclerView可以左滑 则滑动recyclerView whichOne = whichOne | 1; } else { whichOne = whichOne | 2; } } } if (whichOne == 1) { horizontalScrollView.scrollBy(x - lastx, 0);//因为scrollBy的滑动是累加的,所以必须减去上次滑动的距离,实际移动的距离为差值 exp:第一次滑动1 第二次滑动2,第二次滑动3,scrollBy会滑动6,但实际我只想滑动3 lastx = x; } else { params.leftMargin = lineViewMarginLeft + x; lineView.setLayoutParams(params); } } //GuideSlideView 滑动联动ViewPager private void setScrollViewPager(float length) { viewPager.scrollBy(-Math.round(length), 0); }
同样的重点在onPageScrolled回调中获取位置和偏移量,然后调用setScroll,有点不同的是每个ViewHolder都有自己的游标,滑动结束后,滑动到具体位置时需要控制ViewHolder和满屏跑游标的显示状态。
还有一点不同的就是对HorizontalScrollView设置了OnTouchListener,这是为了满足多触点的需求,当ViewPager滑到一半,另一个触点拖动引导条时,ViewPager会随之滑动。
看过之前一篇文章的话,这里针对滑动的逻辑并没有太大出入,只是用HorizontalScrollView替换了RecyclerView
效果
截图可能不太清楚,当快速滑动时,光标会有闪烁现象
缺陷
控件没有复用快速滑动时,光标会闪
为了解决上述问题见与ViewPager完美结合的顺滑引导条系列之三
相关文章推荐
- 与ViewPager完美结合的顺滑引导条系列之需求分析
- 与ViewPager完美结合的顺滑引导条系列之一
- 与ViewPager完美结合的顺滑引导条系列之三
- Android进阶篇之引导页系列之ViewPager静态引导
- Android进阶篇之引导页系列之ViewPager实现Animation动画引导页
- TabLayout和viewpager的完美结合
- 完美解决Google最新的Design:TabLayout结合ViewPager无法显示Icon的问题
- 最流行框架---viewpager,fragment完美结合
- ViewPager——RadioGroup结合引导页面
- Android UI设计(引导界面):ViewPager之二滑动切换效果
- 【Android UI设计与开发】第02期:引导界面(二)使用ViewPager实现欢迎引导页面
- ViewPager结合PagerAdapter实现在Fragment下的随手指滑动翻页
- android UI进阶之用ViewPager实现欢迎引导页面
- 【完美解决系列】Android在自定义Dialog中TextView内容显示不全的问题
- 【Android】完美解决SwipeRefreshLayout中嵌套ViewPager时滑动冲突的方法
- Android ViewPager实现引导页
- Android应用开发系列课程------ViewPager与ViewFlipper较量
- ViewPager+Fragment的结合使用
- scrollview与viewpager结合的依赖工程
- MagicIndicator系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器