要实现android长按拖拽控件,需要以下因素:
对于控件view,需要以下监听:

1.setOnDragListener()
2.setOnLongClickListener()

给控件view设置长按拖动,以一个imageView为例:
假设一个imageView已经被实例化了。

imageView.setOnLongClickListener(v -> {
                View.DragShadowBuilder builder = new View.DragShadowBuilder(v);
                v.startDrag(null, builder, null, 0);//第三个参数是传入一个关于这个view信息的任意对象(getLocalState),它即你需要在拖拽监听中的调用event.getLocalState()获取到这个对象来操作用的(比如传入一个RecyclerView中的position)。如果不需要这个对象,传null
                 return true;
            });

假设这个imageView的外围layout对象为containerLayout(比如一个RelativeLayout )
当拖动这个imageView时,外围layout需要执行点什么操作时:

containerLayout.setOnDragListener((v, event) -> {
            Log.d("containerLayout", "event.getAction():" + event.getAction());
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_ENDED: //拖拽停止
                   //your operation
                    break;
                case DragEvent.ACTION_DROP://拖拽中
                        //your operation
                    break;
                default:
                    break;
            }
            return true;
        });

当然,不止是外围layout可以做拖拽监听,其他的view也可以针对这个imageView的拖拽行为而做点什么。举个栗子,拖拽这个imageView到另一个view上面时执行点操作:

view1.setOnDragListener(((v, event) -> {
            switch (event.getAction()) {
                case DragEvent.ACTION_DROP://拖拽中
                    int index = (int) event.getLocalState();
                    //之前讲到的v.startDrag(null, builder, null, 0);第三个参数如果传入一个int 类型的值,在这里便可以 通过(int) event.getLocalState()取出来。如果传的是null,上面这行代码就去掉好了
                    break;
                case DragEvent.ACTION_DRAG_ENDED: //拖拽停止
                    break;
                default:
                    break;
            }
            return true;
        }));

补充:
如果想要长按的瞬间震动一下,可以在长按回调处增加两行代码:

Vibrator vib = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);   //获取系统震动服务
  vib.vibrate(70);   //震动70毫秒

Android 用户界面ImageView拖动复制(Drag and Drop)