Android滑动冲突处理,Android实践之ScrollView中滑动

2020-03-02 10:48 来源:未知
前言:####

WebKit中的JavaScriptCore引擎处理的JS滑动与原生控件滑动冲突尚未完善,这篇文章讨论下如何彻底解决H5端的滑动控件与原生控件滑动冲突

ScrollView,ViewPager,ListView等可滚动的View嵌套WebView时的滑动冲突处理

如何优雅的解决这些复杂嵌套的滚动事件处理:

android的webView给我提供了这个回调方法onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)。

重写这个回调方法,它会告诉我们一些滚动事件。我们来看它返回的4个参数:

  • scrollX:当前View的横向滚动距离

  • scrollY:当前View的纵向滚动距离

  • clampedX:当前View横向方向是否还可滚动

  • clampedY:当前View纵向方向是否还可滑动

有了上面这些条件在配合requestDisallowInterceptTouchEvent()就可以顺利的实现滑动了。requestDisallowInterceptTouchEvent()简单的来说就是告诉父View当前事情是我来处理还是父View来处理。传true时告诉父View滑动事件我自己处理;传false时告诉父View滑动时间你来处理;下面帖上完整代码处理ViewPager嵌套WebView:

public class ScrollWebView extends WebView {public ScrollWebView(Context context) { super;}public ScrollWebView(Context context, AttributeSet attrs) { super(context, attrs);}public ScrollWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);}private boolean isScrollX = false;@Overridepublic boolean onTouchEvent(MotionEvent event) { if (MotionEventCompat.getPointerCount == 1) { switch (event.getAction { case MotionEvent.ACTION_DOWN: isScrollX = false; //事件由webview处理 getParent().getParent() .requestDisallowInterceptTouchEvent; break; case MotionEvent.ACTION_MOVE: //嵌套Viewpager时 getParent().getParent() .requestDisallowInterceptTouchEvent(!isScrollX); break; default: getParent().getParent() .requestDisallowInterceptTouchEvent; } } else { //使webview可以双指缩放(前提是webview必须开启缩放功能,并且加载的网页也支持缩放) getParent().getParent(). requestDisallowInterceptTouchEvent; } return super.onTouchEvent;}//当webview滚动到边界时执行@Overrideprotected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); isScrollX = clampedX;}}

在我们平时项目开发的过程中,是不是会遇到滑动View之间的相互嵌套,比如外部的Scrollview或SwipeRefreshLayout嵌套内部的Viewpager或横向Recyclerview,如外部Viewpager嵌套内部Viewpager。这样往往就会造成滑动的冲突导致不流畅甚至根本滑不动。事件分发机制见我的另一篇Android事件分发机制,用事实说话。

转载注明出处:

1:点击WebView中的轮播图区域左右滑动与上下滑动#####

图片 1

滑动冲突产生的两个根本原因:
  • 外部滑动方向与内部方向不一致
  • 外部滑动方向与内部方向一致
    扎心了,一不一致都可能造成滑动冲突。第一种如ScrollView 嵌套ViewPager,第二种如ViewPager嵌套ViewPager。

在Android开发中,如果是一些简单的布局,都很容易搞定,但是一旦涉及到复杂的页面,特别是为了兼容小屏手机而使用了ScrollView以后,就会出现很多点击事件的冲突,最经典的就是ScrollView中嵌套了ListView。我想大部分刚开始接触Android的同学们都踩到过这个坑,这一篇文章就从最近做的一个项目讲起,然后在过程中提供一些解决冲突的思路。

2:WebView非轮播区域左右翻页ViewPager#####

图片 2

首先介绍下WebView内容的几种滑动方式

解决方案:

项目有一个页面,涉及到了ViewPager,MapView,ListView,也就是说在一个页面中,会有这三个View,很明显,屏幕无法完全显示,需要ScrollView来做一下支援,就引入了ScrollView作为外层的容器。但是由于这个页面的数据展示需要做到用户手动下拉刷新,于是又引入了官方的SwipeRefreshLayout。

1:WebView在固定宽高时与自适应屏幕时的滑动#####

固定宽高时,WebView加载的html页面内容宽度或高度大于webView的宽度或高度时,滑动的都是改变mScrollX, mScrollY的值,这种滑动能在onScrollChanged(int l, int t, int oldl, int oldt)监听到getScrollX, getScrollY值变化.WebView自适应屏幕宽度,高度自适应嵌套在ScrollView中滑动时,WebView高度即为加载页面的实际高度,此时页面中内容的滑动实际就是ScrollView在控制,滑动改变的是ScrollView中第一个子控件的scrollY值

外部拦截法:

从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截事件,不需要则不拦截返回false。伪代码如下:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                if(父View拦截条件){
                    return true;
                }
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }

于是这个页面的布局就成了这样子。如下图。

2:H5控件通过JS控制的滑动#####

这种滑动在WebView的某个固定区域内通过JS控制滑动,无论是水平滑动还是垂直滑动都不会像android原生滑动控件有requestDisallowInterceptTouchEvent请求事件放行,而且又不影响WebView的scrollX,scrollY值,所以即使是(能自动处理水平与垂直滑动区别的)ViewPager也不能判断它是否能够优先滑动第1个问题只要能改变scrollX或scrollY值外层控件就能识别是否能够优先滑动,这里不再讲述如何处理滑动优先上层控件如何处理水平与垂直滑动和多层嵌套-此处链接

直接举例子说明, H5中的轮播控件被嵌套到NestedScrollView中,外层再嵌套SwipeRefreshLayout 下拉刷新,外层再嵌套ViewPager,既然外层控件无法判断JS是否优先滑动,那思路只能由JS绝对优先滑动,在产生水平或垂直滑动或无法滑动时再告诉上层控件是否要放行事件

图片 3举例子前先讲下网上一些大神们如何处理这种冲突网上也有一大堆的方法,有用js交互处理返回滑动区域,在该区域内都不拦截事件, 也有修改WebView的onTouchEvent方法,在ACTION_DOWN时请求.requestDisallowInterceptTouchEvent,在ACTION_MOVE时再根据x,y轴的差值比较来判断是否为水平还是垂直滑动,这里分析下不完善之处a:如果WebView中加载的H5页面或WebView外面嵌套的控件即有水平又有垂直滑动时,这方法就行不通了b:android判断水平与垂直滑动的标准也不是这样子定义的,在google提供的所有的滑动控件中都可以看到 yDiff>mTouchSlop 或 xDiff > mTouchSlop的代码, 不仅是判断差值还要判断差值大于最小滑动单位才算是水平或垂直滑动

直接上代码喽,先是JS部分(这里会用到js交互,与js的事件监听,作者对这块水平烂,如有写错,多多指点)

 // 轮播滚动 var mySwiper = $('.slidepics').swiper({ loop : true, pagination: '.dotted p', paginationClickable: true, spaceBetween: 30, autoplay:3000, autoplayDisableOnInteraction:false, onSliderMove:function { //如果光用此方法来申请app原生控件不拦截事件,在快速滑动的时候可能抢不到事件 //还要配合下面的touch事件来获取 isBeingDrag = true; window.Android.requestEvent; } }); // 轮播滚动事件监听 $('.slidepics').on('touchstart touchmove touchend touchcancel' , function { var touch = event.originalEvent.targetTouches[0]; switch (event.type) { case "touchstart": mLastClientX = touch.clientX; mLastClientY = touch.clientY; isBeingDrag = false; window.Android.requestEvent; break; case "touchmove": if (!isBeingDrag) { var xDiff = Math.abs(touch.clientX - mLastClientX); var yDiff = Math.abs(touch.clientY - mLastClientY); //console.log(xDiff   " "   yDiff); if (xDiff >= touchSlop) { isBeingDrag = true; } else if(yDiff > touchSlop) { //产生app纵向滑动,父控件不强制请求放行事件,这段逻辑主要是不影响外层垂直滑动控件的滑动 //js端控制滑动与app端的标准不一样,所以结合上面的onSliderMove:function方法来判断 //如果H5端控件已经产生滑动时则必须请求父控件放行事件 //如果有些开源控件没有类似onSliderMove方法时,只需提供控件是否产生滑动就行,原理都是一样 isBeingDrag = true; window.Android.requestEvent; } } break; case "touchend": window.Android.requestEvent; break; } }); //app端的滑动值 var touchSlop = 0; //初次按下时的x, y轴值 var mLastClientX, mLastClientY; //是否处于滑动中 var isBeingDrag = false; //js附值,在web加载完成时将android的滑动单位值传给js function initTouchSlop(appTouchSlop) { touchSlop = appTouchSlop; }

JS端事件代码逻辑跟app端差不多,android代码

 /** * js交互类 */ private class JsCallback { @JavascriptInterface public void requestEvent(boolean request) { Log.i("you", "requestDisallowInterceptTouchEvent "   request " " Thread.currentThread().getName; mWebView.requestDisallowInterceptTouchEvent; } } mWebView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); int touchSlop = ViewConfiguration.get(getContext.getScaledTouchSlop(); StringBuilder jsSb = new StringBuilder("javascript:initTouchSlop.append(touchSlop).append; mWebView.loadUrl(jsSb.toString; 

需要注意的是:js交互回调不是在主线程中执行.如果您的界面中没有涉及即有水平又有垂直滑动的复杂嵌套,js代码也可以简单化,只需要监听touchstart, touchend事件即可

小细节:requestDisallowInterceptTouchEvent()方法在SwipeRefreshLayout中可能会不生效,此方法在SwipeRefreshLayout中被重写,在SDK22以下版本的V4包中,此方法什么都不执行,google的版本问题甚是头疼,要解决这问题就是下拉刷新嵌套的控件去实现NestedScroll,设置isNestedScrollingEnabled值为true,google也有写好的NestedScrollView

 @Override public void requestDisallowInterceptTouchEvent(boolean b) { if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView) || (mTarget != null && !ViewCompat.isNestedScrollingEnabled) { // Nope. // 可以看出SwipeRefreshLayout的第一个child没有实现NestedScroll时不能申请到上层控件不拦截事件, //这也是为什么SwipeRefreshLayout嵌套很多控件下拉刷新会有冲突的原因,子控件无法传递requestDisallowInterceptTouchEvent } else { super.requestDisallowInterceptTouchEvent; } }

在使用嵌套下拉刷新SwipeRefreshLayout时注意V4包的版本,SDK22版本以下是不支持的,H5端的冲突也就没法处理如果您有更好的方法解决冲突,请多多指教

下面我们来看一个真实的案例:

SwipeRefreshLayout嵌套ViewPager,需要流畅横向滑动Viewpager,这种操作下通常会顺带牵连到SwipeRefreshLayout致其拉动,如示例:

giphy.gif

用外部拦截法自定义一个MySwipeRefreshLayout,在开始滑动的条件下,判断手势的横纵方向的距离大小,判断用户到底是要横向滑动还是纵向滑动。纵向滑动意为拉动刷新,就拦截事件,横向滑动就意为滑动viewpager,就不拦截事件。解决效果:

giphy.gif

MySwipeRefreshLayout代码如下:

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;

public class MySwipeRefreshLayout extends SwipeRefreshLayout{
    private float startX;
    private float startY;
    private float mTouchSlop;

    public MySwipeRefreshLayout(Context context) {
        super(context);

        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                startX = ev.getX();
                startY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float distanceX = Math.abs(ev.getX() - startX);
                float distanceY = Math.abs(ev.getY() - startY);
                if(distanceX > mTouchSlop && distanceX > distanceY){  //判断为横向滑动
                    return false;
                }

                break;
        }

        return super.onInterceptTouchEvent(ev);
    }
}

getScaledTouchSlop()表示能触发滚动的最小距离,如果小于这个距离就不触发移动控件。

    /**
     * @return Distance in pixels a touch can wander before we think the user is scrolling
     */
    public int getScaledTouchSlop() {
        return mTouchSlop;
    }

图片 4布局图.png

最后附上源码
内部拦截法:(getParent().requestDisallowInterceptTouchEvent(true),请求父容器不拦截事件)

从子View入手,重写子元素的dispatchTouchEvent方法,父View先不要拦截任何事件,所有的事件传递给子View,如果子View需要此事件就消费掉,不需要此事件的话就通过requestDisallowInterceptTouchEvent(true)方法交给父View处理。伪代码如下:

   @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if(事件交给父View条件){
                    getParent().requestDisallowInterceptTouchEvent(false);
                }else{
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

父View需要重写:默认不拦截子View事件

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(ev.getAction() == MotionEvent.ACTION_DOWN){
            return false;
        }else{
            return true;
        }
    }

案例:Viewpager嵌套多个ViewPager:需求是默认滑动内部Viewpager,当内部Viewpager滑动到首页或者末页时,就滑动外部Viewpager切换页面。解决效果:

giphy.gif

自定义子Viewpager:

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class InerViewPager extends ViewPager{
    private int itemCount;

    public InerViewPager(Context context) {
        super(context);
    }

    public InerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                itemCount = getAdapter().getCount();
                if(getCurrentItem() == 0 || getCurrentItem() == itemCount - 1){
                    getParent().requestDisallowInterceptTouchEvent(false);
                }else{
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

}

这里举的是基本栗子,那如果产品经理的想象力丰富,难度系数5.0以上呢?要是开发中遇到较复杂的滑动冲突,我只能说来一个杀一个,来两个杀一双!

加入了ScrollView和SwipeRefreshLayout之后引入了新的问题,就是各个控件之间的事件冲突,嵌套在ScrollView中的ViewPager、MapView、ListView都需要能够正确的处理点击事件,特别是ListView,需求要求它在ScrollView中可以滑动,两种滑动混淆在一起,不是特别好处理。

问题提出来了,下面直接看解决思路。

在ViewGroup中有个方法叫requestDisallowInterceptTouchEvent(boolean disallowIntercept),这个方法可以用来控制该ViewGroup是否截断点击事件。我们解决滑动冲突的时候,其实就是在某个时机去调用这个方法,让父布局不截断点击事件,将点击事件传递到子View,让相关的子View去处理。

下面就是关于在项目中处理各种点击事件冲突的一些例子和思考。处理的方法只是提供一种思路,可能并不是最优的方法,肯定存在其他思路的解决方案。

以下处理滑动冲突的方案都是在子View的OnTouchListener里面进行处理,并没有去复写控件的点击事件处理过程,在使用中还是比较方便的。

MapView地图页面滑动冲突

MapView与ScrollView的冲突主要在于,当用户点击到MapView地图并且滑动的时候,希望由地图Map去处理点击事件,并包括后续的滑动事件、双手指缩放地图等等。

在ScrollView中,是会默认截断点击事件的,导致用户点击到地图后,地图基本是没有反应,更别谈双手指缩放地图了。

用户手指点击到地图,并且滑动的时候,很难确定用户是要ScrollView上下滑动还是操控地图内容滑动,所以我简单的认为,只要用户手指点击到地图,就是要对地图进行操作;当用户手指抬起,就认为用户不需要操作地图了。

解决思路也很简单,就是在用户点击到地图或者滑动地图时候,让ScrollView不截断点击事件,并传递给子View处理,也就是地图去处理点击事件;当用户手指抬起的时候,将ScrollView的状态恢复至之前的状态,也就是ScrollView可以截断点击事件。

我使用的是百度地图,直接上代码,更容易理解。

mMapView.getChildAt.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP){ //允许ScrollView截断点击事件,ScrollView可滑动 mScrollView.requestDisallowInterceptTouchEvent; }else{ //不允许ScrollView截断点击事件,点击事件由子View处理 mScrollView.requestDisallowInterceptTouchEvent; } return false; } });
ViewPager滑动冲突解决

在这个项目中,ViewPager在页面最顶层,如果只是ScrollView里面嵌套了ViewPager,因为这两个控件是不同方向的滑动事件,所以基本不会出现冲突。

但是由于引入了SwipeRefreshLayout,我发现在滑动ViewPager的时候,很容易就触发了SwipeRefreshLayout的下来刷新,进而有可能阻断了ViewPager的左右滑动效果,体验很不好。而且在滑动ViewPager的过程中,用户滑动肯定不是一直水平的,会有一定程度向上向下的滑动。

ViewPager处理冲突和地图处理冲突有些不同,因为当用户点击到ViewPager,在滑动过程中,基本就可以猜测到用户是想左右滑动ViewPager还是上下滑动ScrollView,这就不能像地图一样,在点击到ViewPager就禁止ScrollView截断点击事件(或者SwipeRefreshLayout下拉刷新功能),需要在滑动过程中做出判断。

解决思路就是,设定一个阈值,一旦用户在X轴也就是横向滑动距离超过这个阈值,我就认为用户是要左右滑动ViewPager,就禁止ScrollView截断点击事件同时设置SwipeRefreshLayout不能下拉刷新。当用户抬起手指,就认为用户对ViewPager的操作已经完毕,将ScrollView和SwipeRefreshLayout状态恢复。

mViewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if(action == MotionEvent.ACTION_DOWN) { // 记录点击到ViewPager时候,手指的X坐标 mLastX = event.getX(); } if(action == MotionEvent.ACTION_MOVE) { // 超过阈值 if(Math.abs(event.getX() - mLastX) > 60f) { mRefreshLayout.setEnabled; mScrollView.requestDisallowInterceptTouchEvent; } } if(action == MotionEvent.ACTION_UP) { // 用户抬起手指,恢复父布局状态 mScrollView.requestDisallowInterceptTouchEvent; mRefreshLayout.setEnabled; } return false; }});

用户点击到ViewPager时候,记录下点击位置的X坐标,当用户滑动过程中,如果在X轴上面的滑动超过阈值(我写的是60f,这个可以在实际使用中自行设置最佳的阈值),就禁止ScrollView截断点击事件,同时设置不可下拉刷新。当用户手指离开屏幕,将ScrollView和SwipeRefreshLayout的状态恢复。

在ScrollView中嵌套ListView,会出现各种各样奇怪的问题。比如说ListView显示有问题,可能才一两个Item那么高,并没有完全的展开。网上流传解决这种问题的方法会有两种。

  • 根据展示数据的个数乘以每一个Item的高度,计算出ListView的总体高度,然后动态的设置ListView的高度
  • 复写ListView的onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,让ListView完全展开

这两种方法都可以解决ListView展示不完全的问题,而且也可以滑动(其实是使用ScrollView的滑动效果),但是有一个最大的遗憾,就是ListView里面的View不能复用了。因为这两种方法都是算出了ListView的全部高度,然后将ListView控件的高度设置成这个高度,这样的话,ListView就相当于一个LinearLayout的布局了,失去了复用View的优势,而且在某些场景可能还没有LinearLayout好用,更甚的是,如果有大量图片的话,很容易就OOM了,这是在研发过程中最不希望看见的。

可以参考一下美团,美团的首页,就是一个ScrollView,下滑的时候会发现,并不能无限向下滑动,到了底部会提醒跳转到一个二级页面去查看全部的团购信息。这是处理ScrollView里面嵌套类似ListView列表布局的时候的一种解决方案。

但是在我遇见的这个项目里面,并不能这样处理。

上面的提到的两种解决思路很明确,如果想要ListView正常展示就需要确定ListView的高度,这个很重要。

所以首先,我需要在布局文件中设置ListView的高度,是一个明确的数值。设置高度之后,如果ListView中的数据的Item总高度超过ListView所设置的高度,就可以复用View了。但是这只是解决了ListView的显示问题,ListView与ScrollView的滑动冲突,并没有解决。

要解决滑动的冲突,最主要的是确定禁止ScrollView截断点击事件的时机,然后来分析有哪些时机。

  • ScrollView在未滑动到底部时候,如果点击并滑动ListView时候,ListView是不能滑动的,不禁止。
  • 如果ScrollView滑动到底部,且ListView已经到顶部,继续下拉ListView,其实会拉动ScrollView,不禁止。
  • 如果ScrollView滑动到底部,用户向上滑,ListView滑动,禁止ScrollView截断点击事件能力

很明显,在判断禁止ScrollView截断点击事件时机的时候,需要知道ScrollView是否滑动到了底部。于是,重写了ScrollView的ScrollChanged()方法,来判断ScrollView是否滑动到底部(SDK API 23版本中ScrollView可以设置setOnScrollChangeListener()来监听滑动的变化,但是之前版本不支持,为了兼容,自己需要重写)。

@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt){ super.onScrollChanged(l,t,oldl,oldt); // 滑动的距离加上本身的高度与子View的高度对比 if(t   getHeight() >= getChildAt.getMeasuredHeight{ // ScrollView滑动到底部 if(mOnScrollToBottomListener != null) { mOnScrollToBottomListener.onScrollToBottom(); } } else { if(mOnScrollToBottomListener != null) { mOnScrollToBottomListener.onNotScrollToBottom(); } }}public void setScrollToBottomListener(OnScrollToBottomListener listener) { this.mOnScrollToBottomListener = listener;}public interface OnScrollToBottomListener { void onScrollToBottom(); void onNotScrollToBottom();}

有了思路,而且ScrollView滑动到底部的标识也可以拿到,下面就可以直接来解决滑动冲突了,直接看代码。

mScrollView.setScrollToBottomListener(new BottomScrollView.OnScrollToBottomListener() { @Override public void onScrollToBottom() { isSvToBottom = true; } @Override public void onNotScrollToBottom() { isSvToBottom = false; }});mListView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if(action == MotionEvent.ACTION_DOWN) { mLastY = event.getY(); } if(action == MotionEvent.ACTION_MOVE) { int top = mListView.getChildAt.getTop(); float nowY = event.getY(); if(!isSvToBottom) { // 允许scrollview拦截点击事件, scrollView滑动 mScrollView.requestDisallowInterceptTouchEvent; } else if(top == 0 && nowY - mLastY > THRESHOLD_Y_LIST_VIEW) { // 允许scrollview拦截点击事件, scrollView滑动 mScrollView.requestDisallowInterceptTouchEvent; } else { // 不允许scrollview拦截点击事件, listView滑动 mScrollView.requestDisallowInterceptTouchEvent; } } return false; }});

相对于其他的控件来说,ListView和ScrollView之间的滑动冲突更难解决,但其实在实际使用中并不推荐ScrollView里面嵌套ListView,一旦业务复杂,很容易出现各种UI和业务逻辑冲突的错误。

由于地图加入比较麻烦,所以在Demo中并没有引入地图。看一下运行效果。

图片 5运行效果

本篇文章只是提供一种解决方法的思路,在具体的场景下,交互往往是贴合具体业务需求的。但是不管怎么样,找出点击事件截断和处理的时机是最重要的,围绕这个关键点,总能找出相应的解决方法。

附上Demo工程地址:Demo工程地址链接

TAG标签:
版权声明:本文由美高梅网投平台发布于新闻中心,转载请注明出处:Android滑动冲突处理,Android实践之ScrollView中滑动