Android自動化學習4--uiautomator2判斷是否滑動到頁面底部


前言

我們在使用 uiautomator2 做UI自動化時,有時需要進行多次滑動操作,比如我進入某個APP頁面定位某個元素,如果該元素不存在,那么我可能會一直進行上滑操作,如果定位到該元素就停止繼續滑動。

  • 一般的情況,如果頁面是固定不變的內容,那么我們可以嘗試並計算出大概需要滑動多少次才能到該元素的區域,這個時候我們直接把滑動次數寫死就好。

  • 另一種情況,如果頁面是經常變化的內容,這個時候我們就不知道到底需要滑動多少次,也許需要滑動到頁面底部,才能判斷該元素是否存在。

今天我們就學習下,如何在 uiautomator2 中判斷是否已滑動到頁面底部。

利用 dump_hierarchy() 獲取界面UI結構

在 uiautomator2 中,我們通過 dump_hierarchy() 方法獲取到當前界面的UI結構信息,同時該方法會把數據以 XML 形式的字符串進行返回。

import uiautomator2 as u2

d = u2.connect()
res = d.dump_hierarchy()

print(res)

返回的形式大致如下,它反應了當前頁面的所有UI層次結構關系。

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
  <node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
    <node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
      <node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
        <node index="0" text="" resource-id="com.sec.android.app.launcher:id/launcher" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
          <node index="0" text="" resource-id="com.sec.android.app.launcher:id/drag_layer" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
            <node index="0" text="" resource-id="com.sec.android.app.launcher:id/apps_view" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
              <node index="0" text="" resource-id="com.sec.android.app.launcher:id/apps_content" class="com.android.launcher3.allapps.view.AppsPagedView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,1920]">
                <node index="3" text="" resource-id="" class="android.view.ViewGroup" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[23,225][1057,1833]">
                  <node index="0" text="" resource-id="" class="android.view.ViewGroup" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[23,237][1057,1833]">
                    <node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="ATX" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="true" password="false" selected="false" visible-to-user="true" bounds="[23,237][274,549]">
                      <node index="0" text="" resource-id="com.sec.android.app.launcher:id/iconview_imageView" class="android.widget.ImageView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[67,256][229,418]" />
                      <node index="2" text="ATX" resource-id="com.sec.android.app.launcher:id/iconview_titleView" class="android.widget.TextView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[114,424][183,478]" />
                    </node>
                  </node>
                </node>
              </node>
              <node index="1" text="" resource-id="com.sec.android.app.launcher:id/apps_searchbar_container" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,99][1080,207]">
                <node index="0" text="" resource-id="com.sec.android.app.launcher:id/app_search_wrapper" class="android.widget.RelativeLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[60,99][1020,207]">
                  <node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[60,99][1020,207]">
                    <node index="0" text="" resource-id="com.sec.android.app.launcher:id/app_search_edit_text_wrapper" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[60,99][774,207]">
                      <node index="0" text="  搜索手機" resource-id="com.sec.android.app.launcher:id/app_search_edit_text" class="android.widget.EditText" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="true" password="false" selected="false" visible-to-user="true" bounds="[72,99][774,207]" />
                    </node>
                    <node index="1" text="" resource-id="com.sec.android.app.launcher:id/voice_search_button" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[774,99][894,207]">
                      <node index="0" text="" resource-id="com.sec.android.app.launcher:id/voice_search_icon_imageview" class="android.widget.ImageButton" package="com.sec.android.app.launcher" content-desc="語音搜索" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[774,99][894,207]" />
                    </node>
                    <node index="2" text="" resource-id="com.sec.android.app.launcher:id/apps_search_divider" class="android.view.View" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[915,129][918,177]" />
                    <node index="3" text="" resource-id="com.sec.android.app.launcher:id/more_button_search" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[939,117][1011,189]">
                      <node index="0" text="" resource-id="com.sec.android.app.launcher:id/more_icon_imageview" class="android.widget.ImageButton" package="com.sec.android.app.launcher" content-desc="更多選項" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[939,117][1011,189]" />
                    </node>
                  </node>
                  <node index="1" text="" resource-id="" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[60,204][1020,207]" />
                </node>
              </node>
            </node>
            <node index="2" text="" resource-id="com.sec.android.app.launcher:id/apps_page_indicator" class="android.widget.LinearLayout" package="com.sec.android.app.launcher" content-desc="第 4 頁,共 4 頁。" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[424,1821][655,1872]">
              <node NAF="true" index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[424,1821][475,1872]">
                <node index="0" text="" resource-id="com.sec.android.app.launcher:id/inactive" class="android.widget.ImageView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[424,1821][475,1872]" />
              </node>
              <node NAF="true" index="1" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[484,1821][535,1872]">
                <node index="0" text="" resource-id="com.sec.android.app.launcher:id/inactive" class="android.widget.ImageView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[484,1821][535,1872]" />
              </node>
              <node NAF="true" index="2" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[544,1821][595,1872]">
                <node index="0" text="" resource-id="com.sec.android.app.launcher:id/inactive" class="android.widget.ImageView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[544,1821][595,1872]" />
              </node>
              <node NAF="true" index="3" text="" resource-id="" class="android.widget.FrameLayout" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[604,1821][655,1872]">
                <node index="1" text="" resource-id="com.sec.android.app.launcher:id/active" class="android.widget.ImageView" package="com.sec.android.app.launcher" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[604,1821][655,1872]" />
              </node>
            </node>
          </node>
        </node>
      </node>
    </node>
  </node>
</hierarchy>

判斷已經滑動到頁面底部

我們可以利用 dump_hierarchy() 獲取界面UI結構,那么需要怎么判斷當前是否已滑動到底了呢?其實很簡單,我們只需要判斷每次滑動前的UI結構信息,與滑動后的UI結構信息一致,那么就可以看作是滑動到底部了。

import uiautomator2 as u2
import time

d = u2.connect()

while True:
    # 獲取每次滑動前頁面下半部分的所有元素
    page_content = d.dump_hierarchy()[(len(d.dump_hierarchy()) // 2):]
    d.swipe_ext("up")
    time.sleep(0.5)
    # 獲取每次滑動后頁面下半部分的所有元素,並與上一次滑動前的頁面元素對比,頁面元素沒有變化時跳出循環
    new_page_content = d.dump_hierarchy()[(len(d.dump_hierarchy()) // 2):]
    if new_page_content == page_content:
        break

print("swipe end")


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM