【Android】View.startDragAndDrop

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();
        }
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
# -*- coding: UTF-8 -*- from lib2to3.pgen2 import driver from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy el1 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="通讯录") el1.click() el2 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.ListView/android.widget.FrameLayout[3]/android.widget.RelativeLayout") el2.click() el3 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[1]/android.widget.TextView") el3.click() el4 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[8]") el4.click() el5 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[11]") el5.click()
06-08
根据你提供的代码,看起来你使用了 `lib2to3.pgen2.driver` 模块中的 `find_element` 方法。但是,`lib2to3.pgen2.driver` 模块实际上并没有这个方法,所以出现了 `AttributeError`。 我猜测你可能是想使用 Appium 的 `driver.find_element` 方法。如果是这样的话,你需要更改你的代码来正确导入 `appium.webdriver` 模块并使用它的 `driver` 属性。具体的修改如下: ```python # -*- coding: UTF-8 -*- from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy # 创建 Appium driver desired_caps = {} desired_caps['platformName'] = 'Android' # 添加其他的 desired capabilities 选项... driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) # 执行测试操作 el1 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="通讯录") el1.click() el2 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.ListView/android.widget.FrameLayout[3]/android.widget.RelativeLayout") el2.click() el3 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[1]/android.widget.TextView") el3.click() el4 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[8]") el4.click() el5 = driver.find_element(by=AppiumBy.XPATH, value="/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.view.ViewGroup/android.view.ViewGroup/androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[11]") el5.click() # 关闭 driver driver.quit() ``` 注意,这里我使用了 `appium.webdriver` 模块的 `webdriver.Remote` 方法来创建 driver,然后使用 `driver.find_element` 方法来查找元素。最后,在测试结束后,我也关闭了 driver。你需要根据你的具体情况修改代码中的 `desired_caps` 和元素定位方式等内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值