1. 程式人生 > >Android使用setUserVisibleHint()實現Fragment懶載入

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<Feed.FeedItem> 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<Feed.FeedItem> 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 實現懶載入