Расширенные операции с интерфейсом

В этой главе представлены более глубокие API для автоматизации, с помощью которых вы можете выполнять различные детализированные операции. Содержание этой главы довольно обширно, и если вы сталкиваетесь с этим впервые, мы рекомендуем вам терпеливо прочитать каждый раздел.

Совет

При написании кода автоматизации вы можете напрямую вводить команду `lamda` в терминале справа на удаленном рабочем столе. Там вы можете выполнять тестовый код, представленный ниже, или самостоятельно проводить тесты по выбору элементов, кликам и т.д., что ускорит процесс написания и проверки вашего кода.

Получение элемента

Возможно, вы уже немного знакомы с этим из базовых знаний или предыдущих разделов. Вам необходимо найти соответствующие элементы с помощью селекторов, чтобы выполнять с ними операции. Вы также, вероятно, видели, где можно получить параметры селектора. Далее мы будем подробно рассматривать этот элемент. На изображении справа вы можете видеть информацию об элементе "同意" (Согласиться).

Пример элемента

Внимание

Элемент, на который вы нажимаете непосредственно в левой части интерфейса, может не быть фактическим элементом, так как он может перекрываться с другими элементами по размеру и положению. Обычно, если несколько элементов перекрываются по положению и размеру, мы отображаем их в информационной панели справа. Вы можете прокручивать вверх и вниз, чтобы найти нужный элемент. Вы также можете вручную перебирать все элементы, нажимая клавишу TAB в интерфейсе выбора слева.

Для получения вышеуказанного элемента мы обычно используем text. Условием для использования text является отсутствие на текущем экране другого элемента с таким же текстом "同意". Это самый простой способ. Кроме того, вы можете использовать resourceId, но следует помнить, что resourceId здесь не является уникальным идентификатором, а представляет собой идентификатор ресурса, и на одном экране может быть много элементов с одинаковым resourceId. Другие поля, такие как packageName, checkable и т.д., обычно используются реже, но их можно попробовать, если text, resourceId, description и другие отсутствуют. Мы можем получить этот элемент несколькими способами:

element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")

Клик по элементу

Вызовите следующий API для выполнения обычного клика по элементу. В данном контексте это будет имитировать ручное нажатие на кнопку "Согласиться".

element.click()

Если вам нужно указать конкретное место для клика на элементе, вы можете указать corner. Corner.COR_CENTER означает клик по центру элемента. Вы также можете кликнуть по его верхнему левому или правому нижнему углу (COR_BOTTOMRIGHT).

element.click_exists(corner=Corner.COR_TOPLEFT)

Выполняет длительное нажатие на элементе. Если элемент не существует, выбрасывается исключение. Этот API также поддерживает corner, но не позволяет указать продолжительность длительного нажатия.

element.long_click()

Кликает по элементу, если он существует. Если элемент не существует, вызов этого API не вызовет исключения. Этот API также поддерживает corner.

element.click_exists()
>>> element.click_exists()
True

Проверка существования

Во многих случаях перед выполнением дальнейших действий необходимо проверить существование элемента, иначе последующие шаги могут вызвать ошибку или привести к неверным действиям на неправильном экране. В некоторых ситуациях вам может понадобиться использовать следующий API для проверки существования.

element.exists()

Информация об элементе

В некоторых случаях вам может понадобиться получить частичную информацию об элементе, например, его координаты, область, текст или описание. Вы можете использовать следующий API для чтения информации об элементе.

element.info()

Для нашего тестового элемента выше, этот API выведет следующую информацию:

>>> info = element.info()
>>> print (info)
bounds { ... }
className: "android.widget.TextView"
clickable: true
enabled: true
focusable: true
packageName: "com.tencent.news"
resourceName: "com.tencent.news:id/btn_first_agree"
text: "\345\220\214\346\204\217"
visibleBounds { ... }

Подсказка

Вы могли заметить, что в приведенной выше информации отсутствуют некоторые поля, например, `description`. Обычно это означает, что значение этого поля пустое или `false`. Вы все равно можете получить доступ к соответствующим полям через свойства для получения их значений.

Как видите, эта информация выглядит довольно сложной. Это стандартный формат вывода protobuf. Вы можете напрямую получить доступ к соответствующим свойствам, чтобы вывести их фактические значения. Например, чтобы прочитать значение text элемента, вы можете сделать это следующим образом:

>>> info = element.info()
>>> print (info.text)
同意

Конечно, здесь также есть информация о координатах области элемента, к которой вы также можете получить доступ. Например, если вы хотите получить информацию об области элемента, вы можете использовать следующий код для ее вывода или сохранить ее в переменную для дальнейших операций:

>>> info = element.info()
>>> print (info.bounds)

Выводимое или возвращаемое значение — это информация об области (Bound). Позже вы обнаружите, что это тот же параметр, который используется в некоторых API для создания скриншотов. Да, вы можете передать этот параметр в API скриншота, чтобы сделать снимок только этого элемента, но мы уже сделали для вас удобную обертку.

Возможно, вы также захотите получить ширину и высоту элемента для расчета некоторых смещений, например, для расчета смещения относительно других элементов. Вы можете сделать это так:

>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138

Или получить центральную точку элемента, или угловые точки, такие как верхний левый и правый нижний углы. Конечно, следующие API обычно возвращают информацию о точке (Point), и вы можете получить из объекта Point соответствующие координаты X и Y на экране устройства.

>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792

Следующий вызов используется для получения координат углов элемента. В примере показано получение координат верхнего левого угла. Кроме того, поддерживается получение координат всех четырех углов, таких как bottom-right, top-right, bottom-left.

>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550

Перебор элементов

Вы также можете перебрать все элементы, найденные селектором. В обычном контексте этот элемент, скорее всего, будет один, поэтому для тестирования выберите другой селектор. Вы можете использовать цикл for или другие способы для перебора прямо на селекторе.

for i in element: print (i)

Или, если вы знаете, что существует несколько совпадающих элементов и хотите получить N-й из них, вы можете использовать следующий API.

element_3rd = element.get(3)

Подсчет элементов

Обычно вы не будете использовать этот API напрямую. Следующий вызов позволяет получить количество элементов, соответствующих вашему текущему селектору.

>>> element.count()
1

Скриншот элемента

Мы поддерживаем создание скриншотов на уровне элементов. Вы можете сделать снимок только одного элемента без необходимости делать скриншот всего экрана и затем обрезать его.

element.screenshot(quality=60)

После создания скриншота вы можете напрямую использовать getvalue для получения двоичных данных изображения или передать их непосредственно в PIL Image.

>>> element.screenshot(quality=60).getvalue()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xe2\x02(ICC_PROFILE\x00\x01\x01\x00\x00\x02\x18\x00\x00\x00\x00\x02\x10\x00\x00mntrRGB XYZ \x00\x00...

Или, если вам не нужна дальнейшая обработка, вы можете просто сохранить скриншот в локальный файл.

>>> element.screenshot(quality=60).save("image.png")

Ожидание элемента

В некоторых случаях вам может потребоваться определить, завершилась ли загрузка текущей страницы. Обычно это можно сделать, проверив, отображается ли соответствующий элемент. Следующий пример будет ждать появления элемента "同意" в течение максимум 10 секунд.

Подсказка

Время ожидания здесь указывается в миллисекундах, поэтому 10 секунд нужно умножить на 1000. 10 секунд = 10000 миллисекунд.
element.wait_for_exists(10*1000)
>>> element.wait_for_exists(10*1000)
True

Конечно, мы поддерживаем не только ожидание появления элемента, но и ожидание его исчезновения, то есть до тех пор, пока элемент не исчезнет с экрана.

element.wait_until_gone(10*1000)
>>> element.wait_until_gone(10*1000)
False

Ввод текста

Ввод текста — это аспект, требующий особого внимания. Мы не можем вводить текст в кнопку "Согласиться", потому что это кнопка. Теперь давайте рассмотрим элемент поля ввода. Основная информация об этом элементе показана ниже.

Ввод текста

Внимание

При получении элемента поля ввода есть некоторые важные моменты. Пожалуйста, обратите внимание, что при поиске элемента поля ввода ваш метод ввода (клавиатура) должен быть **в активном состоянии (открыт)**. Также рекомендуется тщательно искать, иначе вы можете получить не настоящее поле ввода.

Подсказка

В процессе автоматизации, чтобы активировать метод ввода, вам просто нужно написать код, который сначала кликнет по родительскому элементу, отображающему поле ввода.

Для вышеуказанного поля ввода мы можем вызвать следующий API, чтобы ввести строку "你好世界" (Привет, мир). Конечно, поддерживается ввод английских или других unicode-строк. Вам просто нужно использовать его, как показано ниже, чтобы ввести текст в поле.

>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True

Или, если вам вдруг захотелось получить текущий текст, отображаемый в этом поле ввода, вы можете вызвать его так.

Внимание

Вы можете заметить, что здесь мы изменили селектор. Изначально мы использовали селектор по `text`, но после ввода текста его содержимое изменилось, и элемент перестал существовать, поэтому мы переключились на другой селектор. Использование подходящего селектора очень важно, но раз уж мы так написали, оставим как есть.
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'

Или же очистить текущее введенное содержимое. Обычно ввод текста автоматически очищает предыдущий текст, но вы также можете сделать это вручную.

Подсказка

На самом деле, аналогичного эффекта можно достичь, циклически нажимая клавишу BACKSPACE с помощью API нажатия клавиш.
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True

Примечание

В крайних случаях ввод текста с помощью этого API может не работать в некоторых местах. Мы работаем над поддержкой таких случаев.

Обычный свайп

Используйте следующий API для выполнения свайпа по экрану, например, для прокрутки списков вверх и вниз. Следующий вызов выполняет свайп вверх. Параметр step можно настроить самостоятельно; чем он больше, тем медленнее будет свайп, что подходит для свайпов, требующих высокой точности.

Внимание

В простых случаях этот параметр селектора можно не указывать, но если свайп не работает, пожалуйста, установите условия селектора на подходящий элемент, например, на элемент с атрибутом `scrollable` или на контейнер первого уровня списка.
d().swipe(direction=Direction.DIR_UP, step=32)
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
Указатель направленияОписание
Direction.DIR_UPСвайп вверх
Direction.DIR_LEFTСвайп влево
Direction.DIR_DOWNСвайп вниз
Direction.DIR_RIGHTСвайп вправо

Быстрый свайп (Fling)

Быстрый свайп имитирует быстрое движение пальцем по экрану. Эта операция быстро прокручивает экран и подходит для симуляции действий, таких как быстрый просмотр. В следующем примере экран прокручивается сверху вниз. Селектор в примере пуст, но вам все равно нужно решить, заполнять ли его в зависимости от конкретной ситуации.

d().fling_from_top_to_bottom()

Быстрый свайп снизу вверх

d().fling_from_bottom_to_top()

Быстрый свайп слева направо

d().fling_from_left_to_right()

Быстрый свайп справа налево

d().fling_from_right_to_left()

Внимание

В простых случаях этот параметр селектора можно не указывать, но если свайп не работает, пожалуйста, установите условия селектора на подходящий элемент, например, на элемент с атрибутом `scrollable` или на контейнер первого уровня списка.
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True

В процессе обновления...

Другие операции

# Перетащить это приложение в папку "Покупки" (измените в соответствии с вашей ситуацией)
element.drag_to(Selector(text="购物"))

#########
# Поиск дочерних и соседних элементов
#########
# Иногда встречаются повторяющиеся элементы или элементы без явных признаков, которые трудно найти
# В этом случае можно сузить область поиска, используя методы поиска дочерних/соседних элементов
# Дочерний элемент, например: в окне входа в чат поля ввода являются дочерними элементами этого окна
# Соседние элементы, например: в окне входа в чат поля для имени пользователя и пароля являются соседними элементами (в обычной ситуации)
form = d(resourceId="login_form")
form.child(index=1)
# Это получит дочерний элемент login_form с индексом 1
form.child(index=1).sibling()
# Таким же образом можно найти соседнюю кнопку "Восстановить пароль"
# (На самом деле, ее можно найти по тексту, так что это необязательно, здесь просто для демонстрации)
form.sibling(textContains="找回密码")
# Они сами являются элементами (element), и вы можете выполнять с ними любые операции для element


# Другое: свайп вниз/влево/вправо/вверх до самого конца
# Поскольку не всегда возможно прокрутить до конца или определить, что конец достигнут
# поэтому параметр max_swipes является обязательным
d().fling_from_top_to_bottom_to_end(max_swipes=32)
d().fling_from_bottom_to_top_to_end(max_swipes=32)
d().fling_from_left_to_right_to_end(max_swipes=32)
d().fling_from_right_to_left_to_end(max_swipes=32)

#########
# scroll: более "механическая" прокрутка
#########
step = 60
max_swipes = 32
# Прокрутить сверху вниз на step шагов
d().scroll_from_top_to_bottom(step)
# Прокрутить снизу вверх на step шагов
d().scroll_from_bottom_to_top(step)
# Прокрутить слева направо на step шагов
d().scroll_from_left_to_right(step)
# Прокрутить справа налево на step шагов
d().scroll_from_right_to_left(step)

# Другое: свайп вниз/влево/вправо/вверх до самого конца
# Аналогично описанию fling выше
d().scroll_from_top_to_bottom_to_end(max_swipes, step)
d().scroll_from_bottom_to_top_to_end(max_swipes, step)
d().scroll_from_left_to_right_to_end(max_swipes, step)
d().scroll_from_right_to_left_to_end(max_swipes, step)