【问题标题】:OnDragListener not receiving callbacks after dragged fastlyOnDragListener 快速拖动后未收到回调
【发布时间】:2018-08-20 00:30:10
【问题描述】:

首先,这个问题可能看起来像this question的重复,看起来像记录为问题here,但仔细研究后你会发现它完全不同。

设置

我正在尝试实现一个简单的拖放功能:当我将 ImageView A 拖到 ImageView B 时,它们将交换背景颜色。

你可以下载这个小测试项目here。下面描述的问题可以通过克隆它并在您的手机上构建来重现。

如果你只想看代码,看MainActivity

问题

如果您缓慢地执行拖放操作(例如,每次拖放 0.5 秒),它会完美运行。

但是,如果您尝试以您禁食的速度进行拖放(例如每秒 4 到 5 次,无论如何是您的最快速度),很快您会发现无法再执行拖放操作。通过查看日志,我发现没有触发任何拖动回调

03-12 10:41:53.782 (...) I/dragEvent: view.startDragAndDrop has been called
03-12 10:41:53.787 (...) I/dragEvent: ACTION_DRAG_STARTED received
03-12 10:41:53.787 (...) I/dragEvent: ACTION_DRAG_STARTED received
03-12 10:41:53.789 (...) I/dragEvent: ACTION_DRAG_ENTERED received
03-12 10:41:53.789 (...) I/dragEvent: ACTION_DRAG_LOCATION received
03-12 10:41:53.796 (...) I/dragEvent: ACTION_DRAG_LOCATION received
03-12 10:41:53.802 (...) I/dragEvent: ACTION_DROP received
03-12 10:41:53.815 (...) I/dragEvent: ACTION_DRAG_ENDED received
03-12 10:41:53.815 (...) I/dragEvent: ACTION_DRAG_ENDED received
03-12 10:41:54.014 (...) I/dragEvent: view.startDragAndDrop has been called //Since this call, no callbacks were received

此时,应用程序不会冻结。 OnTouchListener 仍在接收回调。
拖放冻结,因为我有一个标志来指示拖放是否正在进行,并且由于没有触发 ACTION_DRAG_ENDED,因此该标志卡在true,因此无法执行更多的拖放操作。

我真的不知道为什么,因为我已经记录了所有内容,每一行代码都运行正常,我希望收到来自 Android 框架的回调,但它不是......

为什么我认为它应该是可以解决的

其实这个小测试项目是我尝试克隆this应用中实现的拖放图像交换。而且我无法在他们的应用程序中重现这个问题......所以我认为我的代码一定有问题;或其他解决方法。

【问题讨论】:

    标签: android drag-and-drop touch drag


    【解决方案1】:

    您的快速拖动导致您没有处理的甩动,并且拖放工具没有通过正式结束拖动来通知您它没有继续进行。以下更新后的MainActivity.java有以下变化:

    1. 删除了很多处理与拖动无关的代码。我这样做只是为了简化此处的发布,没有其他原因。
    2. 引入GestureDetectorCompat 以检测何时发生投掷。我对此不做任何事情,只是记录它发生了。您可能需要考虑在代码中使用手势检测器来进行触摸。
    3. 检查ACTION_UP 事件以在拖动未完成时重置布局 (isDragging == true)。

    当拖拽开始时,flings 似乎有一个混乱的结局。下面的代码可以帮助您解决问题。

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        ImageView ivGrid1, ivGrid2, ivGrid3, ivGrid4;
        private GestureDetectorCompat mDetector;
    
        //Drag and Drop
        View dragOriginView;
        Drawable whiteDrawable;
        Drawable tempDrawableStorage;
        boolean isDragging = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    
            ivGrid1 = findViewById(R.id.grid_1);
            ivGrid2 = findViewById(R.id.grid_2);
            ivGrid3 = findViewById(R.id.grid_3);
            ivGrid4 = findViewById(R.id.grid_4);
    
            setupTouchListeners();
            setupDragListeners();
    
            whiteDrawable = new ColorDrawable(ContextCompat.getColor(this, android.R.color.white));
        }
    
        private void setupTouchListeners() {
            View.OnTouchListener listener = new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int action = event.getActionMasked();
    
                    switch (action) {
                        case MotionEvent.ACTION_DOWN:
                            return true;
    
                        case MotionEvent.ACTION_MOVE:
                            boolean beyondLeft = event.getX() <= 0;
                            boolean beyondRight = event.getX() >= v.getWidth();
                            boolean beyondTop = event.getY() <= 0;
                            boolean beyondBottom = event.getY() >= v.getHeight();
                            if (beyondLeft || beyondRight || beyondTop || beyondBottom) {
                                startDragAndDrop(v);
                                return true;
                            }
                            isDragging = false;
                            break;
    
                        case MotionEvent.ACTION_UP:
                            if (isDragging) {
                                dragOriginView.setBackground(tempDrawableStorage);
                                tempDrawableStorage = null;
                                v.setAlpha(1.0f);
                                isDragging = false;
                            }
                    }
                    return false;
                }
            };
            ivGrid1.setOnTouchListener(listener);
            ivGrid2.setOnTouchListener(listener);
            ivGrid3.setOnTouchListener(listener);
            ivGrid4.setOnTouchListener(listener);
        }
    
        private void startDragAndDrop(View view) {
            if (isDragging) return;
            isDragging = true;
            dragOriginView = view;
            view.setAlpha(0.5f);
            ClipData.Item item = new ClipData.Item((String) view.getTag());
            ClipData dragData = new ClipData((String) view.getTag(), new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN},
                                             item);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                view.startDragAndDrop(dragData, new View.DragShadowBuilder(view), null, 0);
            } else {
                view.startDrag(dragData, new View.DragShadowBuilder(view), null, 0);
            }
            Log.i("dragEvent", "view.startDragAndDrop has been called");
            tempDrawableStorage = view.getBackground();
            view.setBackground(whiteDrawable);
        }
    
        private void setupDragListeners() {
            View.OnDragListener listener = new View.OnDragListener() {
                @Override
                public boolean onDrag(View v, DragEvent event) {
                    Log.i("dragEvent", "<<<<<<<<<<<<<<called");
                    switch (event.getAction()) {
                        case ACTION_DRAG_STARTED:
                            Log.i("dragEvent", "ACTION_DRAG_STARTED received");
                            return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
                        case ACTION_DRAG_ENTERED:
                            Log.i("dragEvent", "ACTION_DRAG_ENTERED received");
                            v.setAlpha(0.5f);
                            return true;
                        case ACTION_DRAG_LOCATION:
                            Log.i("dragEvent", "ACTION_DRAG_LOCATION received");
                            return true;
                        case ACTION_DRAG_EXITED:
                            Log.i("dragEvent", "ACTION_DRAG_EXITED received");
                            v.setAlpha(1f);
                            return true;
                        case ACTION_DROP:
                            Log.i("dragEvent", "ACTION_DROP received");
                            v.setAlpha(1f);
                            swapColor(v);
                            return true;
                        case ACTION_DRAG_ENDED:
                            Log.i("dragEvent", "ACTION_DRAG_ENDED received");
                            if (!event.getResult() && v == dragOriginView) {
                                dragOriginView.setBackground(tempDrawableStorage);
                                tempDrawableStorage = null;
                            }
                            v.setAlpha(1f);
                            isDragging = false;
                        default:
                            //Do nothing
                    }
                    return false;
                }
            };
            ivGrid1.setOnDragListener(listener);
            ivGrid2.setOnDragListener(listener);
            ivGrid3.setOnDragListener(listener);
            ivGrid4.setOnDragListener(listener);
        }
    
        private void swapColor(View dragTarget) {
            if (dragOriginView == dragTarget) {
                //Restore color
                dragOriginView.setBackground(tempDrawableStorage);
                tempDrawableStorage = null;
                return;
            }
            Drawable targetBackground = dragTarget.getBackground();
            dragOriginView.setBackground(targetBackground);
            dragTarget.setBackground(tempDrawableStorage);
            tempDrawableStorage = null;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            this.mDetector.onTouchEvent(event);
            return super.onTouchEvent(event);
        }
    
        class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
            private static final String DEBUG_TAG = "Gestures";
    
            @Override
            public boolean onDown(MotionEvent event) {
                Log.d(DEBUG_TAG, "onDown: " + event.toString());
                return true;
            }
    
            @Override
            public boolean onFling(MotionEvent event1, MotionEvent event2,
                                   float velocityX, float velocityY) {
                Log.d(DEBUG_TAG, "onFling: " + ((event1 != null) ? event1.toString() : "" + event2.toString()));
                return true;
            }
        }
    }
    

    【讨论】:

    • //导致您没有处理的投掷,并且拖放设施没有通过正式结束拖动来通知您它没有继续//
    【解决方案2】:

    看看下面的sn-p

    private void setupTouchListeners () {
        View.OnTouchListener listener = new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                    return true;
                }
                if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
                    boolean beyondLeft = event.getX() <= 0;
                    boolean beyondRight = event.getX() >= v.getWidth();
                    boolean beyondTop = event.getY() <= 0;
                    boolean beyondBottom = event.getY() >= v.getHeight();
                    if (beyondLeft || beyondRight || beyondTop || beyondBottom) {
                        startDragAndDrop(v);
                        return true;
                    } else {
                        // done changes here, simply changed flag to false and return false
                        isDragging=false;
                        return false;
                    }
                }
                return false;
            }
        };
        ivGrid1.setOnTouchListener(listener);
        ivGrid2.setOnTouchListener(listener);
        ivGrid3.setOnTouchListener(listener);
        ivGrid4.setOnTouchListener(listener);
    }
    

    通过进行上述更改,您将能够以您想要的任何速度拖动。

    编码愉快:)

    【讨论】:

    • 感谢您的回答!通过上述更改,基本上重新激活拖放,防止拖放冻结。但是,没有收到回调的根本问题仍然存在。我会赞成你的回答,因为它确实有帮助;但我还不会接受;看看是否有任何答案可以解决“未收到回调”的问题。再次感谢!
    猜你喜欢
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多