5

我已经阅读了一些关于这个主题的问题,但还没有真正找到一个可靠的答案。

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

框架布局

  • customView[2] <--- 这是添加的最后一个视图,也是唯一接收事件的视图
  • 自定义视图[1]
  • 自定义视图[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;
    }
}
4

2 回答 2

16

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 -> 触发父触摸事件 -> 再次触发子触摸事件

于 2011-11-18T22:55:10.303 回答
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)
    }
于 2019-11-04T14:40:36.303 回答