Android使用setUserVisibleHint()实现Fragment懒加载

Fragment 懒加载使用场景

当使用viewpager+adapter作为应用大的布局时,viewpager会通过setOffscreenPageLimit来设置预加载的项目,不设置setOffscreenPageLimit,则默认为1(设置0无效,可以查看该方法源码知道),也就是当我们打开应用看到的时候fragmentOne时,实际上其他fragment(例如fragmentSecond)也进行了加载,只不过没有显示出来罢了,但是这样就造成了不必要的资源浪费(例如,fragmentSecond没有显示,但是却进行了大量的网络加载操作)。

以下场景可以使用懒加载:

  1. 预加载Fragment时,也就是加载不可见的Fragment时,该不可见的Fragment初始化数据和页面可能占用了大量的资源;

  2. 如果当页面内有动画或者是存在其它持续性的操作,fragment的状态切换时操作的开关问题;

  3. 初始化时代码量大的问题, inflater.inflate(R.layout.xxx,Container,false)都是多余重复的操作;

使用setUserVisibleHint()实现懒加载

Set a hint to the system about whether this fragment's UI is currently visible to the user. 
An app may set this to false to indicate that the fragment's UI is scrolled out of visibility 
or is otherwise not directly visible to the user. 
This may be used by the system to prioritize operations     such as fragment lifecycle updates 
or loader ordering behavior.

这个SetUserVisibleHint()的方法就是判断这个fragment对用户是否可见的

唯一需要值得注意的是,setUserVisibleHint是在onCreateView周期前调用,此时布局中各View还未初始化,所以只能在setUserVisibleHint中进行纯数据的加载。那么牵涉到ui的操作(比如为某个view设置数据)该放在那里呢?

代码实例

public abstract class BaseFragment extends Fragment {

    private static final String TAG = "tianrui";

    protected boolean mIsVisible;
    protected boolean mHasLoaded;
    protected boolean mHasPrepare;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Log.d("tianrui", "onViewCreated: ");
        super.onViewCreated(view, savedInstanceState);
        ButterKnife.bind(this, view);
        if (mIsVisible) {
            Log.d(TAG, "onViewCreated: load data in #onViewCreated ");
            initData();
            mHasLoaded = true;
        }
        mHasPrepare = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.d("tianrui", "setUserVisibleHint: " + isVisibleToUser);
        mIsVisible = getUserVisibleHint();
        lazyLoad();
    }

    @Override
    public void onDestroyView() {
        Log.d(TAG, "onDestroyView: ");
        super.onDestroyView();
        mHasLoaded = false;
        mHasPrepare = false;
    }


    protected void lazyLoad() {
        Log.d(TAG, "lazyLoad: mIsVisible " + mIsVisible + " mHasLoaded " + mHasLoaded + " mHasPrepare " + mHasPrepare);
        if (!mIsVisible || mHasLoaded || !mHasPrepare) {
            return;
        }
        Log.d(TAG, "lazyLoad: load data in lazyLoad ");
        initData();
    }

    abstract protected void initData();
}

子类实现initData()绑定数据即可


public class FeedFragment extends BaseFragment implements FeedContract.View {

    private static final String TAG = "feed_frag";

    @BindView(R.id.id_rv_list)
    RecyclerView mRvList;

    @BindView(R.id.id_srl_refresh)
    SwipeRefreshLayout mSrlRefresh;

    @BindView(R.id.id_progress_bar)
    ProgressBar mProgressBar;

    @BindView(R.id.fab)
    FloatingActionButton mFab;

    private FeedContract.Presenter mPresenter;
    private String mChannel;

    private interface ExtraKey {
        String CHANNEL_KEY = "channel_key";

    }

    private interface ViewStatus {
        int REFRESHING = 0x01;
        int REFRESH_SUCCESS = 0x02;
    }


    public FeedFragment() {

    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Bundle bundle = getArguments();
        if (bundle != null) {
            mChannel = bundle.getString(ExtraKey.CHANNEL_KEY);
        }
        mPresenter = new FeedPresenter(new RemoteFeedDataSource(mChannel), this);
    }

    public static FeedFragment newInstance(String channel) {
        final FeedFragment feedFragment = new FeedFragment();
        final Bundle bundle = new Bundle();
        bundle.putString(ExtraKey.CHANNEL_KEY, channel);
        feedFragment.setArguments(bundle);
        return feedFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_feed, container, false);
    }

    @Override
    // initData绑定数据, 懒加载节省的是这部分资源.
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    protected void initData() {
        mPresenter.start();
        mSrlRefresh.setColorSchemeColors(getResources().getColor(R.color.colorAccent));
        mSrlRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mPresenter.refresh();
            }
        });
    }


    @Override
    public void setPresenter(FeedContract.Presenter presenter) {
        mPresenter = presenter;
    }

    @Override
    public void showFeedList(List feedItems) {
        Log.d(TAG, "showFeedList: " + feedItems);
        if (feedItems.isEmpty()) {
            return;
        }
        mRvList.setAdapter(new FeedAdapter(feedItems));
        mRvList.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                outRect.top = 10;
            }
        });
        adjustStatus(ViewStatus.REFRESH_SUCCESS);
    }

    @OnClick(R.id.fab)
    void onFabClick(View view) {
        mPresenter.refresh();
    }

    @Override
    public void showErrorPage() {
        Toast.makeText(getContext(), "网络错误", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showLoading() {
        mSrlRefresh.setRefreshing(true);
        // rotate fab.
        final AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this.getContext(), R.animator.fab_click);
        set.setInterpolator(new AccelerateInterpolator());
        set.setTarget(mFab);
        set.start();
    }

    @Override
    public void refresh(List feedItems) {
        if (feedItems == null || feedItems.isEmpty()) {
            return;
        }
        final RecyclerView.Adapter adapter = mRvList.getAdapter();
        if (adapter == null || !(adapter instanceof FeedAdapter)) {
            return;
        }

        ((FeedAdapter) adapter).refreshData(feedItems);

        App.getHandler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mSrlRefresh.setRefreshing(false);
            }
        }, 2000);
        adjustStatus(ViewStatus.REFRESH_SUCCESS);
    }

    private void adjustStatus(int status) {
        switch (status) {
            case ViewStatus.REFRESH_SUCCESS:
                mSrlRefresh.setVisibility(View.VISIBLE);
                mProgressBar.setVisibility(View.GONE);
                break;
            default:
            case ViewStatus.REFRESHING:
                mSrlRefresh.setVisibility(View.GONE);
                mProgressBar.setVisibility(View.VISIBLE);
                break;
        }
    }
}

本文摘自:Android Fragment 实现懒加载

你可能感兴趣的