ScrollView巢狀ListView,載入大量資料,介面卡頓問題的探討
之前使用ScrollView巢狀ListView,解決ScrollView巢狀ListView,列表項高度不同,顯示不全的問題,現在發現一個問題,因為在ListView中計算各個列表項的高度,所以ListView實際上是全部展開的,這樣的話,不能使用ListView重用的機制,如果資料過多,則列表繪製的時候,會出現卡頓,一段時間後才顯示出來。
所以為了效能,就把介面重新優化,還是在ScrollView內使用ListView,不過固定好了ListView的高度,這樣又會有一個問題,就是ScrollView和ListView滑動會衝突,這樣又要解決掉滑動衝突的問題,點選ListView的時候,獲取滑動控制權,點選其他地方,將控制權上交給ScrollView,進一步優化,如果ListView滑動到頂部或底部,將滑動控制權交給ScrollView,使ScrollView整體滑動。
下邊是一些程式碼:lvSignservice 表示ListView
/** * 處理ListView和ScrollView的滑動衝突問題,並且在列表滑動到頂部和底部的時候,滑動ScrollView * */ private void dealWithListViewAndScrollViewTouch(){ lvSignservice.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { lvSignservice.getParent().requestDisallowInterceptTouchEvent(true); return false; } }); scrollview.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { scrollview.getParent().requestDisallowInterceptTouchEvent(false); return false; } }); lvSignservice.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (firstVisibleItem == 0) { View firstVisibleItemView = lvSignservice.getChildAt(0); if (firstVisibleItemView != null && firstVisibleItemView.getTop() == 0) { Log.d("ListView", "##### 滾動到頂部 #####"); lvSignservice.getParent().requestDisallowInterceptTouchEvent(false); scrollview.getParent().requestDisallowInterceptTouchEvent(true); } } else if ((firstVisibleItem + visibleItemCount) == totalItemCount) { View lastVisibleItemView = lvSignservice.getChildAt(lvSignservice.getChildCount() - 1); if (lastVisibleItemView != null && lastVisibleItemView.getBottom() == lvSignservice.getHeight()) { Log.d("ListView", "##### 滾動到底部 ######"); lvSignservice.getParent().requestDisallowInterceptTouchEvent(false); scrollview.getParent().requestDisallowInterceptTouchEvent(true); } } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //do nothing } }); }
<FrameLayout android:layout_width="match_parent" android:layout_height="400dp" android:background="@color/white"> <ListView android:id="@+id/lv_signservice" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="#00000000" android:divider="@null"/> <include layout="@layout/empty_layout"/> </FrameLayout>
ListView高度應該設定為match_parent,有利於優化,使用ListView的時候注意儘量使用layout_height=”match_parent”
其實,官方是不建議使用ScrollView巢狀ListView的:
You should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView
to display its entire list of items to fill up the infinite container supplied by ScrollView.
不應該使用ScrollView巢狀ListView的,因為ListView會管理屬於自己的垂直滾動。更重要的是,如果你這樣做了,所有listview重要的優化都會被抵消掉,listview會被迫顯示所有的item填滿由ScrollView提供的容器。
如果真要巢狀的話,可以看解決ScrollView巢狀ListView,列表項高度不同,顯示不全的問題,基本上兩種方法,一個是手動計算item高度,一個是重寫onMeasure方法。
個人經驗:ScrollView巢狀ListView僅適合於ListView的item少的情況,如果資料過多,則要重新設計,將ListView上邊的部分作為ListView的headView,要儘量使用ListView的重用機制
下邊是一些參考文章:
Android開發之ScrollView中巢狀ListView的解決方案
【Android 非常基礎】android官方不建議ScrollView巢狀ListView
Android應用效能優化系列檢視篇——ListView自適應導致的嚴重效能問題
《Android APP要解決的問題》之顯示篇:ScrollView巢狀ListView
Android 如何 ListView 判斷滾動到最頂部或者底部