天天看點

關于RecyclerView與ListView控件滑動距離的計算方法

作者: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)則不适用該方法。

繼續閱讀