如何获取连续的触摸事件?

11

我的类继承自View,我需要在它上面获得持续的触摸事件。

如果我使用:

public boolean onTouchEvent(MotionEvent me) {

    if(me.getAction()==MotionEvent.ACTION_DOWN) {
        myAction();
    }
    return true;
}

触摸事件只会被捕获一次。

如果我需要在不移动手指的情况下获取连续的触摸,该怎么办? 请告诉我我不需要使用线程或计时器。我的应用程序已经太重了。

谢谢。

8个回答

16

使用 if(me.getAction() == MotionEvent.ACTION_MOVE)。在屏幕上不可能完全保持手指静止,因此无论手指移动多少像素,都会调用Action_Move。

你也可以监听me.getAction() == MotionEvent.ACTION_UP - 直到发生这种情况,用户必须仍然将手指放在屏幕上。


2
没错,我更喜欢的解决方案是在 ACTION_DOWN 事件时设置一个标志,并在 ACTION_UP 事件时将其设置为 false,然后一直使用该标志。 - WarrenFaith
我在各处都看到这个提及,但对我来说似乎并非如此。如果我触摸并保持静止,我就不会收到ACTION_MOVE事件。只有当我把手指向左或向右倾斜时,ACTION_MOVE才会触发。这可能是我的手机设置使其变得不那么敏感吗? - clusterflux
1
@clusterflux 如果你依赖 ACTION_MOVE 来持续触发,即使手指保持静止,那么你可能做错了什么。我的(非常古老的!)答案的后半部分,例如在 down 中设置一个标志并在 up 中清除它,才是正确的方法。 - Steve Haley
1
@SteveHaley 感谢您的帮助。我有一个后续问题。我有一个在 ACTION_DOWN 上调用的函数。我希望它在 ACTION_UP 发生之前持续调用。我尝试使用您建议的标志,但我遇到的问题是,我正在使用 while(!up_flag) 循环来重复调用该函数,一旦 ACTION_DOWN 发生。然而,一旦我进入 while() 循环,我就无法再感知到触摸事件,因此 ACTION_UP 永远不会被感知,所以标志没有改变。我陷入了一个连续的 while 循环中。有关替代方法的任何建议吗? - clusterflux
好的,但是...... 在模拟器上不起作用。与手指不同,鼠标可以保持静止。 - Meow Cat 2012

5

您需要为元素设置以下属性: android:focusable="true" android:clickable="true" 否则,只会执行向下动作。


4

以下是一段简单的代码片段,演示如何处理连续的触摸事件。当您触摸设备并保持触摸状态时,移动手指会触发“触摸移动”动作。

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    if(isTsunami){
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // Write your code to perform an action on down
            break;
        case MotionEvent.ACTION_MOVE:
            // Write your code to perform an action on contineus touch move
            break;
        case MotionEvent.ACTION_UP:
            // Write your code to perform an action on touch up
            break;
    }
    }
    return true;
}

1

试试这个。对我来说有效:

public static OnTouchListener loadContainerOnTouchListener() {
    OnTouchListener listener = new OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        LinearLayout layout = (LinearLayout)v;
        for(int i =0; i< layout.getChildCount(); i++)
        {
            View view = layout.getChildAt(i);
            Rect outRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
            if(outRect.contains((int)event.getX(), (int)event.getY()))
            {
                Log.d(this.getClass().getName(), String.format("Over view.id[%d]", view.getId()));
            }
        }

    }

记住:您设置的监听器必须是容器布局(Grid、Relative、Linear)。

LinearLayout layout = findViewById(R.id.yourlayoutid);
layout.setOnTouchListener(HelperClass.loadContainerOnTouchListener());

1
这可能会有所帮助,
requestDisallowInterceptTouchEvent(true);

在父视图上,像这样 -
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            view.getParent().requestDisallowInterceptTouchEvent(true);
            switch(motionEvent.getAction()){
            }

            return false; 

         }

0
你可以使用下面的代码片段作为参考,其中我使用背景来检测屏幕是否被按住。
Main_Layout.setOnTouchListener(new View.OnTouchListener() {
        @SuppressLint("ResourceAsColor")
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    Main_Layout.setBackgroundColor(R.color.green);
                    event.setAction(MotionEvent.ACTION_DOWN);
                    break;
                default:
                    Main_Layout.setBackgroundColor(R.color.blue);
                    break;

            }
            return false;
        }
    });

0

我正在制作一个游戏,使用自定义视图作为拇指控件...这是我所做的

float x = 0, y = 0;

@Override
public boolean onTouchEvent(MotionEvent event) {
    x = event.getX();
    y = event.getY();

    // handle touch events with 
    switch( event.getActionMasked() ) {
        case MotionEvent.ACTION_DOWN :
            if(cont)
            {
                // remove any previous callbacks
                removeCallbacks(contin);

                // post new runnable
                postDelayed(contin, 10);
            }
            invalidate();
            return true;
        case MotionEvent.ACTION_MOVE :
            if(!cont && thumbing != null)
            {
                //  do non-continuous operations here
            }
            invalidate();       
            return true;
        case MotionEvent.ACTION_UP :

            // set runnable condition to false
            x = 0;

            // remove the callbacks to the thread
            removeCallbacks(contin);
            invalidate();
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

public boolean cont = false;

// sets input to continuous
public void set_continuous(boolean b) { cont = b; }


public Runnable contin = new Runnable()
{

    @Override
    public void run() {
        if(x != 0)
        {
            //  do continuous operations here
            postDelayed(this, 10);
        }
    }

};

一个快速的提示,确保在调用此视图的主活动中通过onPause方法手动删除回调。
@Override
protected void onPause() {
    if(left.cont) left.removeCallbacks(left.contin);
    if(right.cont) right.removeCallbacks(left.contin); 
    super.onPause();
}

这样,如果您暂停并回来,触摸事件不会被处理两次,视图也不受其线程开销的影响。

** 在启用硬件加速的三星Galaxy S3上测试通过 **


0

所有这些答案都部分正确,但它们并没有以正确的方式解决问题。

首先,对于那些决定跟踪事件为ACTION_MOVE的人来说,这只有在用户移动手指时才有效。如果您决定实现自定义拇指控件,那么可以使用,但对于普通的自定义按钮来说就不是这样了。

其次,在ACTION_DOWN中使用标志并在ACTION_UP中进行检查似乎是逻辑上正确的方法,但正如Clusterfux发现的那样,如果您实现了一个while(!up_flag)逻辑,您会陷入麻烦中;)

因此,正确的方法在这里提到:

Android中连续的“Action_DOWN”

请记住,如果您要编写的逻辑在持续按下期间必须以某种方式修改UI,则必须在主线程中执行,否则最好使用另一个线程。


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