Android入門 在ListView中如何進行精確的定位


 

在android的開發中,經常會遇到需要主動去設定某條ListItem的位置的需求。設置位置的函數有

ListView.setSelection(int position)

ListView.setSelectionFromTop(int position, int y);

其中

position指的是指定的item的在ListView中的索引,注意如果有Header存在的情況下,索引是從Header就開始算的。

y指的是到ListView可見范圍內最上邊邊緣的距離。

函數有了,現在就是根據自身需求來進行設置。

 

這次遇到的需求,ListView要求是從下往上展示的,並且當Cursor更新時,要保持住原先的最上方的item(不包括header)的位置不變,然后新的歷史數據在原先那條item上方繼續向上展示。如圖:

ListView從下往上展示,也就是

[java]  view plain copy
 
  1. android:stackFromBottom="true"  
但是發現這一屬性的設置不會影響索引的排序順序,也就是item的索引都是從上往下遞增的,不會變成從下往上遞增。索引為0的item,都是在ListView的最上方的item(或header).

 

那么當Cursor更新時,原先第一條的索引便會發生變化。要想保持住它(圖中的 R)的位置。步驟如下:

(1)獲取這一條在新Cursor中的位置(posiition)

(2)獲取這一條在更換Cursor后ListView中的位置。

(4)由於ListView的可滾動的屬性,我們需要記錄更換Cursor前可視的第一條item的索引(ListView.getFirstVisiblePosition())

(3)區分FirstVisiblePosition是0和大於0的情況。由於header,也就是圖中的Loading那一條在新數據出來后是會消失的。

(4)當FirstVisiblePosition為0時實際指向的是header,我們要保持位置不變的是header下面第一條(R)的位置。那么此時要設置FirstVisiblePosition為1

(5)當FirstVisiblePosition大於0時實際指向的就是item,但是我們需要設置FirstVisiblePosition為0。*

(6)我們根據FirstVisiblePosition用ListView.getChildAt(int position)函數獲取對應的item的View,再根據View.getTop()函數獲取到ListView頂部的距離Y。

這樣ListView.setSelectionFromTop(int position, int y)所需的兩個參數 position 和 y就都有了。

*注解:ListView.getChildAt(int position), 這個position指的是在可視的item中的索引,跟cursor里的位置是大不一樣的。可以看看ListView.getChildCount()函數得到個數是小於或等於Cursor里的個數的(不考慮header的話)。雖然一共可能有20條數據,但是界面只能看到8條,那么這個ChildCount大約就是8了。另一方面, FirstVisiblePosition取出的是在總的條數中的索引,再將會消失的header考慮進來,所以就是 FirstVisiblePosition為0時要設為1,大於0時又要設為0。

下面上代碼:

調用的代碼:

 

[java]  view plain copy
 
  1. int headerCount = mListContainer.getListView().getHeaderViewsCount();  
  2. int firstVisiblePos = mListContainer.getListView().getFirstVisiblePosition();  
  3. int newCursorPosition = getPositionInNewCursor(cursor.getCount(), firstVisiblePos);  
  4. int offsetY = getOffsetY(cursor, firstVisiblePos, newCursorPosition);  
  5.   
  6. mAdapter.changeCursor(cursor);  
  7.   
  8. mUpRefreshLayout.setVisibility(View.GONE);  
  9.         
  10. mListContainer.getListView().setSelectionFromTop(newCursorPosition + headerCount, offsetY);  
getPositionInNewCursor函數:

 

 

[java]  view plain copy
 
  1. private int getPositionInNewCursor(int newCursorCount, int firstVisiblePos){  
  2.     if(firstVisiblePos == 0){  
  3.         firstVisiblePos += 1;  
  4.     }  
  5.   
  6.     int headerCount = mListContainer.getListView().getHeaderViewsCount();  
  7.     int newCursorPos = newCursorCount - mAdapter.getCount() + firstVisiblePos - headerCount;  
  8.       
  9.     return newCursorPos;  
  10. }  
getOffsetY函數:

 

 

[java]  view plain copy
 
  1. private int getOffsetY(Cursor cursor, int firstVisiblePos, int newCursorPosition){  
  2.         int y;  
  3.           
  4.         View firstVisibleItem = null;  
  5.         if(firstVisiblePos == 0){  
  6.             firstVisibleItem = mListContainer.getListView().getChildAt(1);  
  7.         }else{  
  8.             firstVisibleItem = mListContainer.getListView().getChildAt(0);  
  9.         }  
  10.         y = firstVisibleItem.getTop();  
  11.   
  12.         View timeView = firstVisibleItem.findViewById(R.id.time_text_view);  
  13.         if(timeView != null && timeView.getVisibility() == View.VISIBLE){  
  14.   
  15.             Cursor curItem = (Cursor)mAdapter.getItem(newCursorPosition);  
  16.             Cursor preItem = (Cursor)mAdapter.getItem(newCursorPosition - 1);  
  17.             if(curItem != null || preItem != null){  
  18.                 long curTimeStamp = curItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);  
  19.                 long preTimeStamp = preItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);  
  20.                   
  21.                 if(Math.abs(curTimeStamp - preTimeStamp) <= SHOW_TIME_STAMP_TEN_MINS){  
  22.                     LayoutParams param = (LinearLayout.LayoutParams)mTimeView.getLayoutParams();  
  23.                     y += mTimeView.getHeight() + param.topMargin + param.bottomMargin;  
  24.                 }  
  25.             }  
  26.         }  
  27.           
  28.         return y;  
  29.     }  

getOffsetY中有一段計算圖中TimeStamp的高度的代碼,不關心的可以自己跳過一下。因為查詢出歷史數據后可能會造成原先有TimeStamp的那一條在刷新后不再顯示TimeStamp(與上一條合並到一個時間段了),所以要把它的高度也計算進去。

 

 
3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM