Android中ViewPager+Fragment懶加載解決方案

文/程序員男神

前言

最近醫(yī)院上線,我們開發(fā)都奔赴醫(yī)院現(xiàn)場支持,每天加班到很晚。自己也搞的精神不振,還好都把問題解決了。后面的幾天輕松了不少。本文就是最近在開發(fā)新需求遇到的問題,記錄下來以便后面供自己參考。


aj

問題描述

在android開發(fā)中,經(jīng)常會(huì)用到ViewPager+Fragment來實(shí)現(xiàn)切換Tab頁。但是,由于ViewPager的內(nèi)部機(jī)制,它總會(huì)默認(rèn)至少預(yù)加載一個(gè)頁面。
因?yàn)橛袝r(shí)候用戶希望選擇了哪個(gè)Tab再去加載哪個(gè)Tab的數(shù)據(jù),而不是預(yù)加載,如果當(dāng)前頁面和預(yù)加載頁面都需要實(shí)時(shí)刷新當(dāng)前Fragment,或者有大量的網(wǎng)絡(luò)請(qǐng)求,可能就會(huì)比較慢。

解決方案1:

ViewPager因?yàn)閮?nèi)部機(jī)制,有預(yù)加載機(jī)制。那我們直接把ViewPager的預(yù)加載設(shè)置為0不就可以了?

vp.setOffscreenPageLimit(0);

查看源碼:

 /**
     * 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();
        }
    }

我們分析得知:即使設(shè)置為0,也會(huì)把limit設(shè)置為默認(rèn)值1。

解決方案2:

package com.winning.rims.tabtest.tst;

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預(yù)加載問題的解決方案:
 * 1.可以懶加載的Fragment
 * 2.切換到其他頁面時(shí)停止加載數(shù)據(jù)(可選)
 * Created by dj on 2018-09-10.
 * blog :http://blog.csdn.net/linglongxin24/article/details/53205878
 */

public abstract class BaseFragment extends Fragment {
    /**
     * 視圖是否已經(jīng)初初始化
     */
    protected boolean isInit = false;
    protected boolean isLoad = false;
    protected final String TAG = "BaseFragment";
    private View view;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(setContentView(), container, false);
        isInit = true;
        /**初始化的時(shí)候去加載數(shù)據(jù)**/
        isCanLoadData();
        return view;
    }

    /**
     * 視圖是否已經(jīng)對(duì)用戶可見,系統(tǒng)的方法
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        isCanLoadData();
    }

    /**
     * 是否可以加載數(shù)據(jù)
     * 可以加載數(shù)據(jù)的條件:
     * 1.視圖已經(jīng)初始化
     * 2.視圖對(duì)用戶可見
     */
    private void isCanLoadData() {
        if (!isInit) {
            return;
        }
        if (getUserVisibleHint()) {
            lazyLoad();
            isLoad = true;
        } else {
            if (isLoad) {
                stopLoad();
            }
        }
    }

    /**
     * 視圖銷毀的時(shí)候講Fragment是否初始化的狀態(tài)變?yōu)閒alse
     */
    @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();
        }

    }

    /**
     * 設(shè)置Fragment要顯示的布局
     *
     * @return 布局的layoutId
     */
    protected abstract int setContentView();

    /**
     * 獲取設(shè)置的布局
     *
     * @return
     */
    protected View getContentView() {
        return view;
    }

    /**
     * 找出對(duì)應(yīng)的控件
     *
     * @param id
     * @param <T>
     * @return
     */
    protected <T extends View> T findViewById(int id) {

        return (T) getContentView().findViewById(id);
    }

    /**
     * 當(dāng)視圖初始化并且對(duì)用戶可見的時(shí)候去真正的加載數(shù)據(jù)
     */
    protected abstract void lazyLoad();

    /**
     * 當(dāng)視圖已經(jīng)對(duì)用戶不可見并且加載過數(shù)據(jù),如果需要在切換到其他頁面時(shí)停止加載數(shù)據(jù),可以調(diào)用此方法
     */
    protected void stopLoad() {
    }
}

方案2的用法:

(1)、創(chuàng)建新的Fragment類,繼承BaseFragment;
(2)、setContentView(),返回該xml布局文件;
(3)、lazyLoad(),在此方法內(nèi)加載需要的數(shù)據(jù);
(4)、stopLoad()方法可選,當(dāng)視圖已經(jīng)對(duì)用戶不可見并且加載過數(shù)據(jù),如果需要在切換到其他頁面時(shí)停止加載數(shù)據(jù),可以覆寫此方法。

package com.winning.rims.tabtest.tst;

import android.util.Log;

import com.winning.rims.tabtest.R;

/**
 * 描述: Fragment1
 * 作者|時(shí)間: djj on 2018/9/11 16:14
 * 博客地址: http://www.itdecent.cn/u/dfbde65a03fc
 *
 */

public class Fragment1 extends BaseFragment {
    @Override
    public int setContentView() {
        return R.layout.fm_layout1;
    }

    @Override
    protected void lazyLoad() {
        String message = "Fragment1" + (isInit ? "已經(jīng)初始并已經(jīng)顯示給用戶可以加載數(shù)據(jù)" : "沒有初始化不能加載數(shù)據(jù)")+">>>>>>>>>>>>>>>>>>>";
        showToast(message);
        Log.d(TAG, message);
    }

    @Override
    protected void stopLoad() {
        Log.d(TAG, "Fragment1" + "已經(jīng)對(duì)用戶不可見,可以停止加載數(shù)據(jù)");
    }
}

fm_layout1 xmL布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="22sp"
        android:textColor="@color/colorAccent"
        android:text="第一個(gè)Fragment" />

</LinearLayout>

其他Fragment一樣,不在此多舉例子。

activity 中的代碼實(shí)例:

package com.winning.rims.tabtest.tst;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.widget.RadioGroup;

import com.winning.rims.tabtest.R;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RadioGroup rg;
    private ViewPager vp;

    private void assignViews() {
        rg = (RadioGroup) findViewById(R.id.rg);
        vp = (ViewPager) findViewById(R.id.vp);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main1);
        assignViews();
        initData();
    }

    private void initData() {
        final List<Fragment> fragments = new ArrayList<>();
        fragments.add(new Fragment1());
        fragments.add(new Fragment2());
        fragments.add(new Fragment3());
        fragments.add(new Fragment4());
        vp.setOffscreenPageLimit(0);
        vp.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return fragments.get(position);
            }

            @Override
            public int getCount() {
                return fragments.size();
            }
        });
        rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                vp.setCurrentItem(radioGroup.indexOfChild(radioGroup.findViewById(i)));
            }
        });
    }
}

activity_main1 xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".tst.MainActivity">

    <RadioGroup
        android:id="@+id/rg"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:button="@null"
            android:gravity="center"
            android:text="第一個(gè)"
            android:textColor="#000000"
            android:textSize="18sp" />

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:button="@null"
            android:gravity="center"
            android:text="第二個(gè)"
            android:textColor="#000000"
            android:textSize="18sp" />

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:button="@null"
            android:gravity="center"
            android:text="第三個(gè)"
            android:textColor="#000000"
            android:textSize="18sp" />

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:button="@null"
            android:gravity="center"
            android:text="第四個(gè)"
            android:textColor="#000000"
            android:textSize="18sp" />
    </RadioGroup>

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

日志

日志打印

通過查看日志,我們發(fā)現(xiàn),每次切換,都會(huì)先走stopLoad(),然后才去調(diào)用lazyLoad()方法。

總結(jié):
雖然參考到別人代碼,但是自己敲了一遍,還是很有收獲的,解決問題,不需要焦頭爛額的思考的感覺真好。加油,真好。哈哈哈...
參考代碼:https://blog.csdn.net/linglongxin24/article/details/53205878

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容