Android中焦點移到ListView的問題(轉)

l_serein發表於2012-04-30

發現Android程式設計中的一個問題:如果在一個ListView上面放置一個可以接收焦點的東西,比如Button,當使用向上方向鍵滾動ListView到第一條後,焦點會移到上面的Button上,這個沒問題。但然後使用向下的方向鍵時,焦點會跳到ListView中當前視窗的最下面一條,而不是焦點離開時的第一條。在ListView下方有Button的時候,向上移動焦點,也會出現類似的情況。

這個問題在Android的示例裡面也有,ApiDemos->Views->Tabs->Content By Intent。這個示例裡當使用方向鍵從list這個Tab向下移動焦點的時候,會跳過一屏的條目。

在網上搜了一下,僅僅有一個人提到了這個問題,但沒有看到解答。

我查了一下原始碼,實現設定焦點的程式碼是:

git://android.git.kernel.org/platform/frameworks/base.git›core›java›android›widget›ListView.java

Java程式碼  收藏程式碼
  1. @Override  
  2. protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {  
  3.     super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);  
  4.   
  5.     int closetChildIndex = -1;  
  6.     if (gainFocus && previouslyFocusedRect != null) {  
  7.         previouslyFocusedRect.offset(mScrollX, mScrollY);  
  8.   
  9.         // figure out which item should be selected based on previously  
  10.         // focused rect  
  11.         Rect otherRect = mTempRect;  
  12.         int minDistance = Integer.MAX_VALUE;  
  13.         final int childCount = getChildCount();  
  14.         final int firstPosition = mFirstPosition;  
  15.         final ListAdapter adapter = mAdapter;  
  16.   
  17.         for (int i = 0; i < childCount; i++) {  
  18.             // only consider selectable views  
  19.             if (!adapter.isEnabled(firstPosition + i)) {  
  20.                 continue;  
  21.             }  
  22.   
  23.             View other = getChildAt(i);  
  24.             other.getDrawingRect(otherRect);  
  25.             offsetDescendantRectToMyCoords(other, otherRect);  
  26.             int distance = getDistance(previouslyFocusedRect, otherRect, direction);  
  27.   
  28.             if (distance < minDistance) {  
  29.                 minDistance = distance;  
  30.                 closetChildIndex = i;  
  31.             }  
  32.         }  
  33.     }  
  34.   
  35.     if (closetChildIndex >= 0) {  
  36.         setSelection(closetChildIndex + mFirstPosition);  
  37.     } else {  
  38.         requestLayout();  
  39.     }  
  40. }  

通過debug發現,previouslyFocusedRect在這裡是ListView的,而不是之前焦點View的。在按向下鍵時,getDistance比較ListView的bottom和各個child的top,當然會選中離ListView下沿最近的。具體為什麼會previouslyFocusedRect是ListView,我還沒有深入分析。

一個解決辦法

這不是一個根本解決的方法:寫一個新的class,繼承ListView,覆蓋onFocusChanged。

Java程式碼  收藏程式碼
  1. @Override  
  2. protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {  
  3.     super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);  
  4.     if (gainFocus && previouslyFocusedRect != null) {  
  5.         final ListAdapter adapter = getAdapter();  
  6.         final int count = adapter.getCount();  
  7.         switch (direction) {  
  8.             case FOCUS_DOWN:  
  9.                 for (int i = 0; i < count; i++) {  
  10.                     if (!adapter.isEnabled(i)) {  
  11.                         continue;  
  12.                     }  
  13.                     setSelection(i);  
  14.                     break;  
  15.                 }  
  16.                 break;  
  17.             case FOCUS_UP:  
  18.                 for (int i = count-1; i>=0; i--) {  
  19.                     if (!adapter.isEnabled(i)) {  
  20.                         continue;  
  21.                     }  
  22.                     setSelection(i);  
  23.                     break;  
  24.                 }  
  25.                 break;  
  26.             default:  
  27.                 break;  
  28.         }  
  29.     }  
  30. }  

在這裡,我只處理了FOCUS_DOWN和FOCUS_UP。由於不能訪問mFirstPosition,處理也做了簡化:焦點從上方移下來時選擇第一個能選擇的,從下方移上來時選擇最後一個能選擇的。

 

感謝:http://sunote.info/2010/02/25/android-move-focus-to-listview/

相關文章