作者: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;
/**
* add a method for compute scroll distance byrecyclerView
*/
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值进行计算,得出的结果就是滑动的距离。
/**
* add a method forcompute scroll distance by ListView
*/
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)则不适用该方法。