Appium高级操作--从源码角度解析--模拟复杂手势操作

书接上回,Android自动化--Appium基本操作-CSDN博客文章浏览阅读600次,点赞10次,收藏5次。书接上回,上一篇文章已经介绍了appium在Android端的元素定位方法和识别工具Inspector,本次要介绍使用如何利用Appium对找到的元素进行基本操作。 https://blog.csdn.net/fantasy_4/article/details/146080872上一篇文章介绍了Appium的基本操作,本篇文章接着从源码的角度讲解,Appium高级操作中的模拟复杂手势操作

W3C Actions

客户端有比Web端更丰富的操作,除了单击,输入,还有长按,滑动,多指操控等。

Appium基于Selenium开发,Selenium4.0已经弃用了JSON Wire Protocol协议(定义了浏览器与Browser driver通信协议规范),改成支持W3C WebDriver,Appium2.x版本当然也支持,并且Appium2.x版本已经不再使用TouchAction类和Multiaction类,可以用W3C Actions。

Appium模拟复杂手势流程,需要三个步骤1.确定输入源 2.创建动作 3.动作链以及执行所有动作,下面根据这三个步骤进行讲解复杂手势操作

W3C输入源类型

key 输入源pointer 输入源wheel 输入源 ( 客户端不适用 )none 输入源
设备类型键盘鼠标、触控板、手写笔鼠标滚轮、触控板滚动无设备(默认输入源)
用途模拟键盘按键操作模拟鼠标、触控板或手写笔的指针操作模拟滚轮或触控板的滚动操作用于不需要设备的操作,通常作为默认输入源
支持的操作按下键(keyDown)、 释放键(keyUp)、 发送键(sendKeys移动指针(pointerMove、 按下按钮(pointerDown)、 释放按钮(pointerUp)、 点击(click)、 拖动(dragAndDrop)、 悬停(hover) 、双击(doubleClick垂直滚动(scroll)、 水平滚动(scroll)、 缩放(通过组合键,如 Ctrl + 滚动通常与其他输入源组合使用

创建动作类--ActionBuilder类部分源码解析

    def __init__(self,driver,mouse: Optional[PointerInput] = None,wheel: Optional[WheelInput] = None,keyboard: Optional[KeyInput] = None,duration: int = 250,) -> None:mouse = mouse or PointerInput(interaction.POINTER_MOUSE, "mouse")keyboard = keyboard or KeyInput(interaction.KEY)wheel = wheel or WheelInput(interaction.WHEEL)self.devices = [mouse, keyboard, wheel]self._key_action = KeyActions(keyboard)self._pointer_action = PointerActions(mouse, duration=duration)self._wheel_action = WheelActions(wheel)self.driver = driverdef add_pointer_input(self, kind: str, name: str) -> PointerInput:"""Add a new pointer input device to the action builder.
​Parameters:----------kind : strThe kind of pointer input device.- "mouse"- "touch"- "pen"
​name : strThe name of the pointer input device.
​Returns:--------PointerInput : The newly created pointer input device.
​Example:-------->>> action_builder = ActionBuilder(driver)>>> action_builder.add_pointer_input(kind="mouse", name="mouse")"""new_input = PointerInput(kind, name)self._add_input(new_input)return new_inputdef _add_input(self, new_input: Union[KeyInput, PointerInput, WheelInput]) -> None:"""Add a new input device to the action builder.
​Parameters:----------new_input : Union[KeyInput, PointerInput, WheelInput]The new input device to add."""self.devices.append(new_input)

ActionBuilder类的构造函数可以知道,创建一个ActionBuilder类的实例,必传参数:WebDriver实例,鼠标子类型默认为mouse,devices 输入源默认为[mouse, keyboard, wheel]三种类型的列表。

举例:假设新增pointer输入源(调用add_key_input),devices列表中增加new_input并返回一个PointerInput类的新创建输入源

主要方法如下:

  • add_key_input(name: str):键盘输入

  • add_pointer_input(kind: str, name: str):鼠标输入,上文已经提到过鼠标输入源细分类型,即kind值只能为mousetouchpen

  • add_wheel_input(name: str):滚轮输入--客户端不涉及

  • perform():执行动作

  • clear_actions():清除已经加入device列表的动作

动作链以及执行动作--ActionChains类部分源码解析

使用W3C Action模拟用户操作流程,需要使用ActionChains类,即动作链以及执行所有动作。

"""ActionChains are a way to automate low level interactions such as mouse
movements, mouse button actions, key press, and context menu interactions.
This is useful for doing more complex actions like hover over and drag and
drop.
​
Generate user actions.When you call methods for actions on the ActionChains object,the actions are stored in a queue in the ActionChains object.When you call perform(), the events are fired in the order theyare queued up.
"""

大概意思是说:

1.ActionChain 是一种自动化低级别的交互方法,例如移动、鼠标按钮作、按键和上下文菜单交互等,这对于执行更复杂的作(如将鼠标悬停在上面并拖动)很有用。

2.当你要调用ActionChains object的操作行为的方法时,这些操作行为会存储在ActionChains object的队列中,调用 perform() 时,事件将按它们的顺序触发。

也就是说,调用ActionChain类的一系列操作方法之后,还需要最后调用 perform()方法,去一一执行操作

AnyDevice = Union[PointerInput, KeyInput, WheelInput]
​
def __init__(self, driver: WebDriver, duration: int = 250, devices: list[AnyDevice] | None = None) -> None:"""Creates a new ActionChains.
​:Args:- driver: The WebDriver instance which performs user actions.- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput"""self._driver = drivermouse = Nonekeyboard = Nonewheel = Noneif devices is not None and isinstance(devices, list):for device in devices:if isinstance(device, PointerInput):mouse = deviceif isinstance(device, KeyInput):keyboard = deviceif isinstance(device, WheelInput):wheel = deviceself.w3c_actions = ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration)
​
  • ActionBuilder类和ActionChains类的部分源码可知:

  • 创建ActionChains类的实例,必须传入WebDriver实例,PointerInputduration默认为250毫秒(可不传),输入源devices可以为Union[PointerInput, KeyInput, WheelInput]中任意类型,默认为None(可不传)

  • devices列表为None时,创建ActionBuilder实例的device参数默认为[mouse, keyboard, wheel]

  • 创建ActionChains类的属性self.w3c_actionsActionBuilder类的实现

总结流程

下面总结Appium模拟复杂手势整体流程

  1. 创建ActionChains类实例action时,一定要传入WebDriver实例参数,

  2. 创建实例成功后,调用w3c_actions属性(ActionBuilder实例)将devices置为空列表,不使用默认的[mouse, keyboard, wheel],因为客户端鼠标操作子类型为touch不能使用mouse

  3. 调用w3c_actionsadd_XXX_input方法将新输入源加入到devices列表中,并返回XXXInput类的新输入源

  4. 调用新输入源(new_input)的各种操作方法(例如鼠标按下按钮,释放按钮,鼠标移动等)(这块源码就不细讲了可以自行查看)

  5. 如此加入一些列输入操作后,ActionChains类的实例调用perform()方法,实际上调用就是ActionBuilderperform()方法执行一系列操作

举例实现

  1. 长按操作
    from time import sleep
    ​
    from appium import webdriver
    from appium.options.android import UiAutomator2Options
    from appium.webdriver.common.appiumby import AppiumBy
    from selenium.webdriver import ActionChains
    from selenium.webdriver.common.actions.mouse_button import MouseButton
    ​
    desired_caps = {"platformName": "Android","appium:deviceName": "Q7PDU19731008305","appium:appPackage": "com.dangdang.buy2","appium:appActivity": "com.dangdang.buy2.activity.ActivityMainTab","appium:automationName": "UiAutomator2","newCommandTimeout": 300
    }
    ​
    print("Desired Capabilities: ", desired_caps)
    ​
    try:driver = webdriver.Remote("http://localhost:4723", options=UiAutomator2Options().load_capabilities(desired_caps))
    ​if driver is None:raise RuntimeError("Appium driver initialization failed!")
    ​# 定位单击购物车driver.find_element(AppiumBy.ID, "'com.dangdang.buy2:id/tab_cart_iv'").click()sleep(3)# 获取屏幕窗口大小size_dict = driver.get_window_size()# 创建ActionChains类实例actions = ActionChains(driver)# 输入设备列表置空actions.w3c_actions.devices = []# ======手指长按===========new_input = actions.w3c_actions.add_pointer_input('touch', 'finger0')# 输入源动作:移动到某点。使用相对位置:x的0.5 y的0.3new_input.create_pointer_move(x=size_dict['width'] * 0.5, y=size_dict['height'] * 0.3)# 按住鼠标左键new_input.create_pointer_down(button=MouseButton.LEFT)# 等待2秒,模拟长按操作,单位是秒new_input.create_pause(2)# 松开鼠标左键new_input.create_pointer_up(button=MouseButton.LEFT)# 执行操作actions.perform()sleep(3)
    ​
    finally:# 关闭 Appium 会话driver.quit()
     
  2. 左滑操作
    from time import sleep
    ​
    from appium import webdriver
    from appium.options.android import UiAutomator2Options
    from appium.webdriver.common.appiumby import AppiumBy
    from selenium.webdriver.common.action_chains import ActionChains
    from selenium.webdriver import ActionChains
    from selenium.webdriver.common.actions.mouse_button import MouseButton
    ​
    desired_caps = {"platformName": "Android","deviceName": "Q7PDU19731008305","appPackage": "com.sankuai.movie","appActivity": "com.sankuai.movie.MovieMainActivity","automationName": "UiAutomator2"
    }
    ​
    print("Desired Capabilities: ", desired_caps)
    ​
    try:driver = webdriver.Remote("http://localhost:4723", options=UiAutomator2Options().load_capabilities(desired_caps))
    ​if driver is None:raise RuntimeError("Appium driver initialization failed!")# click()点击操作 # 同意条款driver.implicitly_wait(10)driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.sankuai.movie:id/cyf")').click(
    ​# ========左滑操作=======size_dict = driver.get_window_size()actions = ActionChains(driver)# 设备列表置为空actions.w3c_actions.devices = []
    ​new_input = actions.w3c_actions.add_pointer_input('touch', 'finger0')# ======手指从屏幕X轴0.7的位置左滑到X轴0.2的位置,Y轴是屏幕的0.6=======new_input.create_pointer_move(x=size_dict['width']*0.7, y=size_dict['height']*0.6)sleep(3)# 按下鼠标左键new_input.create_pointer_down(button=MouseButton.LEFT)# 等待2秒,模拟滑动new_input.create_pause(0.2)# ======手指从屏幕X轴0.7的位置左滑到X轴0.2的位置,Y轴是屏幕的0.6=======new_input.create_pointer_move(x=size_dict['width']*0.2, y=size_dict['height']*0.6)sleep(3)# 松开鼠标左键new_input.create_pointer_up(button=0)# 执行动作actions.perform()
    ​sleep(3)
    ​
    finally:# 关闭 Appium 会话driver.quit()

  3. 多指触控
    '''只给出双指放大图片部分代码'''# 获取屏幕窗口大小size_dict = driver.get_window_size()# 创建ActionChains类实例actions = ActionChains(driver)# 输入设备列表置空actions.w3c_actions.devices = []
​# ======第一根手指:从正中心向右上角滑动===========# 添加一个新的输入源到设备列表中,输入源类型为Touch,id为finger0new_input = actions.w3c_actions.add_pointer_input('touch', 'finger0')# 输入源动作:移动到某点。使用相对位置:x的0.5 y的0.5new_input.create_pointer_move(x=size_dict['width'] * 0.5, y=size_dict['height'] * 0.5)# 按住鼠标左键new_input.create_pointer_down(button=MouseButton.LEFT)# 等待2秒,模拟长按操作,单位是秒new_input.create_pause(0.2)# 输入源动作:移动到某点。使用相对位置:x的0.9 y的0.1new_input.create_pointer_move(x=size_dict['width'] * 0.9, y=size_dict['height'] * 0.1)# 松开鼠标左键new_input.create_pointer_up(button=MouseButton.LEFT)
​# =====第二根手指:从正中心向左下角滑动========# 添加一个新的输入源到设备列表,输入源类型为Touch,id为finger1new_input = actions.w3c_actions.add_pointer_input('touch', 'finger1')# 输入源动作:移动到某点。使用相对位置:x的0.5 y的0.5new_input.create_pointer_move(x=size_dict['width'] * 0.5, y=size_dict['height'] * 0.5)# 按住鼠标左键new_input.create_pointer_down(button=MouseButton.LEFT)# 等待2秒,模拟长按操作,单位是秒new_input.create_pause(0.2)# 输入源动作:移动到某点。使用相对位置:x的0.1 y的0.9new_input.create_pointer_move(x=size_dict['width'] * 0.1, y=size_dict['height'] * 0.9)# 松开鼠标左键new_input.create_pointer_up(button=MouseButton.LEFT)
​# 执行操作actions.perform()sleep(3)

下文章将继续Appium的其他高级操作的讲解,比如toast元素识别,hybrid App操作,屏幕截图等~敬请期待哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/73249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot学生宿舍管理系统的设计与开发

项目概述 幽络源分享的《SpringBoot学生宿舍管理系统的设计与开发》是一款专为校园宿舍管理设计的智能化系统,基于SpringBoot框架开发,功能全面,操作便捷。该系统涵盖管理员、宿管员和学生三大角色,分别提供宿舍管理、学生信息管…

爱普生温补晶振 TG5032CFN高精度稳定时钟的典范

在科技日新月异的当下,众多领域对时钟信号的稳定性与精准度提出了极为严苛的要求。爱普生温补晶振TG5032CFN是一款高稳定性温度补偿晶体振荡器(TCXO)。该器件通过内置温度补偿电路,有效抑制环境温度变化对频率稳定性的影响&#x…

【原创】在高性能服务器上,使用受限用户运行Nginx,充当反向代理服务器[未完待续]

起因 在公共高性能服务器上运行OllamaDeepSeek,如果按照默认配置启动Ollama程序,则自己在远程无法连接你启动的Ollama服务。 如果修改配置,则会遇到你的Ollama被他人完全控制的安全风险。 不过,我们可以使用一个方向代理&#…

Bash和Zsh的主要差异是?

Bash(GNU Bourne-Again Shell) 和 Zsh(Z Shell) 都是功能强大的Unix/Linux Shell,广泛用于交互式使用和脚本编写。 尽管它们有很多相似之处,但在功能、语法、配置选项等方面也存在一些显著的区别。 是Bas…

芯科科技推出的BG29超小型低功耗蓝牙®无线SoC,是蓝牙应用的理想之选

具有扩大的内存和超低功耗特性的超小型BG29是互联健康设备的理想之选 低功耗无线领域内的领导性创新厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今日宣布:推出全新的第二代无线开发平台产品BG29系列无线片上系统…

【数据挖掘】知识蒸馏(Knowledge Distillation, KD)

1. 概念 知识蒸馏(Knowledge Distillation, KD)是一种模型压缩和知识迁移技术,旨在将大型复杂模型(称为教师模型)中的知识传递给一个较小的模型(称为学生模型),以减少计算成本&…

选型消息队列(MQ):ActiveMQ、RabbitMQ、RocketMQ、Kafka对比

选型消息队列(MQ):ActiveMQ、RabbitMQ、RocketMQ、Kafka对比 选型消息队列(MQ)1. 引言2. 消息队列核心指标3. MQ 技术对比分析4. 详细分析及案例4.1 ActiveMQ:传统企业级 MQ 方案4.2 RabbitMQ:高…

AWK 入门教程:强大的文本处理工具

AWK 是一种强大的文本处理工具,广泛用于 Linux/Unix 系统中对文本文件或数据流进行操作。它能够基于条件筛选、统计字段、重新排列数据等。主要特点包括: 2. AWK 的基本语法 2.1 AWK 程序的结构 AWK 程序的结构: awk pattern { action } file 2.2 常…

mysql select distinct 和 group by 哪个效率高

在有索引的情况下,SELECT DISTINCT和GROUP BY的效率相同;在没有索引的情况下,SELECT DISTINCT的效率高于GROUP BY‌。这是因为SELECT DISTINCT和GROUP BY都会进行分组操作,但GROUP BY可能会进行排序,触发filesort&…

使用conda将python环境打包,移植到另一个linux服务器项目中

问题:因为新的服务器A不能联网,导致离线pip install包耗时耗力,旧的服务器B中的Anaconda和A中是同一个版本,有现成的python环境,并且服务器B可以联网,现想将B中的环境,直接移植到A中使用。 解决…

晶晨S905M/晶晨S905L2芯片-原机安卓4升级安卓7.1.2-通刷线刷固件包

晶晨S905M/晶晨S905L2芯片-原机安卓4升级安卓7.1.2-通刷线刷固件包 线刷方法:(新手参考借鉴一下) 1、准备好一根双公头USB线刷刷机线,长度30-50CM长度最佳,同时准备一台电脑; 2、电脑上安装好…

KICK第四讲Linux 系统下安装 GCC 编译器全指南

Linux 系统下安装 GCC 编译器全指南 GCC(GNU Compiler Collection)是 Linux 系统下最常用的编译器之一,支持 C/C、Java 等多种编程语言。本文将介绍不同 Linux 发行版下的安装方法,帮助开发者快速配置开发环境。 一、使用包管理…

Django系列教程(8)——函数视图及通用类视图

目录 什么是视图(View)及其工作原理 接近现实的函数视图 更复杂的案例: 视图处理用户提交的数据 基于函数的视图和基于类的视图 Django通用类视图 a. ListView b. DetailView c. CreateView d. UpdateView e. FormView f. DeleteView 小结 Django的视图(view)是处理…

c# 查找相似颜色算法

下是一个基于欧几里得距离的C#颜色相似度查找算法实现,包含详细注释和优化策略: using System; using System.Collections.Generic;public class ColorMatcher {// 颜色容器 - 使用字典存储颜色ID到RGB的映射private readonly Dictionary<int, byte[]> _colorDictiona…

【A2DP】蓝牙音频编解码器互操作性要求详解

目录 一、音频编解码器互操作性&#xff1a;核心要点总览 二、必选与可选编解码器互操作性要求大盘点 2.1 必选与可选的编解码器支持 2.2 必选编解码器要求 2.3 可选编解码器要求 2.4 厂商自定义&#xff08;Vendor Specific&#xff09;A2DP编解码器互操作性要求 2.5 不…

electron 设置跨域iframe

在 Electron 的主进程中禁用同源策略 在 Electron 的主进程文件中添加 app.commandLine.appendSwitch("disable-site-isolation-trials"); 来禁用站点隔离试验。在创建 BrowserWindow 时&#xff0c;设置 webPreferences 的 webSecurity: false 来禁用同源策略。

c-线程创建,同步互斥,互斥锁;

文章目录 案例描述1代码实现代码解释 案例背景2代码实现代码解析关键概念总结扩展练习 案例描述1 我们将模拟一个简单的售票系统&#xff0c;其中有两个售票窗口同时出售100张票。为了确保不会卖出超过100张票&#xff0c;并且不会出现卖票时的竞态条件&#xff08;race condi…

SpringBoot第二天

目录 1.Web开发 1.1简介 1.2SpringBoot对静态资源的映射规则 1.3模板引擎 1.3.1引入thymeleaf&#xff1b; 1.3.2Thymeleaf语法 1.3.2.1标准表达式语法 1.变量表达式 1.3.2.2表达式支持的语法 1.3.2.3常用的thymeleaf标签 1.4Springboot整合springmvc 1.4.1Springmvc…

Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级

一、缓存雪崩&#xff1a; 1、什么是缓存雪崩&#xff1a; 如果缓在某一个时刻出现大规模的key失效&#xff0c;那么就会导致大量的请求打在了数据库上面&#xff0c;导致数据库压力巨大&#xff0c;如果在高并发的情况下&#xff0c;可能瞬间就会导致数据库宕机。这时候如果…

Html5记忆翻牌游戏开发经验分享

H5记忆翻牌游戏开发经验分享 这里写目录标题 H5记忆翻牌游戏开发经验分享前言项目概述技术要点解析1. 页面布局&#xff08;HTML CSS&#xff09;响应式设计 2. 翻牌动画效果3. 游戏逻辑实现状态管理卡片配对检测 开发技巧总结1. 模块化设计2. 性能优化3. 用户体验 踩坑经验扩…