在之前的控件搜索原理解析的文章中提到了,robotium在搜索控件時具有自動滾動的功能,接着就來了解一下其滾動是怎麼實現的。滾動的功能實現在主要在Scroller.java中(吐槽一下,爲什麼Robotium的類名都起的清晰易懂,而代碼裏的變量名則經常文不對題呢??),主要入口是public boolean scroll(int direction, boolean allTheWay),裏面會根據所需滾動的View類型——ListView, ScrollView以及WebView,分別採取三種不同的操作, 本文先了解一下ListView的滾動。
AbsListView的滾動
滾動原理很簡單,直接利用view.setSelection(int positon)(不用擔心會觸發點擊事件,查看ListView或GridView API文檔的說明,其中有這樣一句, If in touch mode, the item will not be selected but it will still be positioned appropriately. )
不過雖然結果很簡單,但過程還是有些曲折的,接着看public <T extends AbsListView> boolean scrollList(T absListView, int direction, boolean allTheWay) 這個函數,以向下爲例,跟蹤一下滾動流程。
- public <T extends AbsListView> boolean scrollList(T absListView, int direction, boolean allTheWay) {
- if(absListView == null){
- return false;
- }
- if (direction == DOWN) {
- //向下滾動
- if (allTheWay) {
- //allTheWay表示直接滾到底
- scrollListToLine(absListView, absListView.getCount()-1);
- return false;
- }
- if (absListView.getLastVisiblePosition() >= absListView.getCount()-1) {
- //列表已經處在底部
- scrollListToLine(absListView, absListView.getLastVisiblePosition());
- return false;
- }
- if(absListView.getFirstVisiblePosition() != absListView.getLastVisiblePosition())
- //列表的顯示寬度大於1
- scrollListToLine(absListView, absListView.getLastVisiblePosition());
- else
- //列表的顯示寬度等於1,則要滾動至FirstVisiblePosition()+1
- scrollListToLine(absListView, absListView.getFirstVisiblePosition()+1);
- } else if (direction == UP) {
- if (allTheWay || absListView.getFirstVisiblePosition() < 2) {
- //allTheWay或列表長度小於2,則直接滾到頂部
- scrollListToLine(absListView, 0);
- return false;
- }
- //計算列表的顯示寬度
- final int lines = absListView.getLastVisiblePosition() - absListView.getFirstVisiblePosition();
- //根據寬度計算出該滾動至第幾行
- int lineToScrollTo = absListView.getFirstVisiblePosition() - lines;
- if(lineToScrollTo == absListView.getLastVisiblePosition())
- //當顯示寬度爲1時,向上回滾一行即可
- lineToScrollTo--;
- if(lineToScrollTo < 0)
- //當上面的隱藏行數小於顯示寬度時,直接滾動至第一行即可
- lineToScrollTo = 0;
- scrollListToLine(absListView, lineToScrollTo);
- }
- sleeper.sleep();
- return true;
- }
- public <T extends AbsListView> void scrollListToLine(final T view, final int line){
- if(view == null)
- Assert.fail("AbsListView is null!");
- final int lineToMoveTo;
- if(view instanceof GridView)
- //如果是GridView的話再+1
- //當向下滾動時,如果只滾動至latsVisiblePositon,顯然會重複顯示一個元素,
- //而且當postion > GridView.getCount - 1時,調用gridView.setSelection,gridView直接滾動到底部。
- lineToMoveTo = line+1;
- else
- lineToMoveTo = line;
- inst.runOnMainSync(new Runnable(){
- public void run(){
- //通過setSelection完成列表的自動滾動。
- view.setSelection(lineToMoveTo);
- }
- });
對ListView比較瞭解的話,這裏的代碼應該很簡單。不過有一個地方不明白,就是scrollListToLine對GridView的lineToMoveTo加了一。目前經過查看API文檔和自己做得實驗,找到+1的理由只有如下兩點:
1) 當方向向下且列表顯示寬度大於1時,加1可以避免lastVisiblePositon元素的重複顯示
2) 當postion > view.size() – 1時,setSelection(postion) 會導致listView會滾動至頂部,而GridView會滾動至底部。
所以當滾動方向向下且列表寬度超過1時,此GridView的+1是能正常工作的。
不過,在如下情況下這個+1應該是會出錯的:
1) 當方向向下且列表寬度爲1時,此時在第一層searchFor時已經加了1,而scrollListToLine這裏再給GridView加了1,那麼如果原本的lastVisiblePosition爲26的話,經過這樣處理,會直接滾動到28,而27行就被直接跳過了(經過親自驗證,此時確實會出現問題)。
2) 方向向上時,這個+1就更加無厘頭了,例如當用Solo.scrollToTop()時,gridView會滾到position = 1;更例如當列表寬度爲1時,調用Solo.scrollUp(),你會發現gridView在原地踏步(兩條都驗證完畢)。
ScrollView的滾動
相對比AbsListView,ScrollView的滾動就簡單多了,直接上代碼
- private boolean scrollScrollView(final ScrollView view, int direction){
- if(view == null){
- return false;
- }
- //獲取控件高度
- int height = view.getHeight();
- //滾動量減一,爲了容錯性?
- height--;
- int scrollTo = -1;
- //向下滾動,滾動量爲正值
- if (direction == DOWN) {
- scrollTo = height;
- }
- //向下滾動,滾動量爲負
- else if (direction == UP) {
- scrollTo = -height;
- }
- //記錄當前控件處於滾動框頂部的位置
- int originalY = view.getScrollY();
- final int scrollAmount = scrollTo;
- inst.runOnMainSync(new Runnable(){
- public void run(){
- //滾動
- view.scrollBy(0, scrollAmount);
- }
- });
- //對比滾動前後的位置,如果一樣,說明已到達底部,返回false,否則返回true
- if (originalY == view.getScrollY()) {
- return false;
- }
- else{
- return true;
- }
- }
WebView的滾動
更加簡單,不多說了
- public boolean scrollWebView(final WebView webView, int direction, final boolean allTheWay){
- //滾動webView時,直接調用webView.pageDown()或
- //webView.pageUp()
- if (direction == DOWN) {
- inst.runOnMainSync(new Runnable(){
- public void run(){
- canScroll = webView.pageDown(allTheWay);
- }
- });
- }
- if(direction == UP){
- inst.runOnMainSync(new Runnable(){
- public void run(){
- canScroll = webView.pageUp(allTheWay);
- }
- });
- }
- return canScroll;
- }