溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

發(fā)布時(shí)間:2020-07-30 00:19:14 來(lái)源:網(wǎng)絡(luò) 閱讀:276 作者:zhukev 欄目:移動(dòng)開發(fā)

1. 背景

在使用MonkeyRunner的時(shí)候我們經(jīng)常會(huì)用到Chimchat下面的HierarchyViewer模塊來(lái)獲取目標(biāo)控件的一些信息來(lái)輔助我們測(cè)試,但在MonkeyRunner的官網(wǎng)上是沒有看到相應(yīng)的API的描述的,上面只有以下三個(gè)類的API引用信息(http://developer.android.com/tools/help/MonkeyDevice.html)

  • MonkeyDevice
  • MonkeyImage
  • MonkeyRunnerMonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
所以在這里嘗試整理下HierarchyViewer提供的API的用法并根據(jù)實(shí)踐作出相應(yīng)的建議,首先請(qǐng)看該類提供的所有可用的公共方法,內(nèi)容并不多:
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

從圖中可以看出HierarchyViewer類中提供的方法主要是用來(lái)定位控件相關(guān)的,包括根據(jù)ID取得控件,根據(jù)控件取得控件在屏幕的位置等。但還有一些其他方法,我們會(huì)順帶一并描述,畢竟內(nèi)容并不多。
本文我們依然跟上幾篇文章一樣以SDK自帶的NotePad為實(shí)驗(yàn)?zāi)繕?biāo),看怎么定位到NotesList下面的Menu Options中的Add note這個(gè)Menu Entry。
以下是通過HierarchyViewer這個(gè)工具獲得的目標(biāo)設(shè)備界面的截圖:MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

2.findViewById(String id)

2.1 示例

targetDevice = MonkeyRunner.waitForConnection() '''      public ViewNode findViewById(String id)       * @param id id for the view.      * @return view with the specified ID, or {@code null} if no view found. ''' viewer = targetDevice.getHierarchyViewer() button = viewer.findViewById('id/title') text = viewer.getText(button) print text.encode('utf-8')
  

2.2 分析和建議

此API的目的就是通過控件的ID來(lái)獲得代表用戶控件的一個(gè)ViewNode對(duì)象。因?yàn)檫@個(gè)是第一個(gè)示例,所以這里有幾點(diǎn)需要說(shuō)明
  • 一旦MonkeyRunner連接上設(shè)備,會(huì)立刻獲得一個(gè)MonkeyDevice的對(duì)象代表了目標(biāo)測(cè)試設(shè)備,我們就是通過這個(gè)設(shè)備對(duì)象來(lái)控制設(shè)備的
  • 注意這里需要填寫的id的格式和UIAutomatorViewer獲得ResourceId是不一樣的,請(qǐng)看下圖UIAutomatorViewer截圖中ResourceId前面多出了"android:"字串:MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
  • 這個(gè)方法返回的一個(gè)ViewNode的對(duì)象,代表目標(biāo)控件,擁有大量控件相關(guān)的屬性,由于篇幅問題這里不詳述,往后應(yīng)該會(huì)另外撰文描述它的使用。在本文里知道它代表了目標(biāo)控件就行了
  • 最后打印的時(shí)候需要轉(zhuǎn)換成UTF-8編碼的原因跟Jython默認(rèn)的編碼格式有關(guān)系,具體描述和Workaround請(qǐng)查看:http://www.haogongju.net/art/1636997

3. findViewById(String id, ViewNode rootNode)

3.1示例

'''      public ViewNode findViewById(String id, ViewNode rootNode)       * Find a view by ID, starting from the given root node      * @param id ID of the view you're looking for      * @param rootNode the ViewNode at which to begin the traversal      * @return view with the specified ID, or {@code null} if no view found.       ''' iconMenuView = viewer.findViewById('id/icon_menu') button = viewer.findViewById('id/title',iconMenuView) print "Button Text:",text.encode('utf-8')

3.2分析

這個(gè)方法是上面方法的一個(gè)重載,除了需要指定ID之外,還需要指定一個(gè)rootNode,該rootNode指的就是已知控件的父控件,父到什么層級(jí)就沒有限制了。為什么需要這個(gè)方法了,我們可以想象下這種情況:同一界面上存在兩個(gè)控件擁有相同的ID,但是他們某一個(gè)層級(jí)父控件開始發(fā)生分叉。那么我們就可以把rootNode指定為該父控件(不包括)到目標(biāo)控件(不包含)路徑中的其中一個(gè)父控件來(lái)精確定位我們需要的目標(biāo)控件了。
如我們的示例就是明確指出我們需要的是在父控件“id/icon_menu"(請(qǐng)看背景的hierarchyviewer截圖)下面的那個(gè)”id/title"控件。

4 getAbsolutePositionOfView(ViewNode node)

4.1示例

'''     public static Point getAbsoluteCenterOfView(ViewNode node)      * Gets the absolute x/y center of the specified view node.      *      * @param node view node to find position of.      * @return absolute x/y center of the specified view node.      */ ''' point = viewer.getAbsoluteCenterOfView(button) print "Button Absolute Center Position:",point

4.2 分析和建議

這個(gè)API的目的是想定位一個(gè)已知ViewNode控件的左上角在屏幕上的絕對(duì)坐標(biāo)。對(duì)于我們正常的APP里面的控件,本人實(shí)踐過是沒有問題的。但是有一種情況要特別注意:這個(gè)對(duì)Menu Options下面的控件是無(wú)效的!
以上示例最后一段代碼的輸出是(3,18),其實(shí)這里不用想都知道這個(gè)不可能是相對(duì)屏幕左上角坐標(biāo)(0,0)的絕對(duì)坐標(biāo)值了,就偏移這一點(diǎn)點(diǎn)像素,你真的當(dāng)我的實(shí)驗(yàn)機(jī)器HTC Incredible S是可以植入腦袋的神器啊。
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
那么這個(gè)數(shù)據(jù)是如何獲得的呢?其實(shí)按照我的理解(真的只是我自己的理解,不對(duì)的話就指正吧,但請(qǐng)描述詳細(xì)點(diǎn)以供我參考),這個(gè)函數(shù)的定義應(yīng)該是“獲得從最上層的DecorView(具體DectorView的描述請(qǐng)查看我以前轉(zhuǎn)載的一篇文章《Android DecorView淺析》)左上角坐標(biāo)到目標(biāo)控件的的偏移坐標(biāo)”,只是這個(gè)最上層的DecorView的坐標(biāo)一般都是從(0,0)開始而已。如下圖我認(rèn)為最上面的那個(gè)FrameLayout就代表了DecorView,或者說(shuō)整個(gè)窗體
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
那么在假設(shè)我的觀點(diǎn)是對(duì)的情況下,這個(gè)就很好解析了,請(qǐng)看Menu Option的最上層FrameLayout的絕對(duì)坐標(biāo)是(0,683)
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
而Add note的絕對(duì)坐標(biāo)是(3,701)
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

兩者一相減就是和我們的輸出結(jié)果絕對(duì)吻合的(3,18)了。

5. getAbsoluteCenterOfView(ViewNode node)

5.1 示例

'''     public static Point getAbsoluteCenterOfView(ViewNode node)      * Gets the absolute x/y center of the specified view node.      *      * @param node view node to find position of.      * @return absolute x/y center of the specified view node.      */ ''' point = viewer.getAbsoluteCenterOfView(button) print "Button Absolute Center Position:",point 

5.2 分析和建議

這個(gè)方法的目的是獲得目標(biāo)ViewNode控件的中間點(diǎn)的絕對(duì)坐標(biāo)值,但是對(duì)Menu Options下面的控件同樣不適用,具體請(qǐng)查看第3章節(jié)。

以下兩個(gè)方法都不是用來(lái)定位控件的,一并記錄下來(lái)以供參考。

6. getFocusedWindowName()

6.1 示例

'''     public String getFocusedWindowName()      * Gets the window that currently receives the focus.      *      * @return name of the window that currently receives the focus. ''' window = viewer.getFocusedWindowName() print "Window Name:",window.encode('utf-8')

6.2 解析

其實(shí)就是獲得當(dāng)前打開的窗口的packageName/activityName,輸出與HierarchyViewer工具檢測(cè)到的信息一致,所以猜想其用到同樣的方法。

輸出:

MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

HierarchyViewer監(jiān)控信息:
MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

7. visible(ViewNode node)

7.1 示例

'''     public boolean visible(ViewNode node)       * Gets the visibility of a given element.      * @param selector selector for the view.      * @return True if the element is visible. ''' isVisible = viewer.visible(button) print "is visible:",isVisible
就是查看下控件是否可見,沒什么好解析的了。

8. 測(cè)試代碼

from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice from com.android.monkeyrunner.easy import EasyMonkeyDevice,By from com.android.chimpchat.hierarchyviewer import HierarchyViewer from com.android.hierarchyviewerlib.models import ViewNode, Window from java.awt import Point  #from com.android.hierarchyviewerlib.device import   #Connect to the target targetDevice targetDevice = MonkeyRunner.waitForConnection()  easy_device = EasyMonkeyDevice(targetDevice)  #touch a button by id would need this targetDevice.startActivity(component="com.example.android.notepad/com.example.android.notepad.NotesList")  #time.sleep(2000) #invoke the menu options MonkeyRunner.sleep(6) targetDevice.press('KEYCODE_MENU', MonkeyDevice.DOWN_AND_UP);  '''      public ViewNode findViewById(String id)       * @param id id for the view.      * @return view with the specified ID, or {@code null} if no view found. ''' viewer = targetDevice.getHierarchyViewer() button = viewer.findViewById('id/title') text = viewer.getText(button) print text.encode('utf-8')   '''      public ViewNode findViewById(String id, ViewNode rootNode)       * Find a view by ID, starting from the given root node      * @param id ID of the view you're looking for      * @param rootNode the ViewNode at which to begin the traversal      * @return view with the specified ID, or {@code null} if no view found.       ''' iconMenuView = viewer.findViewById('id/icon_menu') button = viewer.findViewById('id/title',iconMenuView) print "Button Text:",text.encode('utf-8')  '''     public String getFocusedWindowName()      * Gets the window that currently receives the focus.      *      * @return name of the window that currently receives the focus. ''' window = viewer.getFocusedWindowName() print "Window Name:",window.encode('utf-8')  '''     public static Point getAbsoluteCenterOfView(ViewNode node)      * Gets the absolute x/y center of the specified view node.      *      * @param node view node to find position of.      * @return absolute x/y center of the specified view node.      */ ''' point = viewer.getAbsoluteCenterOfView(button) print "Button Absolute Center Position:",point  '''     public static Point getAbsolutePositionOfView(ViewNode node)      * Gets the absolute x/y position of the view node.      *      * @param node view node to find position of.      * @return point specifying the x/y position of the node. ''' point = viewer.getAbsolutePositionOfView(button) print "Button Absolute Position:", point  '''     public boolean visible(ViewNode node)       * Gets the visibility of a given element.      * @param selector selector for the view.      * @return True if the element is visible. ''' isVisible = viewer.visible(button) print "is visible:",isVisible

9.附上HierarchyViewer類的源碼方便參照

/*  * Copyright (C) 2011 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package com.android.chimpchat.hierarchyviewer; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import com.android.hierarchyviewerlib.device.DeviceBridge; import com.android.hierarchyviewerlib.device.ViewServerDevice; import com.android.hierarchyviewerlib.models.ViewNode; import com.android.hierarchyviewerlib.models.Window; import org.eclipse.swt.graphics.Point; /**  * Class for querying the view hierarchy of the device.  */ public class HierarchyViewer {     public static final String TAG = "hierarchyviewer";     private IDevice mDevice;     /**      * Constructs the hierarchy viewer for the specified device.      *      * @param device The Android device to connect to.      */     public HierarchyViewer(IDevice device) {         this.mDevice = device;         setupViewServer();     }     private void setupViewServer() {         DeviceBridge.setupDeviceForward(mDevice);         if (!DeviceBridge.isViewServerRunning(mDevice)) {             if (!DeviceBridge.startViewServer(mDevice)) {                 // TODO: Get rid of this delay.                 try {                     Thread.sleep(2000);                 } catch (InterruptedException e) {                 }                 if (!DeviceBridge.startViewServer(mDevice)) {                     Log.e(TAG, "Unable to debug device " + mDevice);                     throw new RuntimeException("Could not connect to the view server");                 }                 return;             }         }         DeviceBridge.loadViewServerInfo(mDevice);     }     /**      * Find a view by id.      *      * @param id id for the view.      * @return view with the specified ID, or {@code null} if no view found.      */     public ViewNode findViewById(String id) {         ViewNode rootNode = DeviceBridge.loadWindowData(                 new Window(new ViewServerDevice(mDevice), "", 0xffffffff));         if (rootNode == null) {             throw new RuntimeException("Could not dump view");         }         return findViewById(id, rootNode);     }     /**      * Find a view by ID, starting from the given root node      * @param id ID of the view you're looking for      * @param rootNode the ViewNode at which to begin the traversal      * @return view with the specified ID, or {@code null} if no view found.      */     public ViewNode findViewById(String id, ViewNode rootNode) {         if (rootNode.id.equals(id)) {             return rootNode;         }         for (ViewNode child : rootNode.children) {             ViewNode found = findViewById(id,child);             if (found != null) {                 return found;             }         }         return null;     }     /**      * Gets the window that currently receives the focus.      *      * @return name of the window that currently receives the focus.      */     public String getFocusedWindowName() {         int id = DeviceBridge.getFocusedWindow(mDevice);         Window[] windows = DeviceBridge.loadWindows(new ViewServerDevice(mDevice), mDevice);         for (Window w : windows) {             if (w.getHashCode() == id)                 return w.getTitle();         }         return null;     }     /**      * Gets the absolute x/y position of the view node.      *      * @param node view node to find position of.      * @return point specifying the x/y position of the node.      */     public static Point getAbsolutePositionOfView(ViewNode node) {         int x = node.left;         int y = node.top;         ViewNode p = node.parent;         while (p != null) {             x += p.left - p.scrollX;             y += p.top - p.scrollY;             p = p.parent;         }         return new Point(x, y);     }     /**      * Gets the absolute x/y center of the specified view node.      *      * @param node view node to find position of.      * @return absolute x/y center of the specified view node.      */     public static Point getAbsoluteCenterOfView(ViewNode node) {         Point point = getAbsolutePositionOfView(node);         return new Point(                 point.x + (node.width / 2), point.y + (node.height / 2));     }     /**      * Gets the visibility of a given element.      *      * @param selector selector for the view.      * @return True if the element is visible.      */     public boolean visible(ViewNode node) {         boolean ret = (node != null)                 && node.namedProperties.containsKey("getVisibility()")                 && "VISIBLE".equalsIgnoreCase(                         node.namedProperties.get("getVisibility()").value);         return ret;     }     /**      * Gets the text of a given element.      *      * @param selector selector for the view.      * @return the text of the given element.      */     public String getText(ViewNode node) {         if (node == null) {             throw new RuntimeException("Node not found");         }         ViewNode.Property textProperty = node.namedProperties.get("text:mText");         if (textProperty == null) {             // give it another chance, ICS ViewServer returns mText             textProperty = node.namedProperties.get("mText");             if (textProperty == null) {                 throw new RuntimeException("No text property on node");             }         }         return textProperty.value;     } }

10. 參考閱讀

以下是之前不同框架的控件定位的實(shí)踐,一并列出來(lái)方便直接跳轉(zhuǎn)參考:
  • Robotium之Android控件定位實(shí)踐和建議(Appium/UIAutomator姊妹篇)
  • UIAutomator定位Android控件的方法實(shí)踐和建議(Appium姊妹篇)
  • Appium基于安卓的各種FindElement的控件定位方法實(shí)踐和建議


  • <strike id="kwaqi"></strike>
    <ul id="kwaqi"><tbody id="kwaqi"></tbody></ul>
  •  

    作者

    自主博客

    微信

    CSDN

    天地會(huì)珠海分舵

    http://techgogogo.com


    服務(wù)號(hào):TechGoGoGo

    掃描碼:

    MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI

    <samp id="kwaqi"></samp>