我有一个围绕整个布局的ScrollView,因此整个屏幕都是可滚动的.这个ScrollView中的第一个元素是HorizontalScrollView挡路,它具有可以水平滚动的功能.我已经向Horizontalscroll视图添加了一个ontouch侦听器来处理touch 事件,并强制视图"捕捉"到action_up事件上最接近的图像.

所以我想要的效果就像是普通的Android主屏幕,你可以从一个滚动到另一个,当你抬起手指时,它就会弹到一个屏幕上.

这一切都很好,除了一个问题:我需要从左向右几乎完美地水平滑动一个动作直到注册为止.如果我至少垂直滑动(我认为很多人在手机上从一边到另一边滑动时都会这样做),我会收到一个动作取消,而不是一个动作向上.我的理论是,这是因为horizontalscrollview在scrollview中,而scrollview正在劫持垂直touch 以允许垂直滚动.

如何在水平滚动视图中禁用滚动视图的touch 事件,但仍允许在滚动视图的其他位置进行正常的垂直滚动?

以下是我的代码示例:

   public class HomeFeatureLayout extends HorizontalScrollView {
    private ArrayList<ListItem> items = null;
    private GestureDetector gestureDetector;
    View.OnTouchListener gestureListener;
    private static final int SWIPE_MIN_DISTANCE = 5;
    private static final int SWIPE_THRESHOLD_VELOCITY = 300;
    private int activeFeature = 0;

    public HomeFeatureLayout(Context context, ArrayList<ListItem> items){
        super(context);
        setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        setFadingEdgeLength(0);
        this.setHorizontalScrollBarEnabled(false);
        this.setVerticalScrollBarEnabled(false);
        LinearLayout internalWrapper = new LinearLayout(context);
        internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        internalWrapper.setOrientation(LinearLayout.HORIZONTAL);
        addView(internalWrapper);
        this.items = items;
        for(int i = 0; i< items.size();i++){
            LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.homefeature,null);
            TextView header = (TextView) featureLayout.findViewById(R.id.featureheader);
            ImageView image = (ImageView) featureLayout.findViewById(R.id.featureimage);
            TextView title = (TextView) featureLayout.findViewById(R.id.featuretitle);
            title.setTag(items.get(i).GetLinkURL());
            TextView date = (TextView) featureLayout.findViewById(R.id.featuredate);
            header.setText("FEATURED");
            Image cachedImage = new Image(this.getContext(), items.get(i).GetImageURL());
            image.setImageDrawable(cachedImage.getImage());
            title.setText(items.get(i).GetTitle());
            date.setText(items.get(i).GetDate());
            internalWrapper.addView(featureLayout);
        }
        gestureDetector = new GestureDetector(new MyGestureDetector());
        setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
                else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL ){
                    int scrollX = getScrollX();
                    int featureWidth = getMeasuredWidth();
                    activeFeature = ((scrollX + (featureWidth/2))/featureWidth);
                    int scrollTo = activeFeature*featureWidth;
                    smoothScrollTo(scrollTo, 0);
                    return true;
                }
                else{
                    return false;
                }
            }
        });
    }

    class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            try {
                //right to left 
                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    activeFeature = (activeFeature < (items.size() - 1))? activeFeature + 1:items.size() -1;
                    smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
                    return true;
                }  
                //left to right
                else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    activeFeature = (activeFeature > 0)? activeFeature - 1:0;
                    smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
                    return true;
                }
            } catch (Exception e) {
                // nothing
            }
            return false;
        }
    }
}

推荐答案

Update: I figured this out. On my ScrollView, I needed to override the onInterceptTouchEvent method to only intercept the touch event if the Y motion is > the X motion. It seems like the default behavior of a ScrollView is to intercept the touch event whenever there is ANY Y motion. So with the fix, the ScrollView will only intercept the event if the user is deliberately scrolling in the Y direction and in that case pass off the ACTION_CANCEL to the children.

下面是包含HorizontalScrollView的my Scroll View类的代码:

public class CustomScrollView extends ScrollView {
    private GestureDetector mGestureDetector;

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new YScrollDetector());
        setFadingEdgeLength(0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);
    }

    // Return false if we're scrolling in the x direction  
    class YScrollDetector extends SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {             
            return Math.abs(distanceY) > Math.abs(distanceX);
        }
    }
}

Android相关问答推荐

错误:无法解析Symbol@style/Theme. Androidstudio in AndroidManifest.html for Kotlin Android Development''

打开平板电脑的下载文件夹中的文件,例如使用其mimeType将Intent发送到我们的应用程序

在Android Studio Iguana 2023.2.1中,哪里可以找到能量分析器?

广播接收者意图从服务内设置,而不被其他服务接收

显示本地房间数据库中未保存的项目的动态列表

添加可组合元素的列表?

Android在NavHost中的LazyColumn中编写约束布局:error - replace()在未放置的项目上调用

Android 11:在try 获取文件的永久权限后,仍然没有读写权限

Android布局渲染问题

设置文本 colored颜色 动画时如何减少重新组合?

Android 14预测性背部手势-闪烁的白色背景色

如何迭代 SqlDelight Select 结果而不将所有内容加载到内存中?

Electric Eel 后 Gradle 项目同步失败 | 2022.1.1更新

在 Jetpack Compose 中使用 .observeAsState() 时,如何在更改 MutableLiveData 的值后开始执行一段代码?

Jetpack Compose Arc 进度条动画(如何重启动画)

如何在 TextButton 中分隔文本和图标

JetPack Compose - 卡片行中的权重()不起作用

Jetpack Compose:mutableStateOf 不随流量更新

如何将房间数据库导出到 .CSV

在使用 Retrofit 和 Room 时,我是否需要提及协程调度程序?