原文鏈接:http://www.cnblogs.com/by-dream/p/4921701.html#3328376
以一個簡單的例子開始吧。我們完成一個 " 打開QQ,進入QQ空間,然后退出 " 的case。
代碼如下:
package QQ;
import java.io.IOException;
import com.android.uiautomator.core.UiDevice;
import com.android.uiautomator.core.UiObject;
import com.android.uiautomator.core.UiObjectNotFoundException;
import com.android.uiautomator.core.UiSelector;
import com.android.uiautomator.testrunner.UiAutomatorTestCase;
public class Test_qq extends UiAutomatorTestCase
{
public void testDemo() throws IOException, UiObjectNotFoundException {
// 啟應用
Runtime.getRuntime().exec("am start com.tencent.mobileqq/com.tencent.mobileqq.activity.SplashActivity");
sleep(3000);
// 點擊 "動態" tab
UiDevice device = getUiDevice();
int height = device.getDisplayHeight();
int width = device.getDisplayWidth();
device.click(width -50, height-50);
sleep(1000);
// 點擊 "好友動態" 按鈕
UiObject obj_1 = new UiObject(new UiSelector().description("點擊進入好友動態"));
obj_1.click();
sleep(2000);
// 點擊 左上角返回 "動態"按鈕
UiObject obj_2 = new UiObject(new UiSelector().resourceId("com.tencent.mobileqq:id/ivTitleBtnLeft"));
obj_2.click();
sleep(1000);
// 點擊菜單鍵
device.pressMenu();
sleep(1000);
// 點擊退出qq
UiObject obj_3 = new UiObject(new UiSelector().text("退出QQ"));
obj_3.click();
sleep(1000);
// 點擊確定
UiObject obj_4 = new UiObject(new UiSelector().text("確定"));
obj_4.click();
}
}
腳本的運行效果如下:

代碼詳解
針對上面的例子的代碼,我對每一句代碼都做個詳細的解釋吧。
第一部分:啟動應用

exec() 這個函數的意思,相當於是在你在輸入adb shell 命令后,在Android手機系統的命令行下運行。所以上面這句話的意思和我們打開cmd框輸入" adb shell am start *** " 是一樣的的效果。
一般來說我們做App的自動化的時候,第一步都是把App打開,這個am start命令的就可以幫我們實現,類似與Monkeyrunner API中的startActivity() 函數。
第二部分:點擊 “動態” tab
UiDevice對象會在API部分詳細講解,它是一個我們在Uiautomator中經常使用的一個對象。
這里我們首先用它獲取到當前手機的寬和高的像素。然后觀察到 “動態” tab位於右下方,因此在取得右下角的坐標點后,又進行了一個大概的坐標變化(這里為了簡單只是向左和向上移動了50像素,如果要精確的可以進行等比轉化),然后點擊該坐標。
這里之所以用點擊坐標的方法,一方面是因為這個控件Uiautomator不支持用API獲得實例(上一節所說的NAF Nodes,如下圖),另一方面也是想說明在一些控件沒有固定的id、text和desc的時候,我們應該怎么處理。

第三部分:點擊 “好友動態”
要想操作一個控件(例如),首先得獲得一個UiObject對象,而UiObject對象可以通過UiSelector來構造,而UiSelector可以根據控件的id、text、content-desc來進行構造,這里就是用content-desc來構造。

如上圖用 uiautomatorviewer 查到該控件的 content-desc 的內容是 “點擊進入好友動態” ,因此我們就可以通過代碼中的方法來得到UiObject對象了,然后調用click() 方法來達到點擊效果。
第四部分:點擊左上角返回按鈕


同第三部分的方法,找到id后直接獲得到UiObject對象,進行點擊。
第五部分:點擊菜單鍵

UiDevice 可以模擬點擊home、back、menu 這三個鍵,代碼應該大家都懂的怎么變化了吧。
第六部分:退出

這一部分也是先通過獲取出控件屬性中的text值,然后構造出UiObject對象,完成點擊。
以上部分內容就是整個操作QQ這個小例子的全部代碼講解,看完之后對寫Uiautomator代碼有了更進一步的了解了吧。接下來寫看看還有哪些API可以支持我們做更多的事情。
API 列舉
UiDevice
概述:
UiDevice用與訪問關設備狀態的信息,也可以使用這個類來模擬用戶在設備上的操作。可以通過下面的方法得到實例:
UiDevice mdevice = getUiDevice();
摘要:
| 函數返回值 | 函數體 | 說明 | 實例 |
| boolean | click(int x, int y) | 模擬用戶在指定位置點擊 | mdevice.click(200, 300) 點擊屏幕的200,300坐標處 |
| String | getCurrentActivityName() | 獲得的是應用程序在桌面上顯示的名字 | 例如,在qq首頁得到的是“QQ”,在微信登錄頁得到的是“微信”,注意,這個得到的不是Activity的名字 |
| String | getCurrentPackageName() | 獲得當前顯示的應用程序的包名 | 例如,在微信啟動的時候,獲得的是“com.tencent.mm” |
| int | getDisplayHeight() | 獲得當前設備的屏幕分辨率的高 | 例如,我的手機1920*1080,得到的是 1920 |
| int | getDisplayWighth() | 獲得當前設備的屏幕分辨率的寬 | 例如,我的手機1920*1080,得到的是 1080 |
| boolean | isScreenOn() | 判斷手機當前是否滅屏 | 當手機滅屏的時候,得到是“false”,手機亮屏,得到的是“true” |
| void | wakeUp() | 點亮當前屏幕 | 調用后,相當於按下了電源鍵,如果手機設置了滑動鎖屏,滑動鎖屏還是在的,不會自動解開 |
| boolean | pressBack() | 點擊back鍵 | |
| boolean | pressHome() | 點擊home鍵 | |
| boolean | pressMenu() | 點擊menu鍵 | |
| boolean | pressKeyCode(int keyCode) | 利用keycode值模擬一次按下事件 | 例如,需要按下數字1 數字1的keycode是 KEYCODE_NUMPAD_1,更多keycode可以在 http://developer.android.com/intl/zh-cn/reference/android/view/KeyEvent.html 進行查詢 |
| boolean | swipe(int startX, int startY, int endX, int endY, int steps) | 用指定的步長,從A點滑動B點 | 例如,需要從(10, 10)點用兩步滑動到(100, 200)點,則需要mdevice.swipe(10, 10, 100, 200, 2) |
| boolean | takeScreenshot(File storePath) | 截取當前屏幕,保存到文件 | 例如,File files = new File("/sdcard/res.jpg"); mdevice.takeScreenshot(files); 即可將截圖保存到sd卡中了。 |
UiSelector
概述:
按照一定的條件(例如控件的text值,資源id),定位界面上的元素。UiSelector對象的最終目的是去構造一個UiObject對象。
摘要:
1、根據text構造:
| 函數返回值 | 函數體 | 說明 | 用法 |
| UiSelector | text(String text) | 根據“控件text屬性的內容”構造出UiSelector對象 | 例如,一個控件text的值是“發現”,UiSelector s = new UiSelector().text("發現"); |
| UiSelector | textContains(String text) | 根據“控件text屬性包含的內容”構造出UiSelector對象 | 同上例子:UiSelector s = new UiSelector().textContains("現"); |
| UiSelector | textMatches(String regex) | 根據“控件text屬性正則表達式的內容”構造出UiSelector對象 | 正則表達式語法參考網上資料即可。 |
| UiSelector | textStartsWith(String text) | 根據“控件text屬性開始的內容”構造出UiSelector對象 | 同上例子:UiSelector s = new UiSelector().textStartsWith("發"); |
比較常用,准確度也比較高,中文查找的時候,如果遇到 “UiOjbectNotFoundException” 的時候,記得把項目的編碼格式改為utf-8。
2、根據description構造:
| UiSelector | description(String desc) | 根據“控件content-desc屬性的內容”構造出UiSelector對象 |
| UiSelector | descriptionContains(String desc) | 包含** |
| UiSelector | descriptionMatches(String regex) | 正則 |
| UiSelector | descriptionStartsWith(String desc) | 以**開始 |
同text的用法基本一致,也是比較靠譜的一種方式。
3、根據資源id:
| UiSelector | resourceId(String id) | 根據資源id獲取對象,例如:UiSelector s = new UiSelector().resourceId("com.tencent.mm:id/b8m") |
| UiSelector | resourceIdMatches(String regex) | 根據資源id的正則表達式獲取對象 |
4、根據類:
UiSelector className(String className):
根據控件的類名來找到UiSelector對象。
但是呢?因為一般Android布局的時候,同樣的控件類名都是一樣的。
因此我在微信的登錄界面調用: UiSelector s = new UiSelector().className("android.widget.TextView") 這句話,它得到的就是我左上開始算第一個class名稱為“android.widget.TextView”的控件。
UiSelector instance (int instance):
上面提到的假如我們想獲取屏幕上電話號碼的那個TextView使用這樣方法,就可以使用instance:
UiSelector s = new UiSelector().className("android.widget.TextView").instance(1);
UiSelector index(int index):
用法和上面的instance差不多,谷歌的原文說這個方法是unreliable的,推薦使用instance方法。
UiSelector childSelector(UiSelector selector):
有的時候假如子控件不好獲得,而其父控件比較好獲得的時候,我們通常采用這樣的方式,例如下面:

我們目前選中的是LinearLayout,這個Android中的一種布局,它的里面嵌套了兩個控件,一個是ImageView,另一個是EditText。這們這里就通過LinearLayout這個控件找到它的子控件。
很明顯,父控件id已經給定。我們先得到父控件:UiSelector s_p = new UiSelector().resourceId("com.tencent.mm:id/axj");
其次 UiSelector s_c= s_p.childSelector( new UiSelector().className("android.widget.EditText") );
在它的父控件的childSelector方法中傳入一個帶有一定特征的UiSelector對象,即可得到子控件,這里 s_c 就是輸入框的UiSelector對象。
UiSelector fromParent(UiSelector selector):
有的時候父控件也不好獲得,而是同級的控件(同屬一個parent)比較好獲取,那么使用這樣方法,還拿上面的舉例:
我們先得到EditText的UiSelector對象:UiSelector s1 = new UiSelector().resourceId("com.tencent.mm:id/axc");
得到和它同樣一個父控件的ImageView的UiSelector對象:UiSelector s2 = fromParent( new UiSelector().className("android.widget.ImageView") );
5、根據特有屬性:
| UiSelector | checked(boolean val) | 根據是否可check來構造出UiSelector對象 |
| UiSelector | chickable(boolean val) | |
| UiSelector | enabled(boolean val) | |
| UiSelector | focusable(boolean val) | |
| UiSelector | longClickable(boolean val) | |
| UiSelector | scrollable(boolean val) | |
| UiSelector | selected(boolean val) |
舉個簡單的例如,假如當前的界面,只有一個checkbox是勾選狀態,你就可以這樣得到:UiSelector s2 = new UiSelector().checked(true)
UiCollection
概述: 用的不多,直接參考文檔
摘要: http://android.toolib.NET/tools/help/uiautomator/UiCollection.html
UiScrollable
概述: 用的不多,直接參考文檔
摘要: http://android.toolib.net/tools/help/uiautomator/UiScrollable.html
UiObject
概述:可以理解為 直接操作界面ui元素的實例。
摘要:
| 返回值 |
函數 |
| void |
ClearTextField() |
| boolean |
click() |
| boolean |
clickAndWaiForNewWindow(long timeout) |
| boolean |
clickAndWaiForNewWindow() |
| boolean |
clickBottomRight() |
| boolean |
clickTopLeft() |
| boolean |
exists() |
| Rect |
getBounds() |
| UiObject |
getChild(UiSelector selector) |
| int |
getChildCount() |
| String |
getContentDescription() |
| UiObject |
getFromParent(UiSelector selector) |
| String |
getPackageName() |
| final UiSelector |
getSelector() |
| String |
getText() |
| Rect |
getVisibleBounds() |
| boolean |
isCheckable() |
| boolean |
isChecked() |
| boolean |
isClickable() |
| boolean |
isEnabled() |
| boolean |
|
| boolean |
isFocused() |
| boolean |
isLongClickable() |
| boolean |
isScrollable() |
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |
|
| boolean |

