如何在父视图中处理子视图的触摸事件

5
我正在制作一个自定义日历视图,它扩展了 LinearLayout 并具有每个日期的子视图。我想要做的是处理滑动和点击事件,就像你可以想象的那样,滑动是用来改变月份的,而点击则是用于选择日期并显示新的活动。为此,我在 CalendarView 上使用 GestureDetector,并且已经能够使其工作于滑动事件上。但是对于处理点击事件,我不知道该如何找到哪个子视图发生了点击。

  1. 有没有人有解决这个问题的想法?
  2. OnScroll(MotionEvent) 中返回 true 和 false 有什么区别?

以下是我的部分代码:

public class MonthView extends LinearLayout implements GestureDetector.OnGestureListener {

    public MonthView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        gestureDetector = new GestureDetector(this);
        initDateViews();
    }

    //other codes here
    ....

    private void initDateViews() {
        for(int i = 0; i < 42; i++) {
            DateView view = new DateView();
            //init date views and add to calendar view.
            ....
            calendar.Add(view);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.debug(TAG, ">>> MonthView.onTouchEvent()");

        return gestureDetector.onTouchEvent(event);
    }

    @Override
    public boolean OnSingleTapUp(MotionEvent event) {
        // how can I find a child view to handle click event?
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        // right to left
        if (e1.getX() - e2.getX() > minSwipeDistance) {
            this.prevMonth();
        }
        // left to right
        else if(e2.getX() - e1.getX() > minSwipeDistance) {
            this.nextMonth();
        }
        // bottom to top
        else if(e1.getY() - e2.getY() > minSwipeDistance) {
            this.prevMonth();
        }
        //top to bottom
        else if(e2.getY() - e1.getY() > minSwipeDistance) {
            this.nextMonth();
        }

        return false;
    }

    ....
}
2个回答

1
on[GestureEvent]回调的布尔返回值表示回调函数是否消耗了事件。也就是说,在您的onScroll实现中,应该返回true,因为您正在处理这些事件。
然而,onSingleTapUp应该返回false,以表示您希望事件传递给子视图。
然后,您应该注册一个OnClickListener,能够在每个子视图中处理实际数据。
private void initDateViews() {
    for(int i = 0; i < 42; i++) {
        DateView view = new DateView();
        //init date views and add to calendar view.
        ....

        // register onClickListener for e.g. date
        view.setOnCLickListener(new DateOnClickListner(date))
        calendar.Add(view);
    }
}

使用类似以下的 OnclickListener 实现:
class DateOnClickListner(Date date) extends View.OnClickListener {
    public void onClick(View v) {
        ...
    }
}

谢谢您的建议,但实际上这似乎与我最初尝试在日历视图上使用onTouchEvent()和在子视图上使用onClick()的代码非常相似。然而,当手势在子视图上开始时,这并没有像我预期的那样工作。因为子视图通过onClick()回调方法消耗了OnDown事件。这就是为什么我决定在日历视图上使用GestureDetector来处理滑动和点击。 - genki98

0
我通过在MonthView上重写'onInterceptTouchEvent()'来解决了这个问题,并将解决方案发布出来,以帮助那些可能遇到相同问题的人。 我使用GestureDetector实现了在'onFling()'中切换月份的功能,代码如下。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean result = gestureDetector.onTouchEvent(event);

    return result;
}

@Override
public boolean onDown(MotionEvent e) {
    return false;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    // right to left
    if (e1.getX() - e2.getX() > minSwipeDistance) {
        nextMonth();
    }
    // left to right
    else if(e2.getX() - e1.getX() > minSwipeDistance) {
        prevMonth();
    }
    // bottom to top
    else if(e1.getY() - e2.getY() > minSwipeDistance) {
        nextMonth();
    }
    //top to bottom
    else if(e2.getY() - e1.getY() > minSwipeDistance) {
        prevMonth();
    }

    return true;
}

@Override
public void onLongPress(MotionEvent e) {
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return false;
}

@Override
public void onShowPress(MotionEvent e) {
}

@Override
public boolean onSingleTapUp(MotionEvent e) {
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接