2013-02-12 47 views
0

我有一個ListView與不同數量的項目。它可以滾動,也可以放在一個屏幕上。當用戶點擊並拖動列表並超頻滾動時,您將獲得漂亮的超卷邊緣發光效果。做一個簡短的ListView(不滾動)作出反應滾動嘗試

如果列表不滾動,因爲它只有很少的元素,但是點擊並拖動其中一個項目,您仍然會獲得邊緣發光效果。這很好,我想要什麼。

問題是如果您點擊下面的最後一項,並拖動,沒有任何反應。在這種情況下,我想讓它「嘗試滾動」。如果沒有視覺反饋,名單似乎被凍結,這很讓人困惑。

+--------------------------+ 
| Item 1   (x)  |  Tap and drag here works. The list 
+--------------------------+  doesn't move, but the user gets a 
| Item 2     |  visual overscroll indicator on the 
+--------------------------+  top or the bottom. 
| Item 3     | 
+--------------------------+ 
|       | 
|     (x)  |  Tap and drag here does nothing. 
|       | 
|       | 
|       | 
|       | 
|       | 
+--------------------------+ 

如何讓整個ListView對滾動嘗試做出反應?

我確保它覆蓋了所有可用空間,而不僅僅是項目所需的空間(設置爲android:layout_height="fill_parent"並嘗試了不同的BG顏色)。啓動滾動的代碼在哪裏?如果我必須重寫某些內容,那很好,因爲我已經使用自己的ListView子類。

+0

這是什麼動機?看到一個不佔用整個屏幕的列表應該足以說明用戶他們正在看到列表的末尾。 – Karakuri 2013-02-12 15:43:37

+0

問題不在於用戶不知道他們是否在最後。在開發應用程序時,我以不同的觀點大肆渲染。我只看到了一個項目,當我試圖滾動時,我沒有視覺反饋(但是我在5個項目的短列表中有過反饋)。問題是,我以爲我崩潰了應用程序!我重新啓動了它幾次,甚至試圖調試它,然後才意識到我剛剛在一個死角區域進行了攻擊。 – jdm 2013-02-12 15:51:11

+0

好吧,如果你的應用程序沒有崩潰,並且普通的Android用戶不會覺得奇怪(因爲它是標準的平臺行爲),那麼我不確定需要在這裏修復什麼。如果你真的想,也許你可以繼承ListView的子類,並自動添加頁腳視圖與剩餘空間的高度,但我真的認爲這是沒有必要的(可能比聽起來更復雜)。 – Karakuri 2013-02-12 18:37:32

回答

0

找到了。 ListView子類中的以下內容將完成此技巧。我在Android 4.2.1上測試過它,但它應該適用於任何4.x.

聲明:此代碼依賴於ListView的實現細節,並使用反射修改私有變量。我不能誇大這是多麼邪惡。你不應該在生產代碼中使用它!

我將此作爲概念驗證並滿足我的好奇心。如果你想使用這樣的代碼(不幸的是它有時需要獲得特定的效果),在它周圍放置版本衛士,如下所示。只能在您嘗試使用的版本上運行並檢查了源代碼。在其他情況下,您應該只使用默認行爲,例如return super.onTouchEvent(ev)

此代碼允許您通過最後一項下面的空白部分來滾動短列表(不成功嘗試)。點擊並拖動空白處,您應該看到過度滾動發光。如果您已經實施了像超頻滾動橡皮條效果的iPhone,這特別酷。

它是如何工作的?看看AbsListView.OnTouchEvent。在行3397中,motionPosition是當前選擇的項目索引。如果它是-1(沒有項目),if幾行後面是錯誤的,並且mTouchMode沒有設置。在這種情況下,我將ListView手動設置爲滾動模式。

static final int TOUCH_MODE_DOWN = 0; 
static final int TOUCH_MODE_SCROLL = 3; 
static final int TOUCH_MODE_FLING = 4; 

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN || 
     Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) { 
     final int x = (int) ev.getX(); 
     final int y = (int) ev.getY(); 
     int motionPosition = pointToPosition(x, y); 
     int action = (ev.getAction() & MotionEvent.ACTION_MASK); 

     if (action == MotionEvent.ACTION_DOWN) { 
      if (motionPosition == -1) { 
       setFieldInt("mActivePointerId", ev.getPointerId(0)); 
       if (!getAdapterViewFieldBool("mDataChanged")) { 
        if ((getFieldInt("mTouchMode") != TOUCH_MODE_FLING)) { 
         setFieldInt("mTouchMode", TOUCH_MODE_DOWN); 
        } 
       } 

       setFieldInt("mMotionViewOriginalTop", 0); 
       setFieldInt("mMotionX", x); 
       setFieldInt("mMotionY", y); 
       setFieldInt("mMotionPosition", motionPosition); 
       setFieldInt("mLastY", Integer.MIN_VALUE); 
      } 
     } 
    } 
    return super.onTouchEvent(ev); 
} 

private void setFieldInt(String fieldName, int value) { 
    try { 
     Field field = AbsListView.class.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     field.setInt(this, value); 
    } 
    catch (NoSuchFieldException e) { Log.v(TAG, "exception", e); } 
    catch (IllegalAccessException e) { Log.v(TAG, "exception", e); } 
} 

private int getFieldInt(String fieldName) { 
    try { 
     Field field = AbsListView.class.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     return field.getInt(this); 
    } 
    catch (NoSuchFieldException e) { Log.v(TAG, "exception", e); } 
    catch (IllegalAccessException e) { Log.v(TAG, "exception", e); } 
    return 0; 
} 


private boolean getAdapterViewFieldBool(String fieldName) { 
    try { 
     Field field = AdapterView.class.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     return field.getBoolean(this); 
    } 
    catch (NoSuchFieldException e) { Log.v(TAG, "exception", e); } 
    catch (IllegalAccessException e) { Log.v(TAG, "exception", e); } 
    return false; 
}