【问题标题】:How to scroll the scrollable content loaded in the webview which is one item of the RecycleView如何滚动 webview 中加载的可滚动内容,这是 RecycleView 的一项
【发布时间】:2018-12-03 22:24:52
【问题描述】:

有一个 RecycleView,它有几个项目,其中之一是一个包含 WebView 的视图。在 WebView 内部,它加载了一个 html,并且有一个 iframe,其中有一个 html 表格内容,并且可以垂直滚动(包含的 div 的固定高度小于表格的高度)。

RecyclerView 似乎存在问题,它没有将触摸事件传递给 WebView。

这是怎么回事:

触摸屏拖动时,recycleView的onInterceptTouchEvent()先return false at ACTION_DOWN再return true when the event is MotionEvent.ACTION_MOVE。这个return true 将向目标子视图发送 MotionEvent.Cancel,以及所有进一步 事件将被传递到 RecycleView 的 onTouchEvent()

TestDerivedRecycleView:onInterceptTouchEvent(), return: false     
              event: MotionEvent { action=ACTION_DOWN, actionButton=0... }, this.scrollState: 0

TestDerivedRecycleView:onInterceptTouchEvent(), return: true
              event: MotionEvent { action=ACTION_MOVE, actionButton=0... }, this.scrollState: 1

所以我创建了一个派生的 RecycleView,当目标视图上的事件是 ACTION_MOVE 时是 WebView 然后return false to let WebView handle the drag/move。

它正在部分工作。由于 webView 可能有一些其他的内容,例如文本区域。如果触摸该文本区域,则它是不可滚动的,因此回收视图将不会滚动。仅当触摸并拖动该表格元素时,表格元素才会滚动表格内容。

所需的行为应该是当拖动 RecyclerView 上的任何项目时,RecyclerView 应该向上滚动项目,只有当拖动该可滚动表格元素时,如果表格底部未显示,表格应该滚动其内容。

当 webview 是 RecycleView 的子项时,如何使 webview 的内容滚动工作?

class TestDerivedRecycleView : RecyclerView {
    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}


    override fun onTouchEvent(event: MotionEvent): Boolean {

        val ret = super.onTouchEvent(event)

        Log.d("+++", "+++ TestDerivedRecycleView:onTouchEvent(), ret: $ret" +
                "\nevent.action: ${event.action}, " +
                "\nevent: $event" +
                "\nthis: ${this}")

       return ret   
    }


    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {

        val ret = super.onInterceptTouchEvent(event)

        val myCell = findChildViewUnder(event.x, event.y)

        Log.w("+++", "+++ TestDerivedRecycleView:onInterceptTouchEvent(), ret: $ret" +
                "\nmyCell: $myCell"+
                "\n event.action: ${event.action}, this.scrollState: ${this.scrollState}" +
                "\nevent: $event"+
                "\nthis: $this")

        // when the event is ACTION_MOVE and is touched on the WebView then return false to let WebView handle the event
        if (
                (myCell is ArticleWebView) &&
                event.action == MotionEvent.ACTION_MOVE
                && this.scrollState == RecyclerView.SCROLL_STATE_DRAGGING
        ) { 

            Log.e("+++", "+++ TestDerivedRecycleView:onInterceptTouchEvent(), (myCell is WebView) return false")

            return false
        }
        return ret
    }
}

【问题讨论】:

    标签: scroll android-recyclerview android-webview touch-event


    【解决方案1】:

    找到一个解决方案,虽然不知道。

    基本上,问题是 RecycleView 拦截了“移动”事件(其 super.onInterceptTouchEvent(event) 将返回 true),因此 Webview 将收到“取消”事件并且没有其他后续事件,这使得 RecyclerView 中的项目滚动但webview 没有机会滚动其内容。

    解决方案是不允许 RecyclerView 拦截 'move' 事件。

    这种方法还有其他问题,因为在 WebView 中,html 可能有一些内容是可滚动的,而有些内容是不可滚动的。如果简单地让 webview 接管移动并且触摸点位于某个不可滚动的区域,则触摸/拖动移动将不起作用(RecyclerView 中的项目项也不会滚动,因为它不再拦截事件) .

    所以这个部分修复是只有当两个手指在 webview 上触摸然后禁用 RecyclerView 的拦截。这样至少单指触摸将继续滚动列表项,两指触摸将滚动 webview 的可滚动内容。

    不好的地方还是两根手指触摸一个webview的不可滚动区域将没有滚动效果。

    如果有人知道更好的解决方案,请分享,谢谢!

    在 Webview 中添加了监听器:

    val listener = OnTouchListener { v, event ->
        var perantViewGroup = v.parent
        when {
            event.actionMasked == MotionEvent.ACTION_DOWN -> mFingersDown = 1
            event.actionMasked == MotionEvent.ACTION_POINTER_DOWN -> mFingersDown++
            event.actionMasked == MotionEvent.ACTION_POINTER_UP -> mFingersDown--
            event.actionMasked == MotionEvent.ACTION_UP -> mFingersDown = 0
        }
    
        if ((mFingersDown >= 2) && event.actionMasked == MotionEvent.ACTION_MOVE) {
            perantViewGroup.requestDisallowInterceptTouchEvent(true)
        } else if (event.actionMasked == MotionEvent.ACTION_UP) {
            perantViewGroup.requestDisallowInterceptTouchEvent(false)
        }
    
        false
    }
    webview.setOnTouchListener(listener)
    

    并且还在 RecycleView 中添加一些帮助以防止它拦截“移动”事件。

    class TestDerivedRecycleView : RecyclerView {
        ......
    
        private var mFingersDown = 0
        override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
    
            when {
                event.actionMasked == MotionEvent.ACTION_DOWN -> mFingersDown = 1
                event.actionMasked == MotionEvent.ACTION_POINTER_DOWN -> mFingersDown++
                event.actionMasked == MotionEvent.ACTION_POINTER_UP -> mFingersDown--
                event.actionMasked == MotionEvent.ACTION_UP -> mFingersDown = 0
            }
            val touchedCell = findChildViewUnder(event.x, event.y)
            if ((touchedCell is WebView) && (mFingersDown >= 2) && event.actionMasked == MotionEvent.ACTION_MOVE) {
                return false
            }
            return super.onInterceptTouchEvent(event)
        }
    }
    

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题,并用这个解决了 - 在你的WebView 班级:

      @Override
      public boolean onTouchEvent(MotionEvent event) {   
           requestDisallowInterceptTouchEvent(true);
          return super.onTouchEvent(event);
      }
      
      @Override
      protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
          if(scrollY==0){
              requestDisallowInterceptTouchEvent(false);
          }else{
              requestDisallowInterceptTouchEvent(true);
          }
          super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
      
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-22
        • 1970-01-01
        • 2014-08-09
        • 1970-01-01
        • 2016-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多