是否可以将onTouchEvent传递给Android中的多个视图?

时间:2011-11-18 22:52:47

标签: android view ontouchevent android-framelayout

我已经在SO上阅读了关于这个主题的几个问题,但是还没有真正找到答案。

我有一个framelayout,我可以堆叠多个自定义视图,但是onTouch事件只适用于顶视图。 (自定义视图是具有相同onTouch事件的相同视图,只是其中的多个)

的FrameLayout

  • customView [2] < ---这是添加的最后一个视图,也是唯一一个接收该事件的视图
  • customView [1]
  • customView [0]

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


编辑(添加一些代码)

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

但是,一旦我添加了将onTouchEvent设置为返回true或false的方法,现在返回生成值的唯一视图是 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;
    }
}

2 个答案:

答案 0 :(得分:15)

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事件,除非他们按我之前的回答中所述进行操作

注意:使用此实现,用户实际触摸的子视图将触发两次,因为逻辑将会发生 火儿童触摸事件 - &gt; return false - &gt;火父母触摸事件 - &gt;火儿童触摸事件再次

答案 1 :(得分:0)

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

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

这是我的自定义布局中的kotlin代码片段,用于更好地理解:

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)
    }