ScrollView中巢狀 GridView 導致 ScrollView預設不停留在頂部的解決辦法
ScrollView中巢狀 GridView 導致 ScrollView預設不停留在頂部的解決方案和分析
發生情況大概是我在ScrollView底部放了個GridView 來實現一個類似9宮格效果的展示.
Grid固定為2排,每排3個.固定為6個…所以沒有效能問題,不需要重用,所以直接用GridView了..
只是為了方便和資料對應處理.
然後出現的狀況是,當我獲取完資料並呼叫notifyDataSetChanged();後 ScrollView自動滾到了最底部,也就是GridView所在的位置.
百度了一下,獲取了一些解決方案
- view.requestFocus(); 讓介面頂部的某一個View獲取focus
-
grid.setFocusable(false); 讓Grid不能獲得focus
-
手動scrollto(0,0)
-
重寫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