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);

经过测试发现,根本不是这么回事,为什么呢?我们来看看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();
        }
    }

我们发现,即使你设置为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() {
    }
}

}

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" + "已经对用户不可见,可以停止加载数据");
     }
    }
    
    

5.看效果界面

界面
Log

6.GitHub

  • 23
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
以下是一个简单的实现 Android ViewPager 轮播图禁止加载且切换页面时不出现相邻页面白屏的示例代码: ```java public class BannerActivity extends AppCompatActivity { private ViewPager mViewPager; private BannerAdapter mAdapter; private List<BannerItem> mDataList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_banner); initData(); initView(); } private void initView() { mViewPager = findViewById(R.id.view_pager); mAdapter = new BannerAdapter(getSupportFragmentManager(), mDataList); mViewPager.setAdapter(mAdapter); mViewPager.setOffscreenPageLimit(1); mViewPager.setCurrentItem(mDataList.size() * 1000); // 添加页面切换监听器,禁止加载 mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { mAdapter.addExtraPages(position); } @Override public void onPageScrollStateChanged(int state) { } }); } private void initData() { mDataList = new ArrayList<>(); mDataList.add(new BannerItem(R.drawable.banner_1, "Banner 1")); mDataList.add(new BannerItem(R.drawable.banner_2, "Banner 2")); mDataList.add(new BannerItem(R.drawable.banner_3, "Banner 3")); mDataList.add(new BannerItem(R.drawable.banner_4, "Banner 4")); mDataList.add(new BannerItem(R.drawable.banner_5, "Banner 5")); } private static class BannerAdapter extends FragmentPagerAdapter { private List<BannerItem> mDataList; private SparseArray<Fragment> mExtraPages = new SparseArray<>(); BannerAdapter(FragmentManager fm, List<BannerItem> dataList) { super(fm); mDataList = dataList; } @Override public int getCount() { return mDataList.size() * 10000; } @Override public Fragment getItem(int position) { int index = position % mDataList.size(); return BannerFragment.newInstance(mDataList.get(index)); } public void addExtraPages(int position) { int index = position % mDataList.size(); if (mExtraPages.get(index) == null) { mExtraPages.put(index, BannerFragment.newInstance(mDataList.get(index))); } int left = index > 0 ? index - 1 : mDataList.size() - 1; if (mExtraPages.get(left) == null) { mExtraPages.put(left, BannerFragment.newInstance(mDataList.get(left))); } int right = index < mDataList.size() - 1 ? index + 1 : 0; if (mExtraPages.get(right) == null) { mExtraPages.put(right, BannerFragment.newInstance(mDataList.get(right))); } } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); mExtraPages.put(position % mDataList.size(), fragment); return fragment; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { mExtraPages.remove(position % mDataList.size()); super.destroyItem(container, position, object); } } public static class BannerFragment extends Fragment { private BannerItem mItem; public static BannerFragment newInstance(BannerItem item) { BannerFragment fragment = new BannerFragment(); Bundle args = new Bundle(); args.putParcelable("item", item); fragment.setArguments(args); return fragment; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mItem = getArguments().getParcelable("item"); } } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_banner, container, false); ImageView imageView = view.findViewById(R.id.image_view); TextView textView = view.findViewById(R.id.text_view); imageView.setImageResource(mItem.getImageResId()); textView.setText(mItem.getTitle()); return view; } } public static class BannerItem implements Parcelable { private int imageResId; private String title; public BannerItem(int imageResId, String title) { this.imageResId = imageResId; this.title = title; } protected BannerItem(Parcel in) { imageResId = in.readInt(); title = in.readString(); } public static final Creator<BannerItem> CREATOR = new Creator<BannerItem>() { @Override public BannerItem createFromParcel(Parcel in) { return new BannerItem(in); } @Override public BannerItem[] newArray(int size) { return new BannerItem[size]; } }; public int getImageResId() { return imageResId; } public String getTitle() { return title; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(imageResId); dest.writeString(title); } } } ``` 在上面的代码,我们禁止ViewPager 的加载,并手动添加了相邻页面。具体实现细节可以参考代码注释。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值