【问题标题】:Passing touch events to the parent view将触摸事件传递给父视图
【发布时间】:2011-09-16 12:07:07
【问题描述】:

我有一个自定义的ViewSwitcher,我在其中实现了触摸事件,所以我 能够使用触摸屏滚动屏幕。

我的布局层次结构如下所示:

<ViewSwitcher>

    <LinearLayout>
        <ListView />
    </LinearLayout>

    <LinearLayout>
        <ListView />
    </LinearLayout>

</ViewSwitcher>

现在,问题是触摸事件正在被 ListViews 我无法切换视图。当我工作正常 没有ListViews。我需要能够滚动浏览 查看并滚动ListView

我该如何解决这个问题?

编辑:我还需要ListView 项目是可点击的。

提前致谢!

【问题讨论】:

    标签: android android-layout android-listview


    【解决方案1】:

    感谢大家回答问题。但我能够以一种更简单的方式弄清楚。由于我的ViewSwitcher 没有检测到触摸事件,我拦截了触摸事件,调用onTouchEvent() 并返回false。这里:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        onTouchEvent(ev);
        return false;
    }
    

    通过覆盖onInterceptTouchEvent(),我能够拦截活动中的触摸事件。然后我在处理ListViews 切换的ViewSwitcher 中调用了onTouchEvent()。最后通过返回false,它确保ViewGroup 不会消耗事件。

    【讨论】:

    • 假设我想在gridView的单元格内有一个horizo​​ntalScrollView,为什么在这种情况下使用这个解决方案不能很好地工作,并允许我同时进行水平滚动和单击/长按单元格网格视图的?
    • 这帮助我解决了 ViewPager 问题,我想我会把它扔给后代。我在 ViewPager 中有必须附加 OnClickListener/OnTouchListener 的视图。这些消耗导致父 ViewPager 不滚动的运动事件。通过将此技巧应用于包含视图寻呼机的布局,我可以将所有触摸事件转发到寻呼机(滚动器),而不管孩子是否将事件作为点击来消费。
    • 我不认为这是在 onInterceptTouchEvent() 中调用 onTouchEvent() 的“官方”方式,除非这意味着某种黑客行为。因为如果你从 onInterceptTouchEvent() 返回 true,onTouchEvent() 无论如何都会被调用。
    • 有关此答案的更多信息(以正确的方式完成):developer.android.com/training/gestures/viewgroup.html
    • 在对其他解决方案进行了数小时的反复试验后,我找到了这个解决方案,它在我的情况下有效。一个水平滚动视图和一个父线性布局,其中线性布局没有接收到触摸事件
    【解决方案2】:

    将子视图的 TouchEvent 传递给父视图的最简单方法是将其添加到子视图:

    @Override
    public boolean onTouchEvent(MotionEvent event) { 
        super.onTouchEvent(event);
        return false;
    }
    

    【讨论】:

    • 这是唯一适用于 RecyclerView 的解决方案。看起来你必须扩展 RecyclerView,覆盖 onTouchEvent 并禁用任何进一步的交互,事件链继续。
    • 当我将此添加到我的回收站视图中时,列表项不接受点击事件
    【解决方案3】:

    在我的父布局中,我发现阻止子捕获触摸事件的唯一方法是重写 onInterceptTouchEvent 以返回 true。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        return true;
    }
    

    【讨论】:

      【解决方案4】:

      我认为您没有简单的方法来做到这一点。

      这并不复杂,但您需要创建自己的视图来扩展 ListView。然后您可以覆盖onTouch 处理程序并决定(取决于触摸事件)是要处理它(并返回true)还是将其传递给父视图。

      问题还在于,一旦 View 处理了一个触摸事件,它将获得所有剩余的事件......

      来自Android documentation

      onTouch() - 返回一个布尔值 表明你的听众是否 消费这个事件。重要的 事情是这个事件可以有 每个动作之后的多个动作 其他。所以,如果你返回 false 时 收到向下的动作事件,你 表示你没有消费 事件,也不感兴趣 在此事件的后续操作中。 因此,您将不会被要求任何 事件中的其他操作,例如 作为一个手指手势,或最终 向上动作事件

      因此,例如,如果您希望垂直移动以滚动列表并在同一触摸事件期间(不抬起手指),您希望水平移动以切换视图,这将非常具有挑战性。

      但是,如果您可以使用手势或处理自定义视图中的所有内容,并且取决于 MotionEvent 是什么,请向 ViewSwitcher 发送命令。

      【讨论】:

        【解决方案5】:

        您是否尝试将 ListView 项目设置为不可点击,如下所示:listView.setClickable(false);这应该向上传播点击事件。

        【讨论】:

        • 这并没有解决它。而且我还需要列表视图的项目是可点击的。
        【解决方案6】:

        如果您的视图想要向上传递事件,请确保在 onTouchEvent 中返回 false。否则,平台认为您消费了该事件,无需进一步处理。

        【讨论】:

          【解决方案7】:

          我所做的是在viewswitcher的listview上设置一个toucheventlistener,处理事件,检测fling动作并调用viewswitcher的metchod。它有效。

          【讨论】:

            【解决方案8】:

            您还可以在dispatchTouchEvent 中插入处理touchevent 的调用,但在这种情况下,您还必须覆盖onTouchEvent 以返回true,否则只会传递手势的第一个MotionEvent DOWN .

            这是可触摸的包装容器:

            <?xml version="1.0" encoding="utf-8"?>
            <view xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                class="com.example.myapp.FragmentContainer$TouchableWrapper" />
            

            还有班级:

            public static class TouchableWrapper extends FrameLayout {
                private GestureDetector gestureDetector;
                public void setGestureDetector(GestureDetector gestureDetector) {
                    this.gestureDetector = gestureDetector;
                }
            
                // these constructors for the XML inflater
                public TouchableWrapper(Context context) {
                    super(context);
                }
                public TouchableWrapper(Context context, AttributeSet attrs) {
                    super(context, attrs);
                }
                public TouchableWrapper(Context context, AttributeSet attrs, int defStyle) {
                    super(context, attrs, defStyle);
                }
            
                @Override
                public boolean onInterceptTouchEvent(MotionEvent event) {
                    Log.d(TAG, "onInterceptTouchEvent " + event.toString());
                    return false; // true for intercept, false è default and pass on to children View
                }
                @Override
                public boolean dispatchTouchEvent(MotionEvent event) {
                    Log.d(TAG, "dispatchTouchEvent " + event.toString());
                    gestureDetector.onTouchEvent(event);
                    return super.dispatchTouchEvent(event);
                }
                @Override
                public boolean onTouchEvent(MotionEvent event) {
                    Log.d(TAG, "onTouchEvent " + event.toString());
                    return true; //return super.onTouchEvent(event); 
                }
            }
            

            这是GestureDetector 参考:

            private GestureDetector gestureDetector;
            

            这是GestureListener

            private GestureDetector.SimpleOnGestureListener sOGL = new GestureDetector.SimpleOnGestureListener() {
                private static final int SWIPE_THRESHOLD = 100;
                private static final int SWIPE_VELOCITY_THRESHOLD = 100;
            
                @Override
                public boolean onDown(MotionEvent e) {
                    return true;
                }
            
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                    boolean result = false;
                    try {
                        float diffY = e2.getY() - e1.getY();
                        float diffX = e2.getX() - e1.getX();
                        if (Math.abs(diffX) > Math.abs(diffY)) {
                            if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                                if (diffX > 0) {
                                    goToRight(); // onSwipeRight();
                                } else {
                                    goToLeft(); // onSwipeLeft();
                                }
                            }
                            result = true;
                        } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffY > 0) {
                                // onSwipeBottom();
                            } else {
                                // onSwipeTop();
                            }
                        }
                        result = true;
            
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }
                    return result; // return false indicate event not handled
                }
            };
            

            Ant this 加载可触摸的容器片段和包含的片段:

            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                Log.d(TAG, "onCreateView()");
                View view = inflater.inflate(R.layout.fragmentcontainer, container, false);
            
                gestureDetector = new GestureDetector(view.getContext(), sOGL);
                ((FragmentContainer.TouchableWrapper) view).setGestureDetector(gestureDetector);
            
                FragmentManager fm = this.getFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                ft.replace(R.id.pager, frag).commit();
            
                return view;
            }
            

            【讨论】:

              【解决方案9】:

              有必要在父视图的 TextView 中使用 autoLink = "three" 处理触摸 这样做了:

              private LinearLayout mParentView;
              private TextView mTextView;
              private View.OnTouchListener mParentListener = new OnTouchListener() {
              
                  @Override
                  public boolean onTouch(View v, MotionEvent event) {
                      ......
                      return false;
                  }
              };
              mParentView.setOnTouchListener(mParentListener);
              
              mTextView.setOnTouchListener(new View.OnTouchListener() {
                  @Override
                  public boolean onTouch(View v, MotionEvent event) {
                      mParentListener.onTouch(mParentView, event);
                      return false;
                  }
              };
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-04-28
                • 2014-05-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多