Android中ViewPager+Fragment取消(禁止)預加載延遲加載(懶加載)問題解決方案


轉載請注明出處: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.看效果界面

界面 
Log

6.GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM