美文网首页Android开发经验谈Android技术知识Android开发
Android中ViewPager+Fragment懒加载解决方

Android中ViewPager+Fragment懒加载解决方

作者: 程序猿男神 | 来源:发表于2018-09-11 16:54 被阅读130次

    文/程序员男神

    前言

    最近医院上线,我们开发都奔赴医院现场支持,每天加班到很晚。自己也搞的精神不振,还好都把问题解决了。后面的几天轻松了不少。本文就是最近在开发新需求遇到的问题,记录下来以便后面供自己参考。


    aj

    问题描述

    在android开发中,经常会用到ViewPager+Fragment来实现切换Tab页。但是,由于ViewPager的内部机制,它总会默认至少预加载一个页面。
    因为有时候用户希望选择了哪个Tab再去加载哪个Tab的数据,而不是预加载,如果当前页面和预加载页面都需要实时刷新当前Fragment,或者有大量的网络请求,可能就会比较慢。

    解决方案1:

    ViewPager因为内部机制,有预加载机制。那我们直接把ViewPager的预加载设置为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();
            }
        }
    

    我们分析得知:即使设置为0,也会把limit设置为默认值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预加载问题的解决方案:
     * 1.可以懒加载的Fragment
     * 2.切换到其他页面时停止加载数据(可选)
     * Created by dj on 2018-09-10.
     * blog :http://blog.csdn.net/linglongxin24/article/details/53205878
     */
    
    public abstract class BaseFragment extends Fragment {
        /**
         * 视图是否已经初初始化
         */
        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;
            /**初始化的时候去加载数据**/
            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() {
        }
    }
    

    方案2的用法:

    (1)、创建新的Fragment类,继承BaseFragment;
    (2)、setContentView(),返回该xml布局文件;
    (3)、lazyLoad(),在此方法内加载需要的数据;
    (4)、stopLoad()方法可选,当视图已经对用户不可见并且加载过数据,如果需要在切换到其他页面时停止加载数据,可以覆写此方法。

    package com.winning.rims.tabtest.tst;
    
    import android.util.Log;
    
    import com.winning.rims.tabtest.R;
    
    /**
     * 描述: Fragment1
     * 作者|时间: djj on 2018/9/11 16:14
     * 博客地址: http://www.jianshu.com/u/dfbde65a03fc
     *
     */
    
    public class Fragment1 extends BaseFragment {
        @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" + "已经对用户不可见,可以停止加载数据");
        }
    }
    

    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="第一个Fragment" />
    
    </LinearLayout>
    

    其他Fragment一样,不在此多举例子。

    activity 中的代码实例:

    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="第一个"
                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="第二个"
                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="第三个"
                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="第四个"
                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>
    

    日志

    日志打印

    通过查看日志,我们发现,每次切换,都会先走stopLoad(),然后才去调用lazyLoad()方法。

    总结:
    虽然参考到别人代码,但是自己敲了一遍,还是很有收获的,解决问题,不需要焦头烂额的思考的感觉真好。加油,真好。哈哈哈...
    参考代码:https://blog.csdn.net/linglongxin24/article/details/53205878

    相关文章

      网友评论

        本文标题:Android中ViewPager+Fragment懒加载解决方

        本文链接:https://www.haomeiwen.com/subject/thbicftx.html