若未了解单点触控,可先大概了解一下动作构建器Actionbuilder的PointerActions操作
appium2.0+之PointerActions详解
以下放大图片
的示例在夜神模拟器
上进行。
一、多点触控
1. 多点触控核心实现逻辑
(1)创建一个用于管理用户交互动作的 动作链对象/动作构建对象
(2)多指针创建:通过add_pointer_input
创建多个 touch 类型的虚拟手指
(3) 定义多个虚拟手指的同步动作序列
(4)统一执行perform
,实现多点触控
2. 执行时间逻辑
动作按 “时间轴” 执行,而非代码顺序,在同一个ActionBuilder
或ActionChains
中,两个手指(finger1
和finger2
)是否会 “同时运行”,不取决于代码的编写顺序,而取决于动作的 “时间线设置”—— 即通过duration
(持续时间)和pause
(暂停时间)定义的动作时序。同一时间点的动作会 “并行执行”,不同时间点的动作会 “串行执行”。
二、单点触控+多点触控案例
代码执行前准备
- 命令行启动appium服务(版本2.19.0):
appium --address 127.0.0.1 --log-level debug --use-drivers uiautomator2
- 模拟器打开,命令行adb devices显示设备已连接
1. 方法1:建立两个Actionbuilder对象
逻辑:建立两个Actionbuilder
对象,一个用来单点触控,一个用来多点触控
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : 草木零
@Software: PyCharm
@File : class04_multiTouch.py
@Time : 2025/7/29 14:22
@desc : 多点触控示例,放大图片
多点触控核心实现:
通过创建两个PointerInput对象模拟两个手指(finger1和finger2)
actionbuilder通过add_pointer_input添加多个触控设备
手指添加操作,最后perform,实现多点触控
'''
from time import sleep
from appium import webdriver
from appium.options.android import UiAutomator2Options # 导入 Android 选项类
from selenium.webdriver.common.actions.action_builder import ActionBuilder
# 手机/模拟器,配置信息
desired_caps = {
"platformName": "Android",
"platformVersion": "7.1.2",
"deviceName": "127.0.0.1:62001",
"appPackage": "com.android.gallery3d",
# 获取包名、界面名:adb shell dumpsys window|findstr mCurrentFocus
"appActivity": "com.android.gallery3d.app.GalleryActivity",
"noReset": False
}
# 显式传入 options 参数(适用于必须使用高版本 Selenium 的场景)
# 将 desired_caps 转换为 Options 实例
options = UiAutomator2Options().load_capabilities(desired_caps)
# 传入 options 参数
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
options=options # 必须传入 options,替代原来的 desired_capabilities
)
# 获取窗口尺寸
window_size = driver.get_window_size() #返回dict格式的当前窗口屏幕尺寸
width = window_size['width'] # 获取宽度
height = window_size['height'] # 获取高度
# 第一根手指的移动位置
startX1 = width*0.6
startY1 = height*0.4
endX1 = width*0.9
endY1 = height*0.1
# 第二根手指移动位置
startX2 = width*0.4
startY2 = height*0.6
endX2 = width*0.1
endY2 = height*0.9
# 分为两个builder(推荐两个builder统一使用touch类型),第一个用来先执行单击操作后perform,第二个builder做多点触控两个手指同时操作
# 创建单点触控的builder
singleBuilder = ActionBuilder(driver)
singleAct = singleBuilder.add_pointer_input('touch', 'singleAct')
# 创建双指多点触控的Builder
zoomBuilder = ActionBuilder(driver)
finger1 = zoomBuilder.add_pointer_input('touch', 'finger1')
finger2 = zoomBuilder.add_pointer_input('touch', 'finger2')
sleep(3) #待页面稳定
# 单击选择中间的图片
singleAct.create_pointer_move(duration=0, x=width*0.5, y=height*0.5)
singleAct.create_pointer_down() #按下
singleAct.create_pause(0.1) #间隔
singleAct.create_pointer_up(0) #抬起左键,0左键,1滚轮,2右键
singleAct.create_pause(1) #间隔1s
singleBuilder.perform()
# 多点触控操作
# 手指1操作
finger1.create_pointer_move(duration=0, x=startX1, y=startY1) # 手指1移动到起点
finger1.create_pointer_down()
finger1.create_pause(0.2) #按下后短暂停留,单位s
finger1.create_pointer_move(500, endX1, endY1)
finger1.create_pointer_up(0) #抬起
# 手指2操作
# 注意,finger2的开始执行时间不取决于代码的编写顺序,而取决于动作的 “时间线设置”,
# duration=0即在时间轴上0开始执行,因为finger1的时间轴开始也是0,这样就实现两根手指同时开始动作,若finger2的duration=1000,那就是在时间轴1s是才开始finger2的执行
finger2.create_pointer_move(duration=0, x=startX2, y=startY2) # 手指2移动到起点,duration单位为ms
finger2.create_pointer_down()
finger2.create_pause(0.2) #按下后短暂停留,单位s
finger2.create_pointer_move(500, endX2, endY2)
finger2.create_pointer_up(button=0) #抬起 button=0为抬起左键,1为滚轮,2为右键
# 执行完整动作链
zoomBuilder.perform()
sleep(2)
driver.quit()
2. 方法2:单点触控通过Actionchains,多点触控通过Actionbuilder实现
用下面的代码替换掉方法1里单点触控的代码即可:
# 创建单点触控的动作链
singleChains = ActionChains(driver)
# 通过Actionchains单击选择中间的图片
imgEl = driver.find_element('id', 'android:id/content')
singleChains.click(imgEl)
singleChains.pause(1) #暂停1s
singleChains.perform()
完整代码:
'''
@Author : 草木零
@Software: PyCharm
@File : class05_actionChains.py
@Time : 2025/7/30 11:39
@desc : 先通过Actionchains实现单点触控,再通过Actionbuilder实现多点触控
'''
from time import sleep
from appium import webdriver
from appium.options.android import UiAutomator2Options # 导入 Android 选项类
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import ActionChains
# 手机/模拟器,配置信息
desired_caps = {
"platformName": "Android",
"platformVersion": "7.1.2",
"deviceName": "127.0.0.1:62001",
"appPackage": "com.android.gallery3d",
# 获取包名、界面名:adb shell dumpsys window|findstr mCurrentFocus
"appActivity": "com.android.gallery3d.app.GalleryActivity",
"noReset": False
}
# 显式传入 options 参数(适用于必须使用高版本 Selenium 的场景)
# 将 desired_caps 转换为 Options 实例
options = UiAutomator2Options().load_capabilities(desired_caps)
# 传入 options 参数
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
options=options # 必须传入 options,替代原来的 desired_capabilities
)
# 获取窗口尺寸
window_size = driver.get_window_size() #返回dict格式的当前窗口屏幕尺寸
width = window_size['width'] # 获取宽度
height = window_size['height'] # 获取高度
# 第一根手指的移动位置
startX1 = width*0.6
startY1 = height*0.4
endX1 = width*0.9
endY1 = height*0.1
# 第二根手指移动位置
startX2 = width*0.4
startY2 = height*0.6
endX2 = width*0.1
endY2 = height*0.9
# 创建单点触控的动作链
singleChains = ActionChains(driver)
# 创建双指多点触控的Builder
zoomBuilder = ActionBuilder(driver)
finger1 = zoomBuilder.add_pointer_input('touch', 'finger1')
finger2 = zoomBuilder.add_pointer_input('touch', 'finger2')
sleep(3) #待页面稳定
# 通过Actionchains单击选择中间的图片
imgEl = driver.find_element('id', 'android:id/content')
singleChains.click(imgEl)
singleChains.pause(1) #暂停1s
# 执行动作链
singleChains.perform()
# 多点触控操作
# 手指1操作
finger1.create_pointer_move(duration=0, x=startX1, y=startY1) # 手指1移动到起点
finger1.create_pointer_down()
finger1.create_pause(0.2) #按下后短暂停留,单位s
finger1.create_pointer_move(500, endX1, endY1)
finger1.create_pointer_up(0) #抬起
# 手指2操作
# 注意,finger2的开始执行时间不取决于代码的编写顺序,而取决于动作的 “时间线设置”,
# duration=0即在时间轴上0开始执行,因为finger1的时间轴开始也是0,这样就实现两根手指同时开始动作,若finger2的duration=1000,那就是在时间轴1s是才开始finger2的执行
finger2.create_pointer_move(duration=0, x=startX2, y=startY2) # 手指2移动到起点,duration单位为ms
finger2.create_pointer_down()
finger2.create_pause(0.2) #按下后短暂停留,单位s
finger2.create_pointer_move(500, endX2, endY2)
finger2.create_pointer_up(button=0) #抬起 button=0为抬起左键,1为滚轮,2为右键
# 执行完整动作链
zoomBuilder.perform()
sleep(2)
driver.quit()
3. 方法3:创建两个动作链Actionchains对象
用下面的代码替换方法2中关于多点触控的代码即可:
# 创建双指多点触控的Actionchains实例
zoomChains = ActionChains(driver)
finger1 = zoomChains.w3c_actions.add_pointer_input('touch', 'finger1')
finger2 = zoomChains.w3c_actions.add_pointer_input('touch', 'finger2')
# ......省略的中间代码,其实这里finger1/2可调用的方法同Actionbuilder创建的手指是一样的(点开两者的add_pointer_input查看底层代码就知道是一样的返回),所以其他地方不用改
# 执行完整动作链
zoomChains.perform()
完整代码:
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : 草木零
@Software: PyCharm
@File : class05_actionChains02.py
@Time : 2025/7/30 12:03
@desc : 通过Actionchains实现多点触控,放大图片
操作:建立一个Actionchains实例来管理单点触控的操作,再建立另一个Actionchains实例来管理多点触控的操作
'''
from time import sleep
from appium import webdriver
from appium.options.android import UiAutomator2Options # 导入 Android 选项类
from selenium.webdriver import ActionChains
# 手机/模拟器,配置信息
desired_caps = {
"platformName": "Android",
"platformVersion": "7.1.2",
"deviceName": "127.0.0.1:62001",
"appPackage": "com.android.gallery3d",
# 获取包名、界面名:adb shell dumpsys window|findstr mCurrentFocus
"appActivity": "com.android.gallery3d.app.GalleryActivity",
"noReset": False
}
# 显式传入 options 参数(适用于必须使用高版本 Selenium 的场景)
# 将 desired_caps 转换为 Options 实例
options = UiAutomator2Options().load_capabilities(desired_caps)
# 传入 options 参数
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
options=options # 必须传入 options,替代原来的 desired_capabilities
)
# 获取窗口尺寸
window_size = driver.get_window_size() #返回dict格式的当前窗口屏幕尺寸
width = window_size['width'] # 获取宽度
height = window_size['height'] # 获取高度
# 第一根手指的移动位置
startX1 = width*0.6
startY1 = height*0.4
endX1 = width*0.9
endY1 = height*0.1
# 第二根手指移动位置
startX2 = width*0.4
startY2 = height*0.6
endX2 = width*0.1
endY2 = height*0.9
# 创建单点触控的动作链
singleChains = ActionChains(driver)
# 创建双指多点触控的Actionchains实例
zoomChains = ActionChains(driver)
finger1 = zoomChains.w3c_actions.add_pointer_input('touch', 'finger1')
finger2 = zoomChains.w3c_actions.add_pointer_input('touch', 'finger2')
sleep(3) #待页面稳定
# 通过Actionchains单击选择中间的图片
imgEl = driver.find_element('id', 'android:id/content')
singleChains.click(imgEl)
singleChains.pause(1) #暂停1s
# 执行动作链
singleChains.perform()
# 多点触控操作
# 手指1操作
finger1.create_pointer_move(duration=0, x=startX1, y=startY1) # 手指1移动到起点
finger1.create_pointer_down()
finger1.create_pause(0.2) #按下后短暂停留,单位s
finger1.create_pointer_move(500, endX1, endY1)
finger1.create_pointer_up(0) #抬起
# 手指2操作
# 注意,finger2的开始执行时间不取决于代码的编写顺序,而取决于动作的 “时间线设置”,
# duration=0即在时间轴上0开始执行,因为finger1的时间轴开始也是0,这样就实现两根手指同时开始动作,若finger2的duration=1000,那就是在时间轴1s是才开始finger2的执行
finger2.create_pointer_move(duration=0, x=startX2, y=startY2) # 手指2移动到起点,duration单位为ms
finger2.create_pointer_down()
finger2.create_pause(0.2) #按下后短暂停留,单位s
finger2.create_pointer_move(500, endX2, endY2)
finger2.create_pointer_up(button=0) #抬起 button=0为抬起左键,1为滚轮,2为右键
# 执行完整动作链
zoomChains.perform()
sleep(2)
driver.quit()
三、扩展:ActionChains部分底层代码
from selenium.webdriver import ActionChains
pycharm中按住Ctrl+左键点击from selenium.webdriver import ActionChains
中的ActionChains
,可查看ActionChains
的实现代码,发现它还是使用的 ActionBuilder
类来实现,如下图:
Actionchains封装的方法
如果Actionchains
中已实现的封装方法不够用,我们再通过w3c_actions
的pointer_action
里的方法来实现,前面已细讲PointerActions
类。
appium2.0+之PointerActions详解
下面展示如何从ActionChains
是如何从高到低看到PointerAction
的。
ActionChains(driver).w3c_actions
中的一些方法
图片:
其中ActionChains(driver).w3c_actions.add_pointer_input()
里的类型kind
有三种:mouse
(鼠标),touch
(触屏),pen
(笔触)
w3c_actions.pointer_action
中实现的方法
所以一层层走下来,如果Actionchains
类中已封装实现的方法不够满足我们的需求,是可以通过更底层的ActionChains(driver).w3c_actions.pointer_action
的方法来实现所求的,而PointerActions
类前面已具体讲解使用方法
appium2.0+之PointerActions详解