【问题标题】:Is it possible to pass an onTouchEvent to multiple views in Android?是否可以将 onTouchEvent 传递给 Android 中的多个视图?
【发布时间】:2012-01-01 16:10:09
【问题描述】:

我在 SO 上阅读了一些关于此主题的问题,但还没有真正找到可靠的答案。

我有一个框架布局,可以在其上堆叠多个自定义视图,但是 onTouch 事件仅适用于顶视图。 (自定义view都是同一个view,同一个onTouch事件,只是多个)

框架布局

  • customView[2]
  • 自定义视图[1]
  • customView[0]

我正在 Android 2.2 上对其进行测试,我想知道下面的其他视图是否有任何方法可以知道触摸发生的位置?


编辑(添加一些代码)

我正在添加一些代码,希望能帮助解释我遇到问题的地方。起初我只是自动让onTouchEvent 返回真。这使得最后一个视图(在我的情况下为 customerView[2])将是唯一生成值的视图。

但是,一旦我添加了将onTouchEvent 设置为返回真或假的方法,现在返回生成值的唯一视图是customView[0]

我希望这能解决我的问题。我对此很陌生,感谢您花时间解释它(当然,我感谢您的耐心等待)。

另外,我意识到我的 TextView 不会更新每个 touchEvent 上的值,我正在努力解决这个问题。

我的活动:

public class MyActivity extend Activity {

    CustomView[] customView;
    TextView[] textView;
    int numViews 3;
    //FrameLayout and Params created

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        for(int i = 0; i < numViews; i++) {
            customView[i] = new CustomView(this, i);


            //Allows the onTouch to be handled by all Views - View[0] is the bottom view
            if(i == 0) {
                customView[i].setTouchBool(true);        //set view's onTouch to return true
            } else {
                customView[i].setTouchBool(false);       //set view's onTouch to return false
            }

            //Set TextView to display the number generated by the CustomView
            textView[i].setText(Double.toString(customView[i].getGeneratedNumber()));

            //Add views to main layout
            frame.addView(textView[i]);
            frame.addView(customView[i]);
        }
    }
}

我的看法:

public class CustomView extends View {

    boolean onTouchHandler = true;
    int xVal = 0, yVal = 0;
    int index;
    double generatedNum = 0;

    public CustomView(Context context) {
        this(context, 0);
        this.index = 0;
    }

    public CustomView(Context context, int index) {
        super(context);
        this.index = index;
    }


    @Override 
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();

        switch(action) {
            case MotionEvent.ACTION_DOWN: {
                //do logic 
            }

            case MotionEvent.ACTION_MOVE: {
                //do logic
            }

            case MotionEvent.ACTION_UP: {
                xVal = (int) ev.getX();
                yVal = (int) ev.getY();

                generateNumber(xVal, yVal, index);

                break; 
            }
        }
         return onTouchHandler;    
    }  


    private void generateNumber(int x, int y, int index) {
        if(index == 0) {
            generatedNum = (x / 2) * (y / 2) + 64;
        } else {
            generatedNum = (x / 2) * (y / 2) + (index * 128);
        }
    }

    public double getGeneratedNumber() {
        return generatedNum;
    }

    public boolean setTouchBool(boolean b) {
        this.onTouchHandler = b;
    }
}

【问题讨论】:

    标签: android view touch-event android-framelayout


    【解决方案1】:

    我知道这个问题很老了,但是我遇到了同样的问题,并通过创建自己的布局来确定实际触摸了哪个孩子来解决它。

    因此,我遍历自定义布局的子级并检查用户是否真正点击了视图。碰撞检测在自定义视图的 onTouch() 方法中处理。 (碰撞检测是通过将 Region() 与事件的 x,y 坐标相交来完成的。对我来说这很方便,因为我使用 Path() 绘制了自定义视图)

    这是我的自定义布局中的一个 kotlin 代码 sn-p,以便更好地理解:

    class CustomLayout(context: Context, attrs: AttributeSet) : 
    RelativeLayout(context, attrs){
    
        override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
            if(ev.action != MotionEvent.ACTION_UP){
               return true
            }
            //Iterate over child view and search for the right child that should handle this touch event
            for (i in childCount - 1 downTo 0) {
                val child = getChildAt(i)
                if (!viewTouched(child, ev)) {
                    continue
            }
            //Do something
            Timber.d("Touched view: ${child.id}")
            }
            return true
        }
    
        private fun viewTouched(child: View, ev: MotionEvent) : Boolean {
            child as OnTouchListener
            //onTouch() does the collision detection
            return child.onTouch(child, ev)
        }
    

    【讨论】:

      【解决方案2】:

      Android 将在每个视图上级联调用 onTouchEvent 的视图,直到它从其中一个接收到 true。如果您希望所有触摸事件都被处理,则返回 false 直到到达最后一个。

      编辑:

      好的。如果我理解正确,您有一个包含一堆子视图的单个顶视图,其中一层很深。我最初的答案是假设您在 ViewGroup 的层次结构中拥有三个相互重叠的自定义视图(View3 是 View2 的子级。View2 是 View1 的子级。View1 是 ParentView 的子级)。您希望将父视图上的用户触摸事件发送给它的所有子视图。

      如果是这样的话,AFAIK,Android 的 API 中没有允许这样做的视图。因此,您必须创建一个自定义视图来执行此操作。

      好的,我还没有测试过这个,所以请告诉我它是否有效,如果它是你正在尝试的。创建一个扩展任何对象 frame 的自定义类,然后像这样覆盖 onTouch 方法。

      @Override 
      public boolean onTouchEvent(MotionEvent ev) {
         for(int i = 0; i < this.getChildCount(); i++){
            this.getChildAt(i).dispatchTouchEvent(ev);
         } 
         return true;
      } 
      

      现在,保持与您的自定义视图相同的逻辑,除了它们都应该返回 false,因为您的父视图将不会收到 onTouch 事件,除非它们按照我之前的回答中所述进行

      注意: 使用此实现,用户实际触摸的子视图将触发两次,因为逻辑将执行 触发子触摸事件 -> 返回 false -> 触发父触摸事件 -> 再次触发子触摸事件

      【讨论】:

      • 判断哪些视图返回 true 的唯一方法是通过您编写的代码逻辑。每个视图都必须决定是否已处理触摸。如果有,请返回true。如果没有,那么您可以调用super.onTouchEvent(),它将根据父视图返回的内容返回真或假。至于为每个视图收集值,这完全取决于你的类结构是如何设置的。
      • 我意识到我给你的工作很少(没有代码)。我继续输入了一些类似于我的视图和活动的代码。也许你可以告诉我我需要从那里去哪里。我希望每个视图都生成一个数字,而不仅仅是一个视图。最终,我希望 TextViews 显示它们,但我试图一次走一步。我很感激你到目前为止给我的解释。
      • 哦,我想我现在明白了。我认为每个视图都是另一个视图的子视图,并且您希望 onTouch 事件向上遍历层次结构,这通常是人们要求的。从它的外观来看,它们都是单一视图的孩子。您希望将单点触摸事件发送到所有子视图,对吗?我会用可能的解决方案更新我的答案。
      • 我为迟到的回复道歉(这是假期)。如果我理解正确,我应该执行以下操作:frameLinearLayout,所以我应该扩展它并覆盖 onTouchEvent。那么在那之后我唯一应该编辑的就是让我的CustomView's onTouchEvent 总是返回false?如果这是正确的,我会继续实施并报告!
      • 现在,我只说他们应该返回false,因为他们都在LinearLayout之上,这意味着用户将触摸孩子而不是父母,所以你需要一种方法将事件级联到父级。这是我能够做到这一点的唯一方法,但话又说回来,我再也不需要以另一种方式做到这一点了。现在想来,这个方法可能会不断地做一个递归循环。您可以通过直接在孩子上调用onTouch() 而不是dispatchTouchEvent() 来解决这个问题,或者有一种方法可以通过焦点方法禁用触摸事件。我会研究一下。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-03
      • 2020-11-08
      • 2012-10-12
      • 2020-10-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      相关资源
      最近更新 更多