HorizontalScrollView及ScrollView解决滑动冲突

本文介绍了解决Android中ScrollView滑动冲突的方法,包括禁止ScrollView滑动及通过重写onInterceptTouchEvent方法实现事件传递,同时提供了HorizontalScrollView手势拦截的具体实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、在很多的形情之下,用到ScrollView的必定会遇到滑动冲突。

解决方法有可以把ScrollView的滑动设为禁止:

        //禁止scrollview 的滑动
        scrollView.setOnTouchListener(new View.OnTouchListener() {
           @Override
           public boolean onTouch(View view, MotionEvent motionEvent) {
                return true;
            }
        });
这样ScrollView的滑动功能就会失效。

但这只是表面上的在整个view的传递中,Touch事件还是会被ScrollView或者HorizontalScrollView所消费掉。ScrollView或HorizontalScrollView包裹的子view就不会接收到Touch事件。所以在子view中无法进行其他关于Touch事件的操作。

要解决事件传递,创建一个HorizontalScrollView或ScrollView的子类继承其全部功能,在这基础上重写onInterceptTouchEvent()方法,把返回值直接置为false。则Touch事件就会经由控件基础上传递到包裹的子view中。等view消费了事件之后则解决了这个ScrollView不往下传事件的问题:

**
 * @author wangyao
 * @package 
 * @date 2017/8/2  15:58
 * @describe 用于解决HorizontalScrollView横向滑动的拦截的问题
 * @project
 */

public class MyHorizontalScrollView extends HorizontalScrollView {
    public MyHorizontalScrollView(Context context) {
        super(context);
    }

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

    public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        return false;
    }
}


二、

1、在事件的传递中经常用到的三个方法是:dispatchTouchEvent():事件是否往下(子view)去分发;onInterceptTouchEvent():事件的拦截,事件是否在这里拦截或者是往下传递;onTouchEvent():事件的消费处理,如此为止消费掉则一个事件的过程完毕。

2、一个事件是先由父View->子View往下传的;

3、如所有的子View的onTouchEvent()都不消费,这个事件再由子View逐层传给父View这要看onTouchEvent()方法去不去消费事件;


三、进行HorizontalScrollView手势的拦截的demo:

需求:在HorizontalScrollView中进行双指滑动HorizontalScrollView跟随着手势滑动,单指滑动的事件自己不处理,留给其子view进行手势的处理。

思想:在自定义MyHorizontalScrollView中的onInterceptTouchEvent()的方法里进行处理事件拦截的条件。

代码:

package com.example.administrator.canvasdemo.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;

/**
 * @author wangyao
 * @package com.example.administrator.canvasdemo.view
 * @date 2017/8/29  20:31
 * @describe TODO
 * @project
 */

public class MyHorizintalScrollView extends HorizontalScrollView {
    private boolean isDragCanvsMode;
    private boolean isDoubleDrag = false;
    private float mStartDownX;
    private float mStartDownY;
    private float mMoveDragCanvsX;
    private float mMoveDragCanvsY;

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

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

    public MyHorizintalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //多点拖动的操作
        switch (event.getAction()&MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:{
//                Log.i("everb","按下去:"+mStartDownX);
            }
            break;
            case MotionEvent.ACTION_POINTER_DOWN:{
                isDoubleDrag = true;
            }
            break;
            case MotionEvent.ACTION_MOVE:{
//                Log.i("everb", "canvas scrollview onTouchEvent:" + event.getX() + " " + event.getY() + " event.getAction(): " + event.getAction()+ " isDoubleDrag:"+isDoubleDrag);
                mMoveDragCanvsX = event.getX();
                Log.i("everb","move的值:"+mMoveDragCanvsX);
                float canvsMoveX = mMoveDragCanvsX - mStartDownX;
                Log.i("everb","移动的值:"+canvsMoveX);
                //双指滑动的距离进行双指滑动HorizontalScrollView也进行随手势滑动
                this.scrollBy(-(int)canvsMoveX,0);
            }
            break;
            case MotionEvent.ACTION_CANCEL:{
                isDoubleDrag = false;
//                Log.i("everb", "canvas scrollview onTouchEvent:ACTION_CANCEL");
            }
            break;
        }
        mStartDownX=mMoveDragCanvsX;
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        switch (event.getAction()&MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:{
                mStartDownX = event.getX();
                isDragCanvsMode=false;
            }
            break;
            case MotionEvent.ACTION_POINTER_DOWN:{
                //多指按下的识别
                isDragCanvsMode=true;
            }
            break;
        }
//        Log.i("everb","canvas scrollview onInterceptTouchEvent:"+event.getX()+" "+event.getY() + " event.getAction()"+event.getAction());

        if(isDragCanvsMode == false){
            //如单指按下则事件不拦截,把事件传递到子view中
            return false;
        }
//        Log.i("everb","canvas scrollview onInterceptTouchEvent:"+event.getX()+" "+event.getY() + " event.getAction()"+event.getAction());
        //如是双指按下则返回true,把事件拦截在自己的onTouchEvent进行处理
        return true;
    }
}


### HorizontalScrollViewScrollView 的同时使用 在 Android 开发中,`HorizontalScrollView` 和 `ScrollView` 可以同时使用,但这需要特别注意它们的嵌套方式及其可能引发的问题。以下是实现两者的具体方法及相关注意事项。 #### 实现方法 为了使 `HorizontalScrollView` 和 `ScrollView` 能够正常工作,通常的做法是将 `HorizontalScrollView` 放置在一个 `ScrollView` 中作为其唯一的子视图。由于 `ScrollView` 和 `HorizontalScrollView` 都具有滚动特性,因此需要通过调整布局结构和处理触摸事件来避免冲突。 以下是一个典型的 XML 布局示例: ```xml <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 其他垂直排列的内容 --> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> <!-- 水平排列的内容 --> <Button android:text="Item 1" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:text="Item 2" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <!-- 更多水平内容 --> </LinearLayout> </HorizontalScrollView> <!-- 其他垂直排列的内容 --> </LinearLayout> </ScrollView> ``` 上述代码展示了如何将 `HorizontalScrollView` 嵌套到 `ScrollView` 中,并确保每个控件的功能独立运行[^4]。 #### 注意事项 1. **单一子视图限制** `ScrollView` 和 `HorizontalScrollView` 都仅允许拥有一个直接子视图。这意味着如果需要放置多个组件,则应将其包裹在一个容器(如 `LinearLayout` 或 `RelativeLayout`)中[^2]。 2. **性能问题** 如果 `ScrollView` 和 `HorizontalScrollView` 同时存在大量数据或复杂布局,可能会导致性能下降。这是因为每次重新测量和布局都会消耗额外资源。建议尽可能简化内部布局并优化渲染逻辑[^3]。 3. **触摸事件冲突** 默认情况下,`ScrollView` 和 `HorizontalScrollView` 不会发生明显的触摸事件冲突,因为 `ScrollView` 会优先拦截纵向滑动手势,而 `HorizontalScrollView` 则专注于横向手势。然而,在某些特殊场景下仍需手动管理触摸行为。可以通过重写 `onInterceptTouchEvent(MotionEvent ev)` 方法来自定义事件分发机制。 4. **不可与 ListView 结合使用** 尽管理论上可以尝试组合多种滚动画布,但实际开发中不推荐让 `ListView` 成为 `HorizontalScrollView` 或 `ScrollView` 的后代节点之一。原因在于这会导致 `ListView` 的优化策略失效,从而显著降低应用流畅度。 5. **动态测量与布局** 当程序运行期间需要动态计算尺寸时,可参照所提供的 Kotlin 示例代码完成相应操作[^1]: ```kotlin val horizontalScrollView = findViewById<HorizontalScrollView>(R.id.horizontalScrollView) horizontalScrollView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) horizontalScrollView.layout(0, 0, horizontalScrollView.measuredWidth, horizontalScrollView.measuredHeight) ``` 此片段可用于初始化未完全加载完毕前即被访问的视图对象状态。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值