轉載請注明出處:http://blog.csdn.net/linglongxin24/article/details/53205878
本文出自【DylanAndroid的博客】
Android中ViewPager+Fragment取消(禁止)預加載延遲加載(懶加載)問題解決方案
在Android中我們經常會用到ViewPager+Fragment組合。然而,有一個很讓人頭疼的問題就是,我們去加載數據的時候由於ViewPager的內部機制所限制,所以它會默認至少預加載一個。這讓人很郁悶,所以,我就想到要封裝一個Fragment來解決這個問題。
這里還解決一個問題就是在Android酷炫歡迎頁播放視頻,仿螞蜂窩自由行和慕課網
這里感謝有一位網友提出了bug,就是在播放視頻的時候如果滑動到第二頁和第三頁,第一頁的視頻還在播放,這是個讓人很頭疼的問題,在這里也完美解決。
1.問題初探
文章開始已經說過ViewPager的預加載機制。那么,我們可不可以設置ViewPager的預加載為0,不就解決問題了嗎?
vp.setOffscreenPageLimit(0);
- 1
- 1
經過測試發現,根本不是這么回事,為什么呢?我們來看看Viewpager的setOffscreenPageLimit()方法的源碼
private static final int DEFAULT_OFFSCREEN_PAGES = 1; /** * Set the number of pages that should be retained to either side of the * current page in the view hierarchy in an idle state. Pages beyond this * limit will be recreated from the adapter when needed. * * <p>This is offered as an optimization. If you know in advance the number * of pages you will need to support or have lazy-loading mechanisms in place * on your pages, tweaking this setting can have benefits in perceived smoothness * of paging animations and interaction. If you have a small number of pages (3-4) * that you can keep active all at once, less time will be spent in layout for * newly created view subtrees as the user pages back and forth.</p> * * <p>You should keep this limit low, especially if your pages have complex layouts. * This setting defaults to 1.</p> * * @param limit How many pages will be kept offscreen in an idle state. */ public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
我們發現,即使你設置為0,那么還是會在里面判斷后設為默認值1。所以這個方法是行不通的。
2.問題再探
我們發現Fragment中有一個setUserVisibleHint(boolean isVisibleToUser)方法,這個方法就是告訴用戶,UI對用戶是否可見,那么我們在這里去加載數據會怎么樣呢?
這又是為什么呢?
因為ViewPager會加載好多Fragment,為了節省內容等會在Fragment不可見的某個時候調用onDestroyView()將用戶界面銷毀掉但是Fragment的實例還在,所以可能第一次加載沒有問題,
但是再次回到第一個Fragment再去加載的時候就會出現UI對用戶可見但是視圖還沒有初始化。
3.最終解決方案
package cn.bluemobi.dylan.viewpagerfragmentlazyload; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; /** * Fragment預加載問題的解決方案: * 1.可以懶加載的Fragment * 2.切換到其他頁面時停止加載數據(可選) * Created by yuandl on 2016-11-17. * blog :http://blog.csdn.net/linglongxin24/article/details/53205878 */ public abstract class LazyLoadFragment extends Fragment { /** * 視圖是否已經初初始化 */ protected boolean isInit = false; protected boolean isLoad = false; protected final String TAG = "LazyLoadFragment"; private View view; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { view = inflater.inflate(setContentView(), container, false); isInit = true; /**初始化的時候去加載數據**/ isCanLoadData(); return view; } /** * 視圖是否已經對用戶可見,系統的方法 */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); isCanLoadData(); } /** * 是否可以加載數據 * 可以加載數據的條件: * 1.視圖已經初始化 * 2.視圖對用戶可見 */ private void isCanLoadData() { if (!isInit) { return; } if (getUserVisibleHint()) { lazyLoad(); isLoad = true; } else { if (isLoad) { stopLoad(); } } } /** * 視圖銷毀的時候講Fragment是否初始化的狀態變為false */ @Override public void onDestroyView() { super.onDestroyView(); isInit = false; isLoad = false; } protected void showToast(String message) { if (!TextUtils.isEmpty(message)) { Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); } } /** * 設置Fragment要顯示的布局 * * @return 布局的layoutId */ protected abstract int setContentView(); /** * 獲取設置的布局 * * @return */ protected View getContentView() { return view; } /** * 找出對應的控件 * * @param id * @param <T> * @return */ protected <T extends View> T findViewById(int id) { return (T) getContentView().findViewById(id); } /** * 當視圖初始化並且對用戶可見的時候去真正的加載數據 */ protected abstract void lazyLoad(); /** * 當視圖已經對用戶不可見並且加載過數據,如果需要在切換到其他頁面時停止加載數據,可以覆寫此方法 */ protected void stopLoad() { } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
4.用法
LazyLoadFragment是一個抽象類,可以作為BaseFragment,繼承它。
-
(1).用setContentView()方法去加載要顯示的布局
-
(2).lazyLoad()方法去加載數據
-
(3).stopLoad()方法可選,當視圖已經對用戶不可見並且加載過數據,如果需要在切換到其他頁面時停止加載數據,可以覆寫此方法
package cn.bluemobi.dylan.viewpagerfragmentlazyload; import android.util.Log; /** * Created by yuandl on 2016-11-17. */ public class Fragment1 extends LazyLoadFragment { @Override public int setContentView() { return R.layout.fm_layout1; } @Override protected void lazyLoad() { String message = "Fragment1" + (isInit ? "已經初始並已經顯示給用戶可以加載數據" : "沒有初始化不能加載數據")+">>>>>>>>>>>>>>>>>>>"; showToast(message); Log.d(TAG, message); } @Override protected void stopLoad() { Log.d(TAG, "Fragment1" + "已經對用戶不可見,可以停止加載數據"); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
5.看效果界面