使用Android接收onTouch和onClick事件

11
我需要处理onTouch手势和onClick事件,那正确的方式是什么?
我有一个视图上设置了onTouchListener和onClickListener。每当我触摸视图时,首先会触发onTouch事件,然后才是onClick事件。但是,在onTouch事件句柄中,我必须返回true或false。返回true意味着事件被消耗,因此Android事件系统将不会进一步传播该事件。
因此,当我在onTouch事件处理程序中返回true时,不会生成onClick事件,也就是我的onClick监听器永远不会被触发。另一方面,返回false则不行,因为这会防止onTouch监听器接收任何进一步的事件,从而无法识别手势。通常解决这个问题的方法是什么?

1
为什么需要onTouch和onClick?只使用onTouch不就足够了吗? - JDx
如何看待GestureDetector - keyser
@JDx 我应该如何从 onTouchListener 中识别出一个点击事件?点击是至少两个触摸事件的序列,其中 ACTION_DOWN 事件在非常相同的位置(或者至少在非常相同的视图上)被触发,后续的 ACTION_UP 事件也是如此。当然,我们可以实现这一点,如果没有其他解决方案,我会这样做。但我的问题确实是,是否有内置的解决方案来处理这种情况? - theV0ID
@Keyser,我在OnGestureListener中没有看到任何类似于onClick的回调函数。 - theV0ID
@theV0ID 不,它与触摸一起使用。 - keyser
1
请检查这个链接,它适用于我的解决方案:https://dev59.com/N2Ik5IYBdhLWcg3wJ7P4 - user3391170
7个回答

8

在您的GestureDetector中,您可以直接调用callOnClick()方法。请注意,View.callOnClick API需要API级别15及以上。试试看吧。

 // Create a Gesturedetector
GestureDetector mGestureDetector = new GestureDetector(context, new MyGestureDetector());

// Add a OnTouchListener into view
m_myViewer.setOnTouchListener(new OnTouchListener()
{

    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        return mGestureDetector.onTouchEvent(event);
    }
});

private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener
{
    public boolean onSingleTapUp(MotionEvent e) {
        // ---Call it directly---
        callOnClick();
        return false;
    }

    public void onLongPress(MotionEvent e) {
    }

    public boolean onDoubleTap(MotionEvent e) {
        return false;
    }

    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;

    }

    public void onShowPress(MotionEvent e) {
        LogUtil.d(TAG, "onShowPress");
    }

    public boolean onDown(MotionEvent e) {            
        // Must return true to get matching events for this down event.
        return true;
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, float distanceY) {
        return super.onScroll(e1, e2, distanceX, distanceY);
    }        

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        // do something
        return super.onFling(e1, e2, velocityX, velocityY);
    }
}

“callOnClick” 方法定义在哪里?我在 API 中找不到它,也没有在 “SimpleOnGestureListener” 或 “GestureDetector” 的 API 中找到。 - theV0ID
View.callOnClick API需要API 15级。您可以在http://developer.android.com/intl/zh-CN/reference/android/view/View.html#callOnClick()找到它。 SimpleOnGestureListener扩展自GestureDetector.OnGestureListener,您可以在http://developer.android.com/intl/zh-CN/reference/android/view/GestureDetector.SimpleOnGestureListener.html找到它。 - Autobots

6

如果您使用onTouchListener,则不必使用onClickListener。在onClickListener中所做的是获取触摸事件并检查事件行动以检测点击。因此,如果您想在onClick时执行一些工作,则可以通过过滤操作在onTouchListener中执行。

public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
    //this is touch

}
if (event.getAction() == MotionEvent.ACTION_UP) {
    //this is click

}
return false;
}

在MotionEvent.ACTION_UP块内明确触发onClick事件是否可能? - theV0ID
1
那么,如果我的视图实际上是一个“ViewGroup”,并且其中一些子视图仍然需要处理“onClick”事件呢? - theV0ID
是的,可以通过调用view.performClick()来实现theV0ID。 - Ayaz Alifov
1
MotionEvent.ACTION_UP 不是一个点击事件。它只是表示手指从屏幕上移开。即使手指在移动时被移开,它也会被调用。 - Prabs

4
@Override
    public boolean onTouch(View v, MotionEvent event) {


        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                layOutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
                layOutParams.y = initialY + (int) (event.getRawY() - initialTouchY);

                break;
            case MotionEvent.ACTION_DOWN:
                initialX = layOutParams.x;
                initialY = layOutParams.y;
                initialTouchX = event.getRawX();
                initialTouchY = event.getRawY();
                if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
                    return false;// to handle Click
                }
                break;
            case MotionEvent.ACTION_UP:
                if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
                    return false;// to handle Click
                }
                break;

        }
        return true;

    }
};

1
我们可以使用基于标志的实现。在滑动/拖动时,我们可以在Action_Up中处理并将标志设置为true,然后返回相同的值。在点击或轻触的情况下,在Action_Down中处理相同的操作,并将标志设置为false,然后返回。
touchAndSwipe=false;
switch(e.getAction()){
    case MotionEvent.ACTION_MOVE:
        break;
    case MotionEvent.ACTION_DOWN:
        x1 = e.getX();
        break;
    case MotionEvent.ACTION_UP:
        x2 = e.getX();
        float diff = x2 - x1;
        if(Math.abs(delta) > 100) {
            // Swipe
            touchAndSwipe = true;
        } else {
            touchAndSwipe = false;
        }
        break;
}
return touchAndSwipe;

0
isMove = false;
case MotionEvent.ACTION_DOWN:
//Your stuff
isMove = false;
case MotionEvent.ACTION_UP:
if (!isMove || (Xdiff < 10 && Ydiff < 10 ) {
view.performClick; //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little
even when you just click it   
}
case MotionEvent.ACTION_MOVE:
isMove = true;

另一种方法是使用线程。在 Action_Down 上启动一个线程来增加计数器。在 Action_UP 的情况下:停止/中断线程。查找计数器是否小于2(根据您的应用程序设置阈值),或者!isMove,然后调用点击函数。


0

我已经成功地在我正在构建的自定义键盘中实现了OnClick和OnTouch。您可以查看此代码并根据您的上下文进行修改,因为您没有包含可供使用的代码示例。

如果您发布您的代码示例,我可以修改我的示例以实现相同的结果以供您使用。当涉及到Android开发时,上下文是一切。请查看此代码片段,看看它是否有所帮助。否则,请发布您自己的示例,我将进行修改并回复。

        View DefaultphaseLay() {

        LinearLayout mViewFirstPhase = (LinearLayout) getLayoutInflater().inflate(R.layout.layout_default_phase, parent);

        ksOne_btn_LiLay = (LinearLayout) mViewFirstPhase.findViewById(R.id.ksOne_btn_LiLay);
        ksOne_btn = (Button) mViewFirstPhase.findViewById(R.id.ksOne_btn);
        ksOne_btn.setOnClickListener(mCorkyListener);
        ksOne_btn.setFocusableInTouchMode(true);
        ksOne_btn.setFocusable(true);
        ksOne_btn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    playClick(keyCode);
                    //v.startAnimation(animScale);
                    //key_sound.start();
                    xkeys_lay.setVisibility(View.GONE);
                    numkeys_lay.setVisibility(View.GONE);
                    if (Constants.HapticFeedbackConstant) {
                        myVib.vibrate(Constants.vibration_duration);
                    }
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    ksOne_btn.setFocusable(false); //Check This Added This In Attempt to Kill Selector on Action_Up
                    //playClick(-100);
                    //key_sound.pause();
                }
                return false;
            }
        });

        ChangeKeyBackgroundMehods.initChange_Key_Background(ksOne_btn_LiLay);
        ChangeFontStyle.initChange_fontStyle(ksOne_btn, getApplicationContext());
        ChangeFont_size.initChange_fontSize(ksOne_btn, getApplicationContext());

这适用于 alpha 类型或数字输出的上下文,如果事件希望发送或消耗其他上下文,则需要进行修改。


以下是另一个使用switch/case和if/else if的示例,但如果与字母数字一起使用,必须确保管理按下状态和Shift状态,否则您将在EditText字段中获得双字符输出。 - Justin Kalis

-1

我同意jdx的观点,如果你使用onTouchlistener,就不需要使用onclicklistener,反之亦然。如果你想在按钮、图像或文本视图上触发事件,那么只需触发onClicklistener即可。如果你想要一些拖动和旋转等动画效果,那么请使用ontouchlistener来获取所触摸表面的坐标。


正如已经指出的那样:如果我的视图实际上是一个ViewGroup,并且其中一些子视图仍然需要处理onClick事件,该怎么办? - theV0ID
有时候你需要同时拥有一个 onclick 和 ontouch 监听器。在 ACTION_UP 中从 ontouch 内部生成一个 click 事件并不是同一件事情。 - Johann

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