作者:Ken.He
由于項目需要,需要對各個app視窗中的控件進行滑動,并截圖,最後拼接。其核心難點就在于滑動,包括它滑動的判斷,滑動的距離計算等等。截圖與拼接部分這裡不詳述。
計算滑動的距離,原理其實很簡單,就是: 滑動後的位置(終點)- 滑動前的位置(起點)= 滑動距離。
這裡介紹一下如何計算RecyclerView與ListView實際滑動距離(目前僅限由下向上滑動一種情況)。
一、RecyclerView:
RecyclerView 作為一個新興的清單控件,是Android L版本中新添加的一個用來取代ListView的SDK,它的靈活性與可替代性比listview更好。RecyclerView與ListView原理是類似的:都是僅僅維護少量的View并且可以展示大量的資料集。RecyclerView用以下兩種方式簡化了資料的展示和處理:
a.使用LayoutManager來确定每一個item的排列方式。
b.為增加和删除項目提供預設的動畫效果。
既然item都交給了LayoutManager來管理,那我們就從它入手。先來看一下需要用到的幾個方法:
findFirstVisibleItemPosition();//擷取顯示在螢幕上可見的第一個子View在清單上對應的position
findLastVisibleItemPosition();//擷取顯示在螢幕上可見的最後一個子View在清單上對應的position
findViewByPosition(int position);//通過已知的子view的position來擷取子view
接下來,我們來了解一下計算過程,首先使用變量mLastVisibleItemPosition記錄下目前狀态下,最後一個子View的position來做為辨別,然後通過findViewByPosition(int position);得到該子view,使用view.getTop()擷取到該子View距離父視圖頂部的距離并使用mLastVisibleItemTopValue記錄下來。滑動後,判斷mLastVisibleItemPosition是否大于初始值,如果是,則通過findViewByPosition(int position)方法擷取之前記錄的子view, 并取該子view目前的top值,計算mLastVisibleItemTopValue與目前top的內插補點,即本次滑動距離。以下附上代碼(由于framework無法調用support包,是以隻能通過反射來取得其方法,自定義RecyclerViewUtil):
privateint mLastVisibleItemPosition = 0;
privateint mLastVisibleItemTopValue = 0;
private intcomputeRecyclerViewScrollDistance(View recyclerView) {
intdistance = 0;
//if mLastVisibleItemPosition > 0 , mLastVisibleItemPosition's value is valid
if(mLastVisibleItemPosition> 0){
//get top value after scroll
ObjectoldPositionItem = RecyclerViewUtil.findViewByPosition(recyclerView,mLastVisibleItemPosition);
if(oldPositionItem!= null){
distance= mLastVisibleItemTopValue - ((View)oldPositionItem).getTop();
}
}
//record current position of last visible item
Objectobj = RecyclerViewUtil.findLastVisibleItemPosition(recyclerView);
if(obj!= null){
mLastVisibleItemPosition= ((Integer)obj).intValue();
}else{
mLastVisibleItemPosition= 0;
}
ObjectnewPositionItem = RecyclerViewUtil.findViewByPosition(recyclerView,mLastVisibleItemPosition);
if(newPositionItem!= null){
mLastVisibleItemTopValue= ((View)newPositionItem).getTop();
}
returndistance;
}
二、ListView:
ListView作為老牌勢力,還是占有重要的地位,至少,實作一些對稱性的清單,listView效率顯得更高。閑話少說,直奔主題,我們要計算ListView的滑動距離,就必須先提一下ListView的滑動方式scrollListBy(int value), 如果使用scrollBy(intvalue)或者scrollTo(int x,int y)方法,listView不會滑動。
ListView滑動距離的計算原理也是一樣,利用getFirstVisibleItemPosition()和getLastVisiblePosition(),getChildAt(int position)以及view.getTop()方法,值得一提的是,這裡的position是ListView的子View的position,并非是所有子項,這是有差別的,我們都知道ListView的視圖與資料dataItem是分離的,并非一一對應,是以我們需要用到getFirstVisibleItemPosition()這個方法,然後在擷取滑動後作為标記的子View時,使用getChildAt(mLastVisibleItemPosition –firstVisiblePosition)方法,才能準确擷取到滑動後的子View,然後取其top值與滑動前的top值進行計算,得出的結果就是滑動的距離。
private int computeListViewScrollDistance(View listView){
if(listView == null){
return 0;
}
int distance = 0;
// if mLastVisibleItemPosition > 0 ,mLastVisibleItemPosition's value is valid
int firstVisiblePosition =((AbsListView)listView).getFirstVisiblePosition();
if(mLastVisibleItemPosition > 0){
// get top value after scroll
View oldPositionItem = ((AbsListView)listView).getChildAt(mLastVisibleItemPosition-firstVisiblePosition);
Log.e(TAG,"oldPositionItem="+oldPositionItem);
if(oldPositionItem != null){
distance = mLastVisibleItemTopValue -oldPositionItem.getTop();
Log.d(TAG, "---computeListViewScrollDistance---beforescroll:"+mLastVisibleItemTopValue+" afterscroll:"+oldPositionItem.getTop() +" distance:"+distance);
}
}
// record current position of last visible item
mLastVisibleItemPosition =((AbsListView)listView).getLastVisiblePosition();
View newPositionItem =((AbsListView)listView).getChildAt(mLastVisibleItemPosition-firstVisiblePosition);
Log.e(TAG,"newPositionItem="+newPositionItem);
if(newPositionItem != null){
mLastVisibleItemTopValue = newPositionItem.getTop();
}
Log.d(TAG,"---computeListViewScrollDistance---mLastVisibleItemPosition="+mLastVisibleItemPosition+" mLastVisibleItemTopValue="+mLastVisibleItemTopValue +"distance="+distance);
return distance;
}
補充:ScrollView可以直接使用其唯一的子View,然後利用view.getY(),滑動前與滑動後的值進行計算得出,但是現在單純的ScrollView線上上幾乎不可見。
總結:線性容器可以通過該原理進行計算,而固定式容器(如WebView、FrameLayout)則不适用該方法。