1. 程式人生 > >ScrollView中巢狀 GridView 導致 ScrollView預設不停留在頂部的解決辦法

ScrollView中巢狀 GridView 導致 ScrollView預設不停留在頂部的解決辦法

ScrollView中巢狀 GridView 導致 ScrollView預設不停留在頂部的解決方案和分析

發生情況大概是我在ScrollView底部放了個GridView 來實現一個類似9宮格效果的展示.

Grid固定為2排,每排3個.固定為6個…所以沒有效能問題,不需要重用,所以直接用GridView了..

只是為了方便和資料對應處理.

然後出現的狀況是,當我獲取完資料並呼叫notifyDataSetChanged();後 ScrollView自動滾到了最底部,也就是GridView所在的位置.

百度了一下,獲取了一些解決方案

  1. view.requestFocus(); 讓介面頂部的某一個View獲取focus
  2. grid.setFocusable(false); 讓Grid不能獲得focus

  3. 手動scrollto(0,0)

  4. 重寫ScrollView中的computeScrollDeltaToGetChildRectOnScreen,讓該方法返回0目前簡單的用setFocusable(false)解決了該問題

分析一下這個問題產生的原因. 從解決方案反推,這個問題產生和 focus有關係

一個猜測是 notifyDataSetChanged()之後,grid由於載入了資料的關係高度產生了變化

這導致了ScrollView內部重新走了 onLayout / onMeaure 流程 在這個流程中 ScrollView會將自身滾動到 獲得 focus 的 child 位置上面關於focus的解決方案即是從這個角度去解決問題

手動scrollto(0,0)是個比較爛的辦法

而重寫ScrollView中的computeScrollDeltaToGetChildRectOnScreen跟蹤一下呼叫鏈

Java

protected void onLayout(boolean changed, int l, int t, int r, int b) {
 super.onLayout(changed, l, t, r, b);
 mIsLayoutDirty = false;
 // Give a child focus if it needs it
 if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this
)) { scrollToChild(mChildToScrollTo); } ... }

可以看到 onLayout 的時候確實會將ScrollView滾動到focus child位置

private void scrollToChild(View child) {
 child.getDrawingRect(mTempRect);

 /* Offset from child's local coordinates to ScrollView coordinates */
 offsetDescendantRectToMyCoords(child, mTempRect);

 int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);

 if (scrollDelta != 0) {
 scrollBy(0, scrollDelta);
 }
}

而scrollToChild會根據computeScrollDeltaToGetChildRectOnScreen的返回值來計算滾動的位置

過載computeScrollDeltaToGetChildRectOnScreen讓其返回0 會導致ScrollView內佈局產生變化時,不能正確滾動到focus child位置

當然你不需要這個功能的話 過載computeScrollDeltaToGetChildRectOnScreen也可以

至於computeScrollDeltaToGetChildRectOnScreen程式碼太長就不貼了

大致是 根據當前 scrollY和focus child 的 rect.bottom 去計算要滾到哪

邏輯理順以後覺得這個問題也沒什麼奇怪的.

現在還剩個問題 不是很明白GridView為何會預設獲得focus