OnTouch MotionEvent 动作移动未触发

Posted

技术标签:

【中文标题】OnTouch MotionEvent 动作移动未触发【英文标题】:OnTouch MotionEvent Action Move not triggering 【发布时间】:2015-12-16 12:59:09 【问题描述】:

我试图在拖动按钮时获取 X、Y 坐标,但由于某种原因,触摸侦听器上附加的代码仅触发两次,而不是在拖动时不断更新,即使有 trothing 我也会感到满意为了提高性能,但正如我所说,onTouch 方法仅在动作停止时触发,然后再触发一次。

这是我的代码:

final Button dragBtn = (Button) findViewById(R.id.MyTestButton);

    dragBtn.setOnTouchListener(new View.OnTouchListener() 

    @Override
    public boolean onTouch(View v, MotionEvent me) 
        // TODO Auto-generated method stub
        ClipData data = ClipData.newPlainText("", "");
        View.DragShadowBuilder shadow = new View.DragShadowBuilder(dragBtn);
        v.startDrag(data, shadow, null, 0);
        int action = me.getAction();


        if (action==MotionEvent.ACTION_DOWN) 

            Log.i("TEST", "ACTION_DOWN");
            return true;
        


        if (action==MotionEvent.ACTION_MOVE)
            Log.i("TEST", "ACTION_MOVE");

            float x = me.getX();
            float y = me.getY();

            //TODO: Rest of my code here.

            return true;
        

        if (action==MotionEvent.ACTION_UP)

            Log.i("TEST", "ACTION_UP");
            return true;
        

        Log.i("TEST", "UKNOWN ACTION");
        return true;
    
);

在日志中我刚刚:

ACTION_DOWN
WindowManager﹕ Drag already in progress
UKNOWN ACTION

【问题讨论】:

【参考方案1】:

您想在OnDragListener 而不是OnTouchListener 中捕获 X 和 Y 坐标数据。在您在屏幕上拖动对象的整个过程中,android 将其解释为一个单独的 ACTION_MOVE 事件,因此您只能从中获取一次数据。将以下代码放入您的自定义 OnDragListener 类中:

@Override
public boolean onDrag(View v, DragEvent event) 
    int action = event.getAction();
    float xCoord;
    float yCoord;
    if(action == DragEvent.ACTION_DRAG_LOCATION) 
        xCoord = event.getX();
        yCoord = event.getY();
    
    return event.getResult();       //returns true for valid drop or false for invalid drop

在您的自定义 OnDragListener 类中的其他位置,您将不得不对 xCoord 和 yCoord 数据进行处理。例如,在我的应用程序中,我在应用程序的“调试”版本中有一个 TextView,我在其中显示 xCoord 和 yCoord 数据。在我的if(action == DragEvent.ACTION_DRAG_LOCATION)... 部分中获得这两个值后,我立即执行TextView.setText() 方法。

希望这会有所帮助。

【讨论】:

我也试过了,但问题是事件随机触发有时像 3 或 4 次,有时是 6、7 和停止,即使我继续拖动对象。但是我没有尝试使用 ACTION_DRAG_LOCATION 我只是尝试使用 DRAG_ENTER。我会尽快尝试。 @ShP 我建议只将event.getX()event.getY() 放在 ACTION_DRAG_LOCATION 中,这样您就不会得到重复的数据。您应该知道,即使您开始拖动对象然后保持手指不动(在放下对象之前),Android 可能仍会因为与仅触摸屏幕相关的触摸倾斜而记录一些移动。您可以通过截断 event.getX()event.getY() 返回的浮点值(或将它们转换为整数)来减少一些“噪音”。 嗯,看起来你的代码有效,但是,并没有解决我的问题。问题是,理论上这应该起作用,但是,它不起作用,因为经过几次动作后,我开始得到“后台部分并发标记扫描 GC 释放”,这是可笑的,我的线程被丢弃了。有什么解决办法吗?我用你上面的代码明白了。 不幸的是,我没有遇到您刚才描述的那个问题,所以我不完全确定如何提供帮助。对不起。【参考方案2】:

MotionEvent.getAction 对动作类型以及额外的指针信息(用于多点触控处理)进行编码。您希望MotionEvent.getActionMasked 只获得动作部分。

编辑:另外,根据下面的讨论,startDragging 存在一些问题。

【讨论】:

嗨@Darrell,但我不明白这会有什么帮助。即使我将运动 getAction 更改为 getActionMasked,也无法解决问题,因为此方法 onTouch 仅触发两次。例如,我更改为记录屏蔽操作,但同样的事情,只有两个日志详细信息,一个值为 0,然后为 3。将上面的代码放在某个活动中并将其附加到某个虚拟按钮,您将看到相同的结果. 知道了,只要您从 onTouch 返回 true,您就应该继续看到事件。尝试在 ACTION_DOWN 子句中移动 startDrag,也许第二次调用“正在拖动”错误导致系统中止拖动? 嘿@Darrell,谢谢!这就是问题所在!您可以编辑您的答案,但无论如何我都会接受。那段该死的代码造成了麻烦,当我删除它时,它按预期触发。但是,如果我将该代码置于操作之下,同样的错误。有什么解决办法吗? 有趣——我以前没有使用过 startDrag,但听起来你可能需要做更多的事情来使用“拖放”框架。见developer.android.com/guide/topics/ui/drag-drop.html【参考方案3】:

一旦系统有了拖动阴影,它就会通过向应用程序中当前可见的所有视图对象发送拖动事件来开始拖放操作。它通过调用 View 对象的拖动侦听器(onDrag() 的实现或通过调用 View 对象的 onDragEvent() 方法来执行此操作。两者都传递了一个具有 ACTION_DRAG_STARTED 的 getAction() 值的 DragEvent 对象

【讨论】:

【参考方案4】:

最后你的代码应该是这样的。

int basex;
int basey;
RelativeLayout.LayoutParams layoutParams;
RelativeLayout layBg;


....................

dragBtn.setOnTouchListener(new View.OnTouchListener() 

    @Override
    public boolean onTouch(View v, MotionEvent me) 
        // TODO Auto-generated method stub


 layoutParams = (RelativeLayout.LayoutParams) dragBtn.getLayoutParams();

        switch (me.getAction()) 
                    case MotionEvent.ACTION_DOWN:

                        basex = ((int) (me.getRawX() - layoutParams.leftMargin));
                        basey = ((int) (me.getRawY() - layoutParams.topMargin));
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int i = (int) event.getRawX();
                        int j = (int) event.getRawY();
                        layBg = ((RelativeLayout) dragBtn.getParent());
                        if ((i - basex > -(dragBtn.getWidth() * 2 / 3))
                                && (i - basex < layBg.getWidth() - dragBtn.getWidth() / 3)) 
                            layoutParams.leftMargin = (i - basex);
                        
                        if ((j - basey > -(dragBtn.getHeight() * 2 / 3))
                                && (j - basey < layBg.getHeight() - dragBtn.getHeight() / 3)) 
                            layoutParams.topMargin = (j - basey);
                        
                        layoutParams.rightMargin = -1000;
                        layoutParams.bottomMargin = -1000;
                        dragBtn.setLayoutParams(layoutParams);
                        break;

                    

                    return true;
    
);

【讨论】:

谢谢@Ravi,我会尽快测试。一个问题是,这会移动整个按钮,还是创建按钮的“副本”,因为我不想从我开始拖动按钮的地方得到空白。 该代码仅用于移动整个按钮,而不是创建副本。 嘿@Ravi,这既不好也不好解决,这只会更新边距并弄乱 UI 的其余部分。我需要这个 View.DragShadowBuilder shadow = new View.DragShadowBuilder(dragBtn); 的工作解决方案 ok NP,它在我这里工作,我不知道要实现 DragShadowBuilder。

以上是关于OnTouch MotionEvent 动作移动未触发的主要内容,如果未能解决你的问题,请参考以下文章

Android : 模拟点击performClick()/模拟长按performLongClick()/模拟onTouch事件

Android : 模拟点击performClick()/模拟长按performLongClick()/模拟onTouch事件

触摸事件MotionEvent

安卓DatePickerDialog使用Butternife的@onTouch 注解

MotionEvent GetY() 和 getX() 返回不正确的值

android中 OnTouch和OnClick有何区别?