博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突...
阅读量:7079 次
发布时间:2019-06-28

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

如果你还在为处理滑动冲突而发愁,那么你需要静下心来看看这边文章,如果你能彻底理解这篇文章中使用的技术,那么,一切滑动冲突的问题解决起来就轻而易举了:
先扔一个最终实现的效果图

先分析下效果图中实现的功能点
  • 顶部下拉时背景图形成视差效果
  • 上拉时标题栏透明切换显示
  • 底部实现TabLayout+ViewPager+Fragment+RecyclerView
  • NestedScrollView+ViewPager的滑动冲突解决
  • NestedScrollView+RecyclerView滑动冲突的解决
复杂在哪里?整个布局中使用了SmartRefreshLayout,NestedScrollView,ViewPager,RecyclerView,每一个都有滑动事件,我们平时只是使用ScrollView+RecyclerView都会有滑动冲突,更何况这里有四个会引起冲突的控件一起使用! 接下来,我们一步一步实现这个效果 1、布局设计分析
-FrameLayout(最外层)    -ImageView(头部背景图)        -SmartRefreshLayout(头部刷新控件)            -JudgeNestedScrollView(自定义的NestedScrollView)                ...省略中间巴拉巴拉布局                -Tablayout                    -ViewPager
2、功能点实现说明 2.1、下拉时视差效果的实现 最外层为FrameLayout,ImageView高度设置超过屏幕顶部,借助SmartRefreshLayout控件在下拉和松开时头部背景图做平移处理,背景图片做了高斯模糊处理
布局代码:
...
下拉刷新时头部背景图片平移代码:
refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener() {            @Override            public void onHeaderPulling(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {                mOffset = offset / 2;                ivHeader.setTranslationY(mOffset - mScrollY);            }            @Override            public void onHeaderReleasing(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {                mOffset = offset / 2;                ivHeader.setTranslationY(mOffset - mScrollY);            }        });
2.2、TabLayout的顶部悬浮效果的实现 此处使用的是最为简单笨拙的方法,两个TabLayout,一个固定为屏幕顶部ToolBar下面,并隐藏,另一个正常绘制在布局中; 计算ToolBar的高度,根据NestedScrollView滑动的高度(这里的高度指的是跟随滑动的TabLayout的Y坐标)恰好到ToolBar的高度位置时显示隐藏的ToolBar;
-FrameLayout    -SmartRefreshLayout        -Tablayout        -Viewpager    -SmartRefreshLayout    -RelativeLayout        -Toolbar        -Tablayout    -RelativeLayout-FrameLayout
toolbar.post(new Runnable() {            @Override            public void run() {                toolBarPositionY = toolbar.getHeight();            }        });
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {            @Override            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {                int[] location = new int[2];                magicIndicator.getLocationOnScreen(location);                int xPosition = location[0];                int yPosition = location[1];                if (yPosition < toolBarPositionY) {                    toolBarTablayout.setVisibility(View.VISIBLE);                } else {                    toolBarTablayout.setVisibility(View.GONE);                }            }        });
2.3、ToolBar的渐变透明度以及按钮的切换
ToolBar中间标题默认隐藏,使用的ButtonBarLayout包裹ImageView和TextView设置百分比透明,具体处理有两点:
buttonBarLayout.setAlpha(0);toolbar.setBackgroundColor(0);
* 下拉头部刷新时ToolBar渐变隐藏,同样利用SmartRefreshLayout处理
refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener() {            @Override            public void onHeaderPulling(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {                toolbar.setAlpha(1 - Math.min(percent, 1));            }            @Override            public void onHeaderReleasing(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {                toolbar.setAlpha(1 - Math.min(percent, 1));            }        });
* 上下滑动时标题栏渐变显示和隐藏,并切换图标颜色(这里实际上是根据临界点直接更换图片,处理的比较简单)
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {            int lastScrollY = 0;            int h = DensityUtil.dp2px(170);            int color = ContextCompat.getColor(getApplicationContext(), R.color.mainWhite) & 0x00ffffff;            @Override            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {                int[] location = new int[2];                magicIndicator.getLocationOnScreen(location);                int xPosition = location[0];                int yPosition = location[1];                if (lastScrollY < h) {                    scrollY = Math.min(h, scrollY);                    mScrollY = scrollY > h ? h : scrollY;                    buttonBarLayout.setAlpha(1f * mScrollY / h);                    toolbar.setBackgroundColor(((255 * mScrollY / h) << 24) | color);                    ivHeader.setTranslationY(mOffset - mScrollY);                }                if (scrollY == 0) {                    ivBack.setImageResource(R.drawable.back_white);                    ivMenu.setImageResource(R.drawable.icon_menu_white);                } else {                    ivBack.setImageResource(R.drawable.back_black);                    ivMenu.setImageResource(R.drawable.icon_menu_black);                }                lastScrollY = scrollY;            }        });
2.4、NestedScrollView嵌套ViewPager导致ViewPager高度为0的处理 很多人可能认为直接自定义ViewPager,测量子View的高度,让ViewPager去适应高度即可,其实不然,如果这样处理的话我们的Viewpager可能就是无限高度,我们在处理完NestedScrollView后,无限高度的ViewPager和RecyclerView又是一个问题,所以我这里的处理是计算ViewPager所需要的最大高度,即TabLayout在最顶部显示时到屏幕底部的最大高度为ViewPager高度
toolbar.post(new Runnable() {            @Override            public void run() {                toolBarPositionY = toolbar.getHeight();                ViewGroup.LayoutParams params = viewPager.getLayoutParams();                params.height = SDScreenUtil.getScreenHeight() - toolBarPositionY - tablayout.getHeight()+1;                viewPager.setLayoutParams(params);            }        });
这里为什么要+1,后面会有解释 2.5、NestedScrollView嵌套RecyclerView滑动冲突 NestedScrollView嵌套RecyclerView滑动冲突我们使用事件拦截处理,这里处理的是NestedScrollView的滑动,首先滑动的时候肯定是需要NestedScrollView的滑动事件,所以我们默认不拦截NestedScrollView的滑动事件,直到TabLayout顶部悬浮的时候,我们拦截NestedScrollView的滑动事件,交给RecyclerView来处理 * 重写NestedScrollView
public class JudgeNestedScrollView extends NestedScrollView {    private boolean isNeedScroll = true;    ...省略构造方法    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_MOVE:                return isNeedScroll;        }        return super.onInterceptTouchEvent(ev);    }    /*    改方法用来处理NestedScrollView是否拦截滑动事件     */    public void setNeedScroll(boolean isNeedScroll) {        this.isNeedScroll = isNeedScroll;    }}
这里默认不拦截NestedScrollView滑动事件,只有当我们TabLayout滑动到顶部时才去拦截,也就是TabLayout显示隐藏的时候
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {            @Override            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {                int[] location = new int[2];                magicIndicator.getLocationOnScreen(location);                int xPosition = location[0];                int yPosition = location[1];                if (yPosition < toolBarPositionY) {                    tablayout.setVisibility(View.VISIBLE);                    scrollView.setNeedScroll(false);                } else {                    tablayout.setVisibility(View.GONE);                    scrollView.setNeedScroll(true);                }
至于前面测量ViewPager高度的时候,为什么会+1处理,这是因为,如果不+1时,刚好是TabLayout要出现的临界点,也就是ViewPager恰好的高度,但是这个时候又刚好是我们NestedScrollView拦截没有取消的临界点,所以,在上滑的时候,TabLayout刚好悬浮顶部时,RecyclerView没有获取事件,无法进行滑动,这就是给ViewPager+1处理的理由;
2.5、NestedScrollView嵌套ViewPager滑动冲突2 如果你足够细心的话,就会发现,当你的TabLayout上滑到一半的时候,再去左右滑动ViewPager是滑动不了的,因为这个时候NestedScrollView依然消费事件,所以我们还需要对NestedScrollView事件进行处理,判断如果是左右滑动的时候,我们不让NestedScrollView处理,而是交给子View处理,即ViewPager
public class JudgeNestedScrollView extends NestedScrollView {    private boolean isNeedScroll = true;    private float xDistance, yDistance, xLast, yLast;    ...省略构造方法    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                xDistance = yDistance = 0f;                xLast = ev.getX();                yLast = ev.getY();                break;            case MotionEvent.ACTION_MOVE:                final float curX = ev.getX();                final float curY = ev.getY();                xDistance += Math.abs(curX - xLast);                yDistance += Math.abs(curY - yLast);                xLast = curX;                yLast = curY;                if (xDistance > yDistance) {                    return false;                }                return isNeedScroll;        }        return super.onInterceptTouchEvent(ev);    }    /*    改方法用来处理NestedScrollView是否拦截滑动事件     */    public void setNeedScroll(boolean isNeedScroll) {        this.isNeedScroll = isNeedScroll;    }}
至此,完美的解决了所有的问题,当时有些细节这里并没有话费太多的时间去处理,如有任何问题,欢迎各位大佬进行指正 源码:
 

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

你可能感兴趣的文章
EditPlus如何设置保存时不产生.bak备份文件?
查看>>
Mac OSX操作系统安装和配置Zend Server 6教程(4)
查看>>
安装homeassistant+python3.6
查看>>
ubuntu下chrome无法同步问题解决
查看>>
搭建Nginx+Java环境(转)
查看>>
pc端车牌识别在智能机器人上的应用
查看>>
余弦相似度计算
查看>>
Koa (koajs) 基于 Node.js 平台的下一代 web 开发框架
查看>>
大型网站技术架构(六)网站的伸缩性架构
查看>>
多表查询
查看>>
理解作用域(引擎,编译器,作用域)
查看>>
获取网页数据的例子
查看>>
struts2的配置文件
查看>>
JSP第5次测试---测试分析
查看>>
tomcat容器
查看>>
同时可以修改时间和日期的datetime_select and 有关时间的转换
查看>>
IOS Orientation, 想怎么转就怎么转~~~
查看>>
Finding Lines
查看>>
服务提供者及门面
查看>>
POJ-1611-The Suspects(并查集)
查看>>