如何在Android中检测Dragable视图的外部拖放

如何在Android中检测Dragable视图的外部拖放,android,drag-and-drop,Android,Drag And Drop,我正在开发一个Android应用程序,我正在使用视图的拖放功能 我在我的视图(截图中的圆圈)上实现了onLongClick(实际上使用的是具有1.5秒长点击阈值的onTouch)。当检测到长时间单击时,我会显示两个放置区域(分别连接onDragListener的ImageView,屏幕截图中的绿色框) 当我的视图被拖放到任意一个拖放区域(调用ACTION\u drop)时,我的代码工作正常,但问题是,我还需要能够知道我的圆何时被拖放到拖放区域之外,以便重置状态。在这种情况下,我不会从两个drop

我正在开发一个Android应用程序,我正在使用视图的拖放功能

我在我的视图(截图中的圆圈)上实现了onLongClick(实际上使用的是具有1.5秒长点击阈值的onTouch)。当检测到长时间单击时,我会显示两个放置区域(分别连接onDragListener的ImageView,屏幕截图中的绿色框)

当我的视图被拖放到任意一个拖放区域(调用ACTION\u drop)时,我的代码工作正常,但问题是,我还需要能够知道我的圆何时被拖放到拖放区域之外,以便重置状态。在这种情况下,我不会从两个drop区域中的任何一个触发任何回调。我也不认为从我使用startDrag方法的视图中可以调用任何方法

在任何放置区域之外放置视图后,我从logcat获得的唯一信息是:

I/ViewRootImpl﹕ 报告丢弃结果:false

有没有办法从我的代码中检测到“drop result:false”?谢谢你的帮助

截图链接如下:

----------更新11/21----------

在我第一次发布这个问题的时候,我仍然试图掌握高级概念,但现在我意识到我应该包括更多细节

下面是一些目前在我的应用程序中使用的代码片段。顺便说一句,我在服务中使用这些视图——圆圈(锚定)和绿色框(dropRegions)——而不是在活动中,所以我使用WindowManager添加视图

我仍然有同样的问题,我不明白的是,ACTION\u DRAG\u ENTERED和ACTION\u DRAG\u Exit从未被调用。到目前为止,唯一的行动对我有效。我还缺什么吗

下面是代码的相关部分

长时间单击圆圈时调用(在Sandstar的响应后稍加修改):

private void主播长按(视图){
Log.d(“长点击”);
showAnchorDropRegion();
ClipData.Item=new ClipData.Item((String)view.getTag());
ClipData data=new ClipData((String)view.getTag(),
新字符串[]{clipddescription.MIMETYPE_TEXT_PLAIN},项);
View.DragShadowBuilder shadowBuilder=新视图.DragShadowBuilder(视图);
view.startDrag(数据,shadowBuilder,null,0);
setupAnchorDropRegion();
showAnchorDropRegion();
}
将拖动侦听器附着到绿色框:

/*设置可以拖放锚点的区域*/
私有void setupAnchorDropRegion(){
ivDropRight=新图像视图(此);
setImageDrawable(getResources().getDrawable(R.drawable.dropregion_normal));
ivDropRight.setOnDragListener(新视图.OnDragListener(){
@凌驾
公共布尔onDrag(视图v,DrageEvent事件){
Log.d(“#####,,“onDrag”);
开关(event.getAction()){
案例DrageEvent.ACTION\u DRAG\u已启动:
//无所事事
返回true;
案例DrageEvent.ACTION\u DRAG\u输入:
Log.d(“#####,,“拖动输入”);
打破
案例DrageEvent.ACTION\u DRAG\u退出:
Log.d(“#####,,“DRAG#u EXITED”);
打破
案例DrageEvent.ACTION_DROP:
Log.d(“#####”,DROP ON:“+event.getX()+”,“+event.getY());
隐藏区();
//将锚点位置初始化到右侧
打破
案例DrageEvent.ACTION\u DRAG\u已结束:
Log.d(“#####”,“拖动结束”);
打破
违约:
打破
}
返回true;
}
});
//对ivDropLeft重复上述步骤
显示绿色框(放置区域):


主要思想来自以下文件:

返回拖放操作结果的指示。 此方法仅在操作类型为时返回有效数据 操作\u拖动\u已结束。返回值取决于 用户释放拖动阴影

因此,删除后,您可以通过
getResult()
获得结果:如果它返回
true
,则它已被删除到接受删除的某个视图中。请注意,接受删除的视图返回
true
操作\u-drop

视图应该从其onDragEvent(DrageEvent)处理程序或 OnDragListener.onDrag()侦听器,如果它接受删除,则为false 它忽略了下降

下面是一些小示例,类似于您的布局,显示了该方法:

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    private View mCircleView = null;
    private Boolean mDroppedIn = null;

    private View.OnDragListener mLeftDragListener = new View.OnDragListener() {
        private boolean mInView = false;

        @Override
        public boolean onDrag(final View v, final DragEvent event) {
            final int action = event.getAction();

            switch (action) {
                case DragEvent.ACTION_DRAG_STARTED:
                    mDroppedIn = null;
                    mCircleView.setVisibility(View.INVISIBLE);
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENTERED:
                    mInView = true;
                    v.setBackgroundResource(android.R.color.holo_green_light);
                    break;

                case DragEvent.ACTION_DRAG_EXITED:
                    mInView = false;
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENDED:
                    mCircleView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (mCircleView.getVisibility() != View.VISIBLE) {
                                mCircleView.setVisibility(View.VISIBLE);
                            }
                        }
                    });
                    reportResult(R.id.leftView, event.getResult());
                    break;

                case DragEvent.ACTION_DROP:
                    return mInView;
            }

            return true;
        }
    };

    private View.OnDragListener mRightDragListener = new View.OnDragListener() {
        private boolean mInView = false;

        @Override
        public boolean onDrag(final View v, final DragEvent event) {
            final int action = event.getAction();

            switch (action) {
                case DragEvent.ACTION_DRAG_STARTED:
                    mDroppedIn = null;
                    mCircleView.setVisibility(View.INVISIBLE);
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENTERED:
                    mInView = true;
                    v.setBackgroundResource(android.R.color.holo_green_light);
                    break;

                case DragEvent.ACTION_DRAG_EXITED:
                    mInView = false;
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENDED:
                    mCircleView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (mCircleView.getVisibility() != View.VISIBLE) {
                                mCircleView.setVisibility(View.VISIBLE);
                            }
                        }
                    });
                    reportResult(R.id.rightView, event.getResult());
                    break;

                case DragEvent.ACTION_DROP:
                    return mInView;
            }

            return true;
        }
    };

    private void reportResult(final int viewId, final boolean result) {
        if (mDroppedIn == null) {
            mDroppedIn = result;
            Toast.makeText(this, "Dropped in: " + result, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCircleView = findViewById(R.id.circleView);

        mCircleView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(final View v) {
                final ClipData.Item item = new ClipData.Item((String)v.getTag());
                final ClipData dragData = new ClipData((String)v.getTag(),
                        new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN}, item);

                mCircleView.startDrag(dragData, new CircleShadowBuilder(v), null, 0);
                findViewById(R.id.leftView).setOnDragListener(mLeftDragListener);
                findViewById(R.id.rightView).setOnDragListener(mRightDragListener);
                return true;
            }
        });
    }

    private class CircleShadowBuilder extends View.DragShadowBuilder {

        private final Drawable mShadow;

        CircleShadowBuilder(final View v) {
            super(v);
            mShadow = v.getResources().getDrawable(R.drawable.circle);
        }

        @Override
        public void onProvideShadowMetrics(final Point shadowSize, final Point shadowTouchPoint) {
            int width = getView().getWidth();
            int height = getView().getHeight();

            mShadow.setBounds(0, 0, width, height);
            shadowSize.set(width, height);
            shadowTouchPoint.set(width, height);
        }

        @Override
        public void onDrawShadow(final Canvas canvas) {
            mShadow.draw(canvas);
        }
    }
}

我需要解决的另一个问题是跟踪拖动侦听器之间的空白区域中的拖动,以防用户没有将鼠标放在侦听器上,这样我就可以像橡皮筋一样将拖动的项设置回其开始位置


DrageEvent.ACTION\u DRAG\u位置不会触发,除非它位于侦听器上。因此,我在FrameLayout中添加了一个拖动侦听器,这只是另一个视图。现在,当用户拖动时,我会获取位置更新,存储这些更新,当用户拖放时,我会检查FrameLayout是否捕获了拖放。这解决了您的问题,并且我有了拖放的位置,无论是不是在一个被通缉的监听者身上,我用橡皮筋把原来的项目拖回到它的开头。

我希望你已经找到了解决方案

我也遇到了同样的问题。我试图在所有可拖动视图之外检测到一个下降

通过查看拖放过程部分中的,我发现无论拖放发生在何处,在拖放完成时都会调用操作\u drag\u ENDED

“在用户释放拖曳阴影后,以及在系统发出(如有必要)带有动作类型action\u DROP的拖曳事件后,系统发出带有动作类型action\u drag\u ENDED的拖曳事件,以指示拖曳操作已结束。无论用户在何处释放拖曳阴影,都会完成此操作。ev
public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    private View mCircleView = null;
    private Boolean mDroppedIn = null;

    private View.OnDragListener mLeftDragListener = new View.OnDragListener() {
        private boolean mInView = false;

        @Override
        public boolean onDrag(final View v, final DragEvent event) {
            final int action = event.getAction();

            switch (action) {
                case DragEvent.ACTION_DRAG_STARTED:
                    mDroppedIn = null;
                    mCircleView.setVisibility(View.INVISIBLE);
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENTERED:
                    mInView = true;
                    v.setBackgroundResource(android.R.color.holo_green_light);
                    break;

                case DragEvent.ACTION_DRAG_EXITED:
                    mInView = false;
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENDED:
                    mCircleView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (mCircleView.getVisibility() != View.VISIBLE) {
                                mCircleView.setVisibility(View.VISIBLE);
                            }
                        }
                    });
                    reportResult(R.id.leftView, event.getResult());
                    break;

                case DragEvent.ACTION_DROP:
                    return mInView;
            }

            return true;
        }
    };

    private View.OnDragListener mRightDragListener = new View.OnDragListener() {
        private boolean mInView = false;

        @Override
        public boolean onDrag(final View v, final DragEvent event) {
            final int action = event.getAction();

            switch (action) {
                case DragEvent.ACTION_DRAG_STARTED:
                    mDroppedIn = null;
                    mCircleView.setVisibility(View.INVISIBLE);
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENTERED:
                    mInView = true;
                    v.setBackgroundResource(android.R.color.holo_green_light);
                    break;

                case DragEvent.ACTION_DRAG_EXITED:
                    mInView = false;
                    v.setBackgroundResource(android.R.color.holo_green_dark);
                    break;

                case DragEvent.ACTION_DRAG_ENDED:
                    mCircleView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (mCircleView.getVisibility() != View.VISIBLE) {
                                mCircleView.setVisibility(View.VISIBLE);
                            }
                        }
                    });
                    reportResult(R.id.rightView, event.getResult());
                    break;

                case DragEvent.ACTION_DROP:
                    return mInView;
            }

            return true;
        }
    };

    private void reportResult(final int viewId, final boolean result) {
        if (mDroppedIn == null) {
            mDroppedIn = result;
            Toast.makeText(this, "Dropped in: " + result, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCircleView = findViewById(R.id.circleView);

        mCircleView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(final View v) {
                final ClipData.Item item = new ClipData.Item((String)v.getTag());
                final ClipData dragData = new ClipData((String)v.getTag(),
                        new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN}, item);

                mCircleView.startDrag(dragData, new CircleShadowBuilder(v), null, 0);
                findViewById(R.id.leftView).setOnDragListener(mLeftDragListener);
                findViewById(R.id.rightView).setOnDragListener(mRightDragListener);
                return true;
            }
        });
    }

    private class CircleShadowBuilder extends View.DragShadowBuilder {

        private final Drawable mShadow;

        CircleShadowBuilder(final View v) {
            super(v);
            mShadow = v.getResources().getDrawable(R.drawable.circle);
        }

        @Override
        public void onProvideShadowMetrics(final Point shadowSize, final Point shadowTouchPoint) {
            int width = getView().getWidth();
            int height = getView().getHeight();

            mShadow.setBounds(0, 0, width, height);
            shadowSize.set(width, height);
            shadowTouchPoint.set(width, height);
        }

        @Override
        public void onDrawShadow(final Canvas canvas) {
            mShadow.draw(canvas);
        }
    }
}
package com.xengar.android.handleimage.ui;

import android.content.ClipData;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;

import com.xengar.android.handleimage.R;

/**
 * DragAndDropActivity
 */
public class DragAndDropActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drag_and_drop);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        /*
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });*/

        findViewById(R.id.myimage1).setOnTouchListener(new MyTouchListener());
        findViewById(R.id.myimage2).setOnTouchListener(new MyTouchListener());
        findViewById(R.id.myimage3).setOnTouchListener(new MyTouchListener());
        findViewById(R.id.myimage4).setOnTouchListener(new MyTouchListener());
        findViewById(R.id.topleft).setOnDragListener(new MyDragListener());
        findViewById(R.id.topright).setOnDragListener(new MyDragListener());
        findViewById(R.id.bottomleft).setOnDragListener(new MyDragListener());
        findViewById(R.id.bottomright).setOnDragListener(new MyDragListener());

    }

    private final class MyTouchListener implements OnTouchListener {
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                ClipData data = ClipData.newPlainText("", "");
                DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
                        view);
                view.startDrag(data, shadowBuilder, view, 0);
                view.setVisibility(View.INVISIBLE);
                return true;
            } else {
                return false;
            }
        }
    }

    class MyDragListener implements OnDragListener {
        Drawable enterShape = getResources().getDrawable(
                R.drawable.shape_droptarget);
        Drawable normalShape = getResources().getDrawable(R.drawable.shape);

        @Override
        public boolean onDrag(View v, DragEvent event) {
            View view;
            int action = event.getAction();
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                    // do nothing
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                    v.setBackgroundDrawable(enterShape);
                    break;
                case DragEvent.ACTION_DRAG_EXITED:
                    v.setBackgroundDrawable(normalShape);
                    break;
                case DragEvent.ACTION_DROP:
                    // Dropped, reassign View to ViewGroup
                    view = (View) event.getLocalState();
                    ViewGroup owner = (ViewGroup) view.getParent();
                    owner.removeView(view);
                    LinearLayout container = (LinearLayout) v;
                    container.addView(view);
                    //view.setVisibility(View.VISIBLE);
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                    v.setBackgroundDrawable(normalShape);
                    // Regardless of valid or invalid drop show it again.
                    // See https://developer.android.com/reference/android/view/DragEvent.html#ACTION_DRAG_ENDED
                    view = (View) event.getLocalState();
                    view.setVisibility(View.VISIBLE);
                default:
                    break;
            }
            return true;
        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.xengar.android.handleimage.ui.DragAndDropActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <!--<include layout="@layout/content_drag_and_drop" /> -->

    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.xengar.android.handleimage.ui.DragAndDropActivity"
        tools:showIn="@layout/activity_drag_and_drop">

        <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:columnCount="2"
            android:columnWidth="320dp"
            android:orientation="vertical"
            android:rowCount="2"
            android:stretchMode="columnWidth" >

            <LinearLayout
                android:id="@+id/topleft"
                android:layout_width="160dp"
                android:layout_height="160dp"
                android:layout_row="0"
                android:background="@drawable/shape" >

                <ImageView
                    android:id="@+id/myimage1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/gallery_lion" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/topright"
                android:layout_width="160dp"
                android:layout_height="160dp"
                android:background="@drawable/shape" >

                <ImageView
                    android:id="@+id/myimage2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/gallery_lion" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/bottomleft"
                android:layout_width="160dp"
                android:layout_height="160dp"
                android:background="@drawable/shape" >

                <ImageView
                    android:id="@+id/myimage3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/gallery_lion" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/bottomright"
                android:layout_width="160dp"
                android:layout_height="160dp"
                android:background="@drawable/shape" >

                <ImageView
                    android:id="@+id/myimage4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/gallery_lion" />
            </LinearLayout>

        </GridLayout>


    </android.support.constraint.ConstraintLayout>

    <!--
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" /> -->

</android.support.design.widget.CoordinatorLayout>