1. 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种冲突
  1. 如果你还在为处理滑动冲突而发愁,那么你需要静下心来看看这边文章,如果你能彻底理解这篇文章中使用的技术,那么,一切滑动冲突的问题解决起来就轻而易举了:
    先扔一个最终实现的效果图

  1. 先分析下效果图中实现的功能点
  • 顶部下拉时背景图形成视差效果
  • 上拉时标题栏透明切换显示
  • 底部实现TabLayout+ViewPager+Fragment+RecyclerView
  • NestedScrollView+ViewPager的滑动冲突解决
  • NestedScrollView+RecyclerView滑动冲突的解决
  1. 复杂在哪里?整个布局中使用了SmartRefreshLayout,NestedScrollView,ViewPager,RecyclerView,每一个都有滑动事件,我们平时只是使用ScrollView+RecyclerView都会有滑动冲突,更何况这里有四个会引起冲突的控件一起使用!
  2.  
  3. 接下来,我们一步一步实现这个效果
    1、布局设计分析
  1. -FrameLayout(最外层)
  2. -ImageView(头部背景图)
  3. -SmartRefreshLayout(头部刷新控件)
  4. -JudgeNestedScrollView(自定义的NestedScrollView)
  5. ...省略中间巴拉巴拉布局
  6. -Tablayout
  7. -ViewPager
  1. 2、功能点实现说明
    2.1、下拉时视差效果的实现
    最外层为FrameLayout,ImageView高度设置超过屏幕顶部,借助SmartRefreshLayout控件在下拉和松开时头部背景图做平移处理,背景图片做了高斯模糊处理

  1. 布局代码:
  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. >
  7.  
  8. <ImageView
  9. android:id="@+id/iv_header"
  10. android:layout_width="match_parent"
  11. android:layout_height="670dp"
  12. android:layout_marginTop="-300dp"
  13. android:adjustViewBounds="true"
  14. android:contentDescription="@string/app_name"
  15. android:scaleType="centerCrop"
  16. android:src="@drawable/image_home"
  17. app:layout_collapseMode="parallax" />
  18.  
  19. <com.scwang.smartrefresh.layout.SmartRefreshLayout
  20. android:id="@+id/refreshLayout"
  21. android:layout_width="match_parent"
  22. android:layout_height="match_parent"
  23. app:srlEnablePreviewInEditMode="false">
  24. ...
  1. 下拉刷新时头部背景图片平移代码:
  1. refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener() {
  2. @Override
  3. public void onHeaderPulling(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {
  4. mOffset = offset / 2;
  5. ivHeader.setTranslationY(mOffset - mScrollY);
  6. }
  7.  
  8. @Override
  9. public void onHeaderReleasing(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {
  10. mOffset = offset / 2;
  11. ivHeader.setTranslationY(mOffset - mScrollY);
  12. }
  13. });
  1. 2.2TabLayout的顶部悬浮效果的实现
    此处使用的是最为简单笨拙的方法,两个TabLayout,一个固定为屏幕顶部ToolBar下面,并隐藏,另一个正常绘制在布局中;
    计算ToolBar的高度,根据NestedScrollView滑动的高度(这里的高度指的是跟随滑动的TabLayoutY坐标)恰好到ToolBar的高度位置时显示隐藏的ToolBar
  1. -FrameLayout
  2. -SmartRefreshLayout
  3. -Tablayout
  4. -Viewpager
  5. -SmartRefreshLayout
  6. -RelativeLayout
  7. -Toolbar
  8. -Tablayout
  9. -RelativeLayout
  10. -FrameLayout
  1.  
  1. toolbar.post(new Runnable() {
  2. @Override
  3. public void run() {
  4. toolBarPositionY = toolbar.getHeight();
  5. }
  6. });
  1.  
  1. scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
  2. @Override
  3. public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
  4. int[] location = new int[2];
  5. magicIndicator.getLocationOnScreen(location);
  6. int xPosition = location[0];
  7. int yPosition = location[1];
  8. if (yPosition < toolBarPositionY) {
  9. toolBarTablayout.setVisibility(View.VISIBLE);
  10. } else {
  11. toolBarTablayout.setVisibility(View.GONE);
  12. }
  13. }
  14. });
  1. 2.3ToolBar的渐变透明度以及按钮的切换
  1. <android.support.v7.widget.Toolbar
  2. android:id="@+id/toolbar"
  3. style="@style/AppTheme.Toolbar"
  4. android:layout_marginBottom="0dp"
  5. android:background="@android:color/transparent"
  6. app:layout_collapseMode="pin">
  7.  
  8. <LinearLayout
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:gravity="center_vertical"
  12. android:orientation="horizontal">
  13.  
  14. <ImageView
  15. android:id="@+id/iv_back"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:src="@drawable/back_white" />
  19.  
  20. <android.support.v7.widget.ButtonBarLayout
  21. android:id="@+id/buttonBarLayout"
  22. android:layout_width="0dp"
  23. android:layout_height="wrap_content"
  24. android:layout_gravity="center"
  25. android:layout_weight="1"
  26. android:gravity="center">
  27.  
  28. <de.hdodenhof.circleimageview.CircleImageView
  29. android:id="@+id/toolbar_avatar"
  30. style="@style/UserTitleAvatar"
  31. android:src="@drawable/timg" />
  32.  
  33. <TextView
  34. android:id="@+id/toolbar_username"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_gravity="center"
  38. android:maxLines="1"
  39. android:text="SiberiaDante"
  40. android:textColor="@color/mainBlack"
  41. android:textSize="@dimen/font_16" />
  42.  
  43. </android.support.v7.widget.ButtonBarLayout>
  44.  
  45. <ImageView
  46. android:id="@+id/iv_menu"
  47. android:layout_width="wrap_content"
  48. android:layout_height="match_parent"
  49. android:layout_gravity="end"
  50. android:src="@drawable/icon_menu_white" />
  51. </LinearLayout>
  52.  
  53. </android.support.v7.widget.Toolbar>
  1. ToolBar中间标题默认隐藏,使用的ButtonBarLayout包裹ImageViewTextView设置百分比透明,具体处理有两点:
  1. buttonBarLayout.setAlpha(0);
  2. toolbar.setBackgroundColor(0);
  1. * 下拉头部刷新时ToolBar渐变隐藏,同样利用SmartRefreshLayout处理
  1. refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener() {
  2. @Override
  3. public void onHeaderPulling(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {
  4. toolbar.setAlpha(1 - Math.min(percent, 1));
  5. }
  6.  
  7. @Override
  8. public void onHeaderReleasing(RefreshHeader header, float percent, int offset, int bottomHeight, int extendHeight) {
  9. toolbar.setAlpha(1 - Math.min(percent, 1));
  10. }
  11. });
  1. * 上下滑动时标题栏渐变显示和隐藏,并切换图标颜色(这里实际上是根据临界点直接更换图片,处理的比较简单)
  1. scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
  2. int lastScrollY = 0;
  3. int h = DensityUtil.dp2px(170);
  4. int color = ContextCompat.getColor(getApplicationContext(), R.color.mainWhite) & 0x00ffffff;
  5.  
  6. @Override
  7. public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
  8. int[] location = new int[2];
  9. magicIndicator.getLocationOnScreen(location);
  10. int xPosition = location[0];
  11. int yPosition = location[1];
  12.  
  13. if (lastScrollY < h) {
  14. scrollY = Math.min(h, scrollY);
  15. mScrollY = scrollY > h ? h : scrollY;
  16. buttonBarLayout.setAlpha(1f * mScrollY / h);
  17. toolbar.setBackgroundColor(((255 * mScrollY / h) << 24) | color);
  18. ivHeader.setTranslationY(mOffset - mScrollY);
  19. }
  20. if (scrollY == 0) {
  21. ivBack.setImageResource(R.drawable.back_white);
  22. ivMenu.setImageResource(R.drawable.icon_menu_white);
  23. } else {
  24. ivBack.setImageResource(R.drawable.back_black);
  25. ivMenu.setImageResource(R.drawable.icon_menu_black);
  26. }
  27.  
  28. lastScrollY = scrollY;
  29. }
  30. });
  1. 2.4NestedScrollView嵌套ViewPager导致ViewPager高度为0的处理
    很多人可能认为直接自定义ViewPager,测量子View的高度,让ViewPager去适应高度即可,其实不然,如果这样处理的话我们的Viewpager可能就是无限高度,我们在处理完NestedScrollView后,无限高度的ViewPagerRecyclerView又是一个问题,所以我这里的处理是计算ViewPager所需要的最大高度,即TabLayout在最顶部显示时到屏幕底部的最大高度为ViewPager高度
  1. toolbar.post(new Runnable() {
  2. @Override
  3. public void run() {
  4. toolBarPositionY = toolbar.getHeight();
  5. ViewGroup.LayoutParams params = viewPager.getLayoutParams();
  6. params.height = SDScreenUtil.getScreenHeight() - toolBarPositionY - tablayout.getHeight()+1;
  7. viewPager.setLayoutParams(params);
  8. }
  9. });
  1. 这里为什么要+1,后面会有解释
  2.  
  3. 2.5NestedScrollView嵌套RecyclerView滑动冲突
    NestedScrollView嵌套RecyclerView滑动冲突我们使用事件拦截处理,这里处理的是NestedScrollView的滑动,首先滑动的时候肯定是需要NestedScrollView的滑动事件,所以我们默认不拦截NestedScrollView的滑动事件,直到TabLayout顶部悬浮的时候,我们拦截NestedScrollView的滑动事件,交给RecyclerView来处理
    * 重写NestedScrollView
  1. public class JudgeNestedScrollView extends NestedScrollView {
  2. private boolean isNeedScroll = true;
  3. ...省略构造方法
  4. @Override
  5. public boolean onInterceptTouchEvent(MotionEvent ev) {
  6. switch (ev.getAction()) {
  7. case MotionEvent.ACTION_MOVE:
  8. return isNeedScroll;
  9. }
  10. return super.onInterceptTouchEvent(ev);
  11. }
  12.  
  13. /*
  14. 改方法用来处理NestedScrollView是否拦截滑动事件
  15. */
  16. public void setNeedScroll(boolean isNeedScroll) {
  17. this.isNeedScroll = isNeedScroll;
  18. }
  19. }
  1. 这里默认不拦截NestedScrollView滑动事件,只有当我们TabLayout滑动到顶部时才去拦截,也就是TabLayout显示隐藏的时候
  1. scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
  2. @Override
  3. public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
  4. int[] location = new int[2];
  5. magicIndicator.getLocationOnScreen(location);
  6. int xPosition = location[0];
  7. int yPosition = location[1];
  8. if (yPosition < toolBarPositionY) {
  9. tablayout.setVisibility(View.VISIBLE);
  10. scrollView.setNeedScroll(false);
  11. } else {
  12. tablayout.setVisibility(View.GONE);
  13. scrollView.setNeedScroll(true);
  14. }
  1. 至于前面测量ViewPager高度的时候,为什么会+1处理,这是因为,如果不+1时,刚好是TabLayout要出现的临界点,也就是ViewPager恰好的高度,但是这个时候又刚好是我们NestedScrollView拦截没有取消的临界点,所以,在上滑的时候,TabLayout刚好悬浮顶部时,RecyclerView没有获取事件,无法进行滑动,这就是给ViewPager+1处理的理由;
  1. 2.5NestedScrollView嵌套ViewPager滑动冲突2
    如果你足够细心的话,就会发现,当你的TabLayout上滑到一半的时候,再去左右滑动ViewPager是滑动不了的,因为这个时候NestedScrollView依然消费事件,所以我们还需要对NestedScrollView事件进行处理,判断如果是左右滑动的时候,我们不让NestedScrollView处理,而是交给子View处理,即ViewPager
  1. public class JudgeNestedScrollView extends NestedScrollView {
  2. private boolean isNeedScroll = true;
  3. private float xDistance, yDistance, xLast, yLast;
  4. ...省略构造方法
  5. @Override
  6. public boolean onInterceptTouchEvent(MotionEvent ev) {
  7. switch (ev.getAction()) {
  8. case MotionEvent.ACTION_DOWN:
  9. xDistance = yDistance = 0f;
  10. xLast = ev.getX();
  11. yLast = ev.getY();
  12. break;
  13. case MotionEvent.ACTION_MOVE:
  14. final float curX = ev.getX();
  15. final float curY = ev.getY();
  16. xDistance += Math.abs(curX - xLast);
  17. yDistance += Math.abs(curY - yLast);
  18. xLast = curX;
  19. yLast = curY;
  20. if (xDistance > yDistance) {
  21. return false;
  22. }
  23. return isNeedScroll;
  24.  
  25. }
  26. return super.onInterceptTouchEvent(ev);
  27. }
  28.  
  29. /*
  30. 改方法用来处理NestedScrollView是否拦截滑动事件
  31. */
  32. public void setNeedScroll(boolean isNeedScroll) {
  33. this.isNeedScroll = isNeedScroll;
  34. }
  35. }
  1. 至此,完美的解决了所有的问题,当时有些细节这里并没有话费太多的时间去处理,如有任何问题,欢迎各位大佬进行指正
  2.  
  3. 源码:https://github.com/SiberiaDante/MultiScrollDemo
  4.  

使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突的更多相关文章

  1. Android 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突

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

  2. 纯CSS3写的10个不同的酷炫图片遮罩层效果【转】

    这个是纯CSS3实现的的10个不同的酷炫图片遮罩层效果,可以欣赏一下 在线预览 下载地址 实例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...

  3. 纯CSS3写的10个不同的酷炫图片遮罩层效果

    这个是纯CSS3实现的的10个不同的酷炫图片遮罩层效果,可以欣赏一下 在线预览 下载地址 实例代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...

  4. 使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉载入很多其它

    一.概述 本篇博客介绍的是怎样使用SwipeRefreshLayout和RecyclerView实现高仿简书Android端的下拉刷新和上拉载入很多其它的效果. 依据效果图能够发现,本案例实现了例如以 ...

  5. 三分钟学会用 js + css3 打造酷炫3D相册

    之前发过该文,后来不知怎么回事不见了,现在重新发一下. 中秋主题的3D旋转相册 如图,这是通过Javascript和css3来实现的.整个案例只有不到80行代码,我希望通过这个案例,让正处于迷茫期的j ...

  6. MVC中使用SignalR打造酷炫实用的即时通讯功能附源码

    前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯.由于当时走的太急,忘记把代码拿出来.想想这已经是大半年前的事情了,时间过 ...

  7. Canvas+Video打造酷炫播放体验

    一.简介 直到现在,仍然不存在一项旨在网页上显示视频的标准. 今天,大多数视频是通过插件(比如 Flash)来显示的.然而,并非所有浏览器都拥有同样的插件. HTML5 规定了一种通过 video 元 ...

  8. MVC中使用SignalR打造酷炫实用的即时通讯功能(轉載)

    資料來源:http://www.fangsi.net/1144.html 前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯 ...

  9. Flutter酷炫的路由动画效果

    现在Flutter的路由效果已经非常不错了,能满足大部分App的需求,但是谁不希望自己的App更酷更炫那,下面介绍几个酷炫的路由动画. 其实路由动画的原理很简单,就是重写并继承PageRouterBu ...

随机推荐

  1. Use MusicBrainz in iOS(三)查询专辑的完整信息

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u010962810/article/details/24066737 本文讨论下通过专辑名获取专辑的 ...

  2. BZOJ3105:[CQOI2013]新Nim游戏(线性基,贪心)

    Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴 ...

  3. 关于Maven配置的一些标签含义(后续逐渐补充)

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  4. JMX RMI 攻击利用

    攻击者通过构造恶意的MBean,调用 getMBeansFromURL 从远程服务器获取 MBean,通过MLet标签提供恶意的MBean对象下载. 前提条件: 允许远程访问,没有开启认证 (com. ...

  5. jenkins发版脚本更新

    jenkins 项目名中明确了 是jar tar.gz war包研发需要提供  项目名-地点-环境(研发.测试.生产)-应用项目名称(admin)-包格式(jar\war\gz) deployment ...

  6. Https 安全传输的原理

    序言 今天来聊一聊https 安全传输的原理. 在开始之前,我们来虚构两个人物, 一个是位于中国的张大胖(怎么又是你?!), 还有一个是位于米国的Bill (怎么还是你?!). 这俩哥们隔着千山万水, ...

  7. OpenCV——图像的矩(计算矩、轮廓面积、轮廓或曲线长度)

    图像矩描述了图像的全局特征 一阶矩与形状有关 二阶距显示曲线围绕直线平均值的扩展程度 三阶矩是关于平均值的对称性测量 由二阶和三阶矩可以导出7个不变矩,不变矩是图像的统计特性,满足平移.伸缩.旋转的不 ...

  8. css样式匹配苹果个型号手机

    /*适配苹果X*/ @media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-devic ...

  9. Android给拼接好的Bitmap加上个性化边框

    在上一节中将到将若干张图片拼接成为一张图片.但是这种简单的操作往往不能满足实际的需求,有时我们会需要给图片添加上个性化的边框,来更好的展示图片. 下面就讲一下在图片拼接后如何给bitmap添加边框. ...

  10. mysql,int(5)、int(10)啥区别联系

    实际没啥区别..这个5和10并不是最大5位,最大10位的意思. 好比选择了int(5),并且当你选择了0填充的话.你的数据假设存了123,那么你的显示会是00123,(有些操作mysql的工具看不出来 ...