之前的一遍学习笔记主要就Android滑动冲突中,在不同方向的滑动所造成冲突进行了了解,这种冲突很容易理解,当然也很容易解决。今天,就同方向的滑动所造成的冲突进行一下了解,这里就先以垂直方向的滑动冲突为背景,这也是日常开发中最常见的一种情况。

这里先看一张效果图

查看图文详情

由于GIF 图片大小的限制,截图效果不是很好

上图是在购物软件上常见的上拉查看图文详情,关于这中动画效果的实现,其实实现整体的效果,办法是有很多的,网上有很多相关的例子,但是对某些细节的处理不是很清晰,比如,下拉之后显示的部分(例如底部的图文详情)又是一个类ScrollView的控件(比如WebView)的话,又会产生新的问题。这里就以下拉查看图文详情为背景做一下同方向滑动冲突的分析。

整体思路

这里看下图

多个ScrollView嵌套示意图

首先,关于这张图做一些设定:

  • 黑色的框代表手机屏幕
  • 绿色的框代表一个外层的ScrollView,这个应该是实际应用中可见view的总长度
  • 两个红色的框代表嵌套在里面的两个类ScrollView控件,这里我们就暂时简称为 SUp,SDown

好了,接下来就分析一下实现整个流程的过程。

这里必须明确的一点,无论何时,SUp和SDown可见的部分始终是手机屏幕的高度。知道了这一点,我们就可以按以下步骤展开

首先,我们确保外部的ScrollView不拦截滑动事件,这样SUp必然获得此次事件,然后根据其Action_Move事件,当其为向上滑动自身滑动距离+屏幕高度=其自身高度 时,即可认为SUp滑动到了底部,此时外部ScrollView可拦截滑动事件,从而保证整个视图能够继续向上滑动,这个时候底部SDown就显示出来了。

同理,这时候不允许外部ScrollView拦截滑动事件,由SDown处理,根据其Action_move事件,当其为向下滑动,且自身可滑动距离为0时,就说明SDown已经滑动到了顶部,这时外部ScrollView又可以获得拦截滑动事件的权利,从而保证整个视图能够向上继续滑动,此时SUp再次显示,又开始新一轮循环拦截。

这样整体的一个流程就可以实现动图中的效果。好了,说完原理,还是看代码。

代码实现

SUp实现

public class UpScrollView extends ScrollView {/*** 屏幕高度*/private int mScreenHeight;/*** 上一次的坐标*/private float mLastY;/*** 当前View滑动距离*/private int mScrollY;/*** 当前View内子控件高度*/private int mChildH;public UpScrollView(Context context) {super(context);init(context);}public UpScrollView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public UpScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics dm = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(dm);mScreenHeight = dm.heightPixels;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {//默认设定顶层View不拦截getParent().getParent().requestDisallowInterceptTouchEvent(true);switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:mLastY = (int) ev.getY();break;case MotionEvent.ACTION_MOVE:float y = ev.getY();float deltaY = y - mLastY;mChildH = this.getChildAt(0).getMeasuredHeight();int translateY = mChildH - mScrollY;//向上滑动时,如果translateY等于屏幕高度时,即表明滑动到底部,可由顶层View控制滑动if (deltaY < 0 && translateY == mScreenHeight) {getParent().getParent().requestDisallowInterceptTouchEvent(false);}break;default:break;}return super.onTouchEvent(ev);}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {super.onScrollChanged(l, t, oldl, oldt);mScrollY = t;}
}

这里在ACTION_MOVE里做了减法,其实道理是一样的。

onScrollChanged 是在View类中实现,查看其API可以看到其第二个参数t解释

  • @param t Current vertical scroll origin.

即为当前View此次滑动的距离

SDown实现

public class MyWebView extends WebView {public float oldY;private int t;public MyWebView(Context context) {super(context);init();}public MyWebView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {WebSettings settings = getSettings();settings.setJavaScriptEnabled(true);setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {view.loadUrl(url);return true;}});}@Overridepublic boolean onTouchEvent(MotionEvent ev) {getParent().getParent().requestDisallowInterceptTouchEvent(true);switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:oldY = ev.getY();break;case MotionEvent.ACTION_MOVE:float Y = ev.getY();float Ys = Y - oldY;if (Ys > 0 && t == 0) {getParent().getParent().requestDisallowInterceptTouchEvent(false);}break;}return super.onTouchEvent(ev);}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {this.t = t;super.onScrollChanged(l, t, oldl, oldt);}}

看以看到,这里底部的View并没有继承ScrollView,而是选择继承了WebView,这里只是为了方便,当然继承ScrollView也是没有问题。这里只是需要按实际情况考虑,因为底部图文详情的内容就是一个WebView加载数据。

这个类的实现,按照之前说的原理应该很好理解。

外部ScrollView###

public class CustomerScrollViews extends ScrollView {/*** 屏幕高度*/private int mScreenHeight;private UpScrollView upScrollView;private MyWebView myWebView;private boolean init = false;private float fator = 0.2f;private int factorHeight;private boolean upShow = true;public CustomerScrollViews(Context context) {super(context);init(context);}public CustomerScrollViews(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public CustomerScrollViews(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics dm = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(dm);mScreenHeight = dm.heightPixels;factorHeight = (int) (mScreenHeight * fator);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (!init) {LinearLayout parentView = (LinearLayout) getChildAt(0);//获得内部的两个子viewupScrollView = (UpScrollView) parentView.getChildAt(0);myWebView = (MyWebView) parentView.getChildAt(2);
//            //并设定其高度为屏幕高度upScrollView.getLayoutParams().height = mScreenHeight;myWebView.getLayoutParams().height = mScreenHeight;init = true;}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (changed) {scrollTo(0, 0);}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_UP:int scrollY = getScrollY();if (upShow) {if (scrollY <= factorHeight) {smoothScrollTo(0, 0);} else {smoothScrollTo(0, mScreenHeight);upShow = false;}} else {int scrollpadding = mScreenHeight - scrollY;if (scrollpadding >= factorHeight) {this.smoothScrollTo(0, 0);upShow = true;} else {this.smoothScrollTo(0, mScreenHeight);}}return true;}return super.onTouchEvent(ev);}}

这个类的实现,就很灵活了,在onMeasure方法中初始化完内部的View之后,在OnTouch方法中就可以根据实际需求完成不同的逻辑实现,这里只是为了仿照查看图文详情的效果,对整个视图通过ScrollView的smoothScrollTo方法进行位移变化,这个逻辑很简单。

这里重点说一下一个地方:

upScrollView = (UpScrollView) parentView.getChildAt(0);
myWebView = (MyWebView) parentView.getChildAt(2);

你可能会奇怪中间的child(1)去了哪里?这里还要从MainActivity的布局文件说起

dual_scrollview_activity_layout1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.dreamwork.activity.superscrollview.CustomerScrollViewsandroid:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.example.dreamwork.activity.superscrollview.UpScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:scrollbars="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:scaleType="fitXY"android:src="@drawable/taobao" /><ImageViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:scaleType="fitXY"android:src="@drawable/taobao" /><TextViewandroid:textSize="20sp"android:padding="10dp"android:gravity="center"android:layout_marginTop="20dp"android:layout_marginBottom="60dp"android:text="查看图文详情"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout></com.example.dreamwork.activity.superscrollview.UpScrollView><include layout="@layout/selector_tab_items" /><com.example.dreamwork.activity.superscrollview.MyWebViewandroid:id="@+id/web"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout></com.example.dreamwork.activity.superscrollview.CustomerScrollViews></LinearLayout>

整个布局文件可以看出,我们在CustomerScrollViews这个最外层的自定义ScrollView内部又放置了两个自定义的ScrollView(就如我们看到的原理图那样),只不过在这两个ScrollView类控件的中间通过layout又放置一个LinearLayout,里面的内容就是在动图中看到的那个中间的写着qq,baidu字样的用于切换WebView内容的一个View。这里就不贴代码了。

这样,你就可以理解之前的child(1)为什么被跳过了吧。

使用###

public class DualScrollViewActivity1 extends Activity implements View.OnClickListener {private MyWebView webView;private TextView sinaTv, qqTv, baiduTv;private View line1, line2, line3;private final String BAIDU = "http://www.baidu.com";private final String QQ = "http://www.qq.com";private final String SINA = "http://sina.cn";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);InitView();sinaTv.performClick();}private void InitView() {setContentView(R.layout.dual_scrollview_activity_layout1);webView = V.f(this, R.id.web);sinaTv = V.f(this, R.id.one);sinaTv.setOnClickListener(this);qqTv = V.f(this, R.id.two);qqTv.setOnClickListener(this);baiduTv = V.f(this, R.id.three);baiduTv.setOnClickListener(this);line1 = V.f(this, R.id.line1);line2 = V.f(this, R.id.line2);line3 = V.f(this, R.id.line3);}@Overridepublic void onClick(View v) {reset();String url = "";switch (v.getId()) {case R.id.one:line1.setVisibility(View.VISIBLE);url = SINA;break;case R.id.two:line2.setVisibility(View.VISIBLE);url = QQ;break;case R.id.three:line3.setVisibility(View.VISIBLE);url = BAIDU;break;default:break;}webView.loadUrl(url);}private void reset(){line1.setVisibility(View.GONE);line2.setVisibility(View.GONE);line3.setVisibility(View.GONE);}
}

关于底部View内容更新,WebView 通过加载不同URL实现不同视图效果,只是作为Demo测试,实际中应考虑通过fragment切换实现。

这里对滑动冲突的解决方法,是由内而外的展开,默认使顶层View失去拦截能力,在由底部View的滑动距离,做出不同逻辑判断控制了顶层的拦截与否;这也是比较容易理解和实现的思路。当然,对于此类滑动冲突,有很多不同思路,这里只是列举其一。

实际开发开发中,这种带有同方向滑动特性的控件嵌套时,产生的问题不只是滑动冲突,有时候会有内容显示不全或不显示的情况。

最常见如ScrollView嵌套ListView,这种情况只需自定义ListView使其高度计算为一个很大的值,某种意义上让其失去了滑动的特性,但是这样也让ListView貌似失去了视图回收机制,这种时候如果加载很多很多很多图片,效果就会很不理想。对于这种情况,通过对ListView添加headView及footView也是一种解决的办法,但也得实际UI情况允许。

ScrollView嵌套RecyclerView时稍微麻烦一点,需要自定义ScrollView,还需要自定义实现LinearLayoutManager。

由此可见,在Android中这种带有类似特性的控件嵌套后,问题还是很多的。对此,Google出了一个叫做NestedScrolling的东西,接下来准备研究一下,看看其在解决滑动冲突方面的优势。

链接: https://www.jianshu.com/p/3f5cb15c70f5

Android滑动冲突解决方法(二)相关推荐

  1. Android滑动冲突解决方法

    Android滑动冲突解决方法 滑动冲突 首先讲解一下什么是滑动冲突.当你需要在一个ScrollView中嵌套使用ListView或者RecyclerView的时候你会发现只有ScrollView能够 ...

  2. Android滑动冲突解决方式(下拉刷新上拉加载更多,适配RecyclerView/ListView/ScrollView)

    一.Android事件的分发机制 这里需要了解下Andorid事件的分发机制.事件分发一般是针对一组事件,即ACTION_DOWN > ACTION_UP 或 ACTION_DOWN > ...

  3. 关于ViewPager嵌套ViewPager左右滑动冲突解决方法

    情况如下,TabLayout+ViewPager+Fragment,其中一个Fragment里面又嵌套了一层TabLayout+ViewPager+Fragment,运行的时候出现了两个情况. 1.子 ...

  4. ScrollView嵌套ViewPager,ViewPage动态设置高度,嵌套事件冲突——滑动冲突解决方法

    1.创建自定义ViewPager public class ViewPagerForScrollView extends ViewPager {int myh=0 ;boolean ifme =fal ...

  5. Android滑动冲突的完美解决方案

    一.Android滑动冲突的完美解决方案 在Android开发中滑动冲突可以说是比较常见的一类问题,也是比较让人头疼的一类问题,两个原本完美的控件,组合在一起之后,忽然发现整个世界都不好了.滑动冲突主 ...

  6. 关于Android滑动冲突的解决方法(二)

    之前的一遍学习笔记主要就Android滑动冲突中,在不同方向的滑动所造成冲突进行了了解,这种冲突很容易理解,当然也很容易解决.今天,就同方向的滑动所造成的冲突进行一下了解,这里就先以垂直方向的滑动冲突 ...

  7. Android进阶必备:滑动冲突解决与事件分发机制(附视频讲解)这篇看完还不懂请寄刀片

    1.前言 Android学习一段时间,需求做多了必然会遇到滑动冲突问题,比如在一个ScrollView中要嵌套一个地图View,这时候触摸移动地图或者放大缩小地图就会变得不太准确甚至没有反应,这就是遇 ...

  8. Android Studio androidx 包冲突解决方法

    Android Studio androidx 包冲突解决方法 参考文章: (1)Android Studio androidx 包冲突解决方法 (2)https://www.cnblogs.com/ ...

  9. android studio 导入模块SDK 以及该模块中继承application的类与原工程继承application的类存在冲突 解决方法

    android studio 导入模块SDK 以及该模块中继承application的类与原工程继承application的类存在冲突 解决方法 导入某修改后的海康视频SDK(老师给的),并修改相关文 ...

最新文章

  1. 递归的妙用—遍历子控件
  2. es6学习1: 模拟react Comopnent类的实现
  3. 关于.NET参数传递方式的思考
  4. 普渡大学计算机硕士申请条件,普渡大学CS硕士申请需要哪些条件?未来的就业前景全面解析...
  5. 再谈软件测试-工作感悟
  6. 假设有搅乱顺序的一群儿童成一个队列_数据结构与算法系列之栈amp;队列(GO)...
  7. python正向切片_使Python切片正常(正/正向+无无+无负索引+在边界内)
  8. 排序算法的总结——Java实现
  9. SpringBoot:javalist和set区别
  10. 微星刀锋 无法进入bios_微星MPG X570 GAMING EDGE WIFI刀锋板主板BIOS设置u盘启动教程...
  11. 无限小说网 内部服务器错误,iOS网络模块优化(失败重发、缓存请求有网发送)...
  12. Golang学习资源整理
  13. java数组的实例化
  14. MyBioSource STMN1 elisa 试剂盒解决方案
  15. 第十届全国大学生GIS应用技能大赛下午(试题及参考答案)
  16. 关闭ADOX.Catalog创建Access的链接,避免ldb锁定
  17. windows远程命令执行
  18. 创建一个docker容器
  19. c语言输入函数 scanf 详解 - 解决常见输入问题
  20. 一文弄懂halcon例程:rim.hdev

热门文章

  1. 网站服务器如何导入数据库,网站服务器如何导入数据库
  2. Lasso回归和岭回归
  3. Python-之基础篇3
  4. 上市公司退市的条件是什么
  5. 激活函数之ReLU函数
  6. 腾讯云CDN加速产品介绍第五章-CDN计费方案
  7. 使用WSAIoctl获取AcceptEx函数指针
  8. Android S CTS STS 常见的 fail 项
  9. cesium系列 - 错误收集
  10. 一线互联网移动架构师360°全方面性能调优,年薪超过80万!