1,概述

原生api,提供跨进程拖拽数据,需提供剪切板、DragShadowBuilder等参数。

简单理解,调用该方法会在系统层次上生成阴影,默然与拖拽view大小相同,阴影中心位置与触摸点重合。

2,核心方法

public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
        Object myLocalState, int flags) {
        ......
}

开始拖放操作。 当您的应用程序调用此方法时,它会将View.DragShadowBuilder对象传递给系统。 系统调用该对象的View.DragShadowBuilder.onProvideShadowMetrics(Point, Point)来获取拖动阴影的度量,然后调用对象的View.DragShadowBuilder.onDrawShadow(Canvas)来绘制拖动阴影本身。
一旦系统有了拖动阴影,它就会通过向应用程序中当前可见的所有视图对象发送拖动事件来开始拖放操作。 它通过调用 View 对象的拖动侦听器( onDrag()的实现或通过调用 View 对象的onDragEvent()方法来完成此操作。两者都传递了一个DragEvent对象,该对象的DragEvent.getAction()值为DragEvent.ACTION_DRAG_STARTED 。
您的应用程序可以在任何附加的 View 对象上调用startDragAndDrop() 。 View 对象不需要是View.DragShadowBuilder中使用的对象,也不需要与用户选择拖动的 View 相关。

参数:
data – 一个ClipData对象,指向要通过拖放操作传输的数据。
shadowBuilder – 用于构建拖动阴影的View.DragShadowBuilder对象。
myLocalState – 一个包含有关拖放操作的本地数据的Object 。 当向同一活动中的视图分派拖动事件时,该对象将通过DragEvent.getLocalState()可用。 其他活动中的视图将无法访问此数据( DragEvent.getLocalState()将返回 null)。
myLocalState 是一种轻量级机制,用于将信息从拖动的 View 发送到目标 View。 例如,它可以包含区分复制操作和移动操作的标志。
flags – 控制拖放操作的标志。 这可以设置为 0 表示没有标志,或以下任意组合:
DRAG_FLAG_GLOBAL
DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION
DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION
DRAG_FLAG_GLOBAL_URI_READ
DRAG_FLAG_GLOBAL_URI_WRITE
DRAG_FLAG_OPAQUE


返回:
如果方法成功完成,则为true ,如果在任何地方失败,则为false 。 返回false表示系统由于另一个正在进行的操作或其他一些原因而无法进行拖动。

3,关注点

DragShadowBuilder有几个可以重写的方法,如

onPrivodeShadowMetrics:

outShadowSize指开始拖拽时,生成阴影面积,默然与view一致;

outShadowTouchPoint指阴影位置相对触摸点位置;

该方法在startDragAndDrop中回调一次;

public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
            final View view = mView.get();
            if (view != null) {
                outShadowSize.set(view.getWidth(), view.getHeight());
                outShadowTouchPoint.set(outShadowSize.x / 2, outShadowSize.y / 2);
            } else {
                Log.e(View.VIEW_LOG_TAG, "Asked for drag thumb metrics but no view");
            }
        }

onDrawShadow:

绘制阴影图像。 系统根据从onProvideShadowMetrics(Point, Point)回调接收到的尺寸创建Canvas对象。在startDragAndDrop和updateDragShadow中回调;

 public void onDrawShadow(Canvas canvas) {
            final View view = mView.get();
            if (view != null) {
                view.draw(canvas);
            } else {
                Log.e(View.VIEW_LOG_TAG, "Asked to draw drag shadow but no view");
            }
        }

4,方法完整代码

 public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
            Object myLocalState, int flags) {
        if (ViewDebug.DEBUG_DRAG) {
            Log.d(VIEW_LOG_TAG, "startDragAndDrop: data=" + data + " flags=" + flags);
        }
        if (mAttachInfo == null) {
            Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view.");
            return false;
        }
        if (!mAttachInfo.mViewRootImpl.mSurface.isValid()) {
            Log.w(VIEW_LOG_TAG, "startDragAndDrop called with an invalid surface.");
            return false;
        }

        if (data != null) {
            data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
        }

        Point shadowSize = new Point();
        Point shadowTouchPoint = new Point();
        shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);

        if ((shadowSize.x < 0) || (shadowSize.y < 0)
                || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
            throw new IllegalStateException("Drag shadow dimensions must not be negative");
        }

        // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
        // does not accept zero size surface.
        if (shadowSize.x == 0  || shadowSize.y == 0) {
            if (!sAcceptZeroSizeDragShadow) {
                throw new IllegalStateException("Drag shadow dimensions must be positive");
            }
            shadowSize.x = 1;
            shadowSize.y = 1;
        }

        if (ViewDebug.DEBUG_DRAG) {
            Log.d(VIEW_LOG_TAG, "drag shadow: width=" + shadowSize.x + " height=" + shadowSize.y
                    + " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
        }

        final ViewRootImpl root = mAttachInfo.mViewRootImpl;
        final SurfaceSession session = new SurfaceSession();
        final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                .setName("drag surface")
                .setParent(root.getSurfaceControl())
                .setBufferSize(shadowSize.x, shadowSize.y)
                .setFormat(PixelFormat.TRANSLUCENT)
                .setCallsite("View.startDragAndDrop")
                .build();
        final Surface surface = new Surface();
        surface.copyFrom(surfaceControl);
        IBinder token = null;
        try {
            final Canvas canvas = surface.lockCanvas(null);
            try {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                shadowBuilder.onDrawShadow(canvas);
            } finally {
                surface.unlockCanvasAndPost(canvas);
            }

            // repurpose 'shadowSize' for the last touch point
            root.getLastTouchPoint(shadowSize);

            token = mAttachInfo.mSession.performDrag(
                    mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(),
                    shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data);
            if (ViewDebug.DEBUG_DRAG) {
                Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
            }
            if (token != null) {
                if (mAttachInfo.mDragSurface != null) {
                    mAttachInfo.mDragSurface.release();
                }
                mAttachInfo.mDragSurface = surface;
                mAttachInfo.mDragToken = token;
                // Cache the local state object for delivery with DragEvents
                root.setLocalDragState(myLocalState);
            }
            return token != null;
        } catch (Exception e) {
            Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
            return false;
        } finally {
            if (token == null) {
                surface.destroy();
            }
            session.kill();
        }
    }

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐