Split RecyclerViewFragment#getAdapter() into getAdapter() and onCreateAdapter()

This commit is contained in:
Phillip Hsu 2016-09-03 03:15:22 -07:00
parent b1ac2e60dd
commit ca2da0ad66
4 changed files with 52 additions and 19 deletions

View File

@ -47,11 +47,18 @@ public abstract class RecyclerViewFragment<
protected abstract void onScrolledToStableId(long id, int position);
/**
* @return the adapter to set on the RecyclerView. SUBCLASSES MUST OVERRIDE THIS, BECAUSE THE
* DEFAULT IMPLEMENTATION WILL ALWAYS RETURN AN UNINITIALIZED ADAPTER INSTANCE.
* @return the adapter to set on the RecyclerView. Called in onCreateView().
* @param savedInstanceState the same Bundle used to save out and restore state for this Fragment
* when the configuration is changed. Implementors may find this useful
* if their ViewHolder type(s) require saving and restoring state across
* configurations.
*/
@Nullable
protected A getAdapter() {
protected abstract A onCreateAdapter(Bundle savedInstanceState);
/**
* @return the adapter instance created from {@link #onCreateAdapter(Bundle)}
*/
protected final A getAdapter() {
return mAdapter;
}
@ -69,7 +76,7 @@ public abstract class RecyclerViewFragment<
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
mList.setLayoutManager(getLayoutManager());
mList.setAdapter(mAdapter = getAdapter());
mList.setAdapter(mAdapter = onCreateAdapter(savedInstanceState));
return view;
}

View File

@ -6,7 +6,6 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.content.Loader;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@ -136,11 +135,8 @@ public class AlarmsFragment extends RecyclerViewFragment<
dialog.show(getFragmentManager(), TAG_TIME_PICKER);
}
@Nullable
@Override
protected AlarmsCursorAdapter getAdapter() {
if (super.getAdapter() != null)
return super.getAdapter();
protected AlarmsCursorAdapter onCreateAdapter(Bundle savedInstanceState) {
// Create a new adapter. This is called before we can initialize mAlarmController,
// so right now it is null. However, after super.onCreate() returns, it is initialized, and
// the reference variable will be pointing to an actual object. This assignment "propagates"
@ -262,7 +258,43 @@ public class AlarmsFragment extends RecyclerViewFragment<
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_EXPANDED_POSITION, getAdapter().getExpandedPosition());
/*
* From Fragment#onSaveInstanceState():
* - This is called "at any time before onDestroy()".
* - "This corresponds to Activity.onSaveInstanceState(Bundle) and most of the discussion
* there applies here as well".
* From Activity#onSaveInstanceState():
* - "If called, this method will occur before {@link #onStop}
* [which follows onPause() in the lifecycle]. There are
* no guarantees about whether it will occur before or after {@link #onPause}."
*
* isResumed() is true "for the duration of onResume() and onPause()".
* From the results of a few trials, this never seemed to call through, so i'm assuming
* isResumed() returned false every time.
*/
if (/*isResumed() && */getAdapter() != null) {
// Normally when we scroll far enough away from this Fragment, *its view* will be
// destroyed, i.e. the maximum point in its lifecycle is onDestroyView(). However,
// if the configuration changes, onDestroy() is called through, and then this Fragment
// and all of its members will be destroyed. This is not
// a problem if the page in which the configuration changed is this page, because
// the Fragment will be recreated from onCreate() to onResume(), and any
// member initialization between those points occurs as usual.
//
// However, when the page in which the configuration changed
// is far enough away from this Fragment, there IS a problem. The Fragment
// *at that page* is recreated, but this Fragment will NOT be; the ViewPager's
// adapter will not reinstantiate this Fragment because it exceeds the
// offscreen page limit relative to the initial page in the new configuration.
//
// As such, we should only save state if this Fragment's members (i.e. its RecyclerView.Adapter)
// are not destroyed
// because that indicates the Fragment is both registered in the adapter AND is within the offscreen
// page limit, so its members have been initialized (recall that a Fragment in a ViewPager
// does not actually need to be visible to the user for onCreateView() to onResume() to
// be called through).
outState.putInt(KEY_EXPANDED_POSITION, getAdapter().getExpandedPosition());
}
}
/////////////////////////////////////////////////////////////////////////////////////

View File

@ -259,11 +259,8 @@ public class StopwatchFragment extends RecyclerViewFragment<
updateAllFabs();
}
@Nullable
@Override
protected LapsAdapter getAdapter() {
if (super.getAdapter() != null)
return super.getAdapter();
protected LapsAdapter onCreateAdapter(Bundle savedInstanceState) {
return new LapsAdapter();
}

View File

@ -83,11 +83,8 @@ public class TimersFragment extends RecyclerViewFragment<
startActivityForResult(intent, REQUEST_CREATE_TIMER);
}
@Nullable
@Override
protected TimersCursorAdapter getAdapter() {
if (super.getAdapter() != null)
return super.getAdapter();
protected TimersCursorAdapter onCreateAdapter(Bundle savedInstanceState) {
// Create a new adapter. This is called before we can initialize mAsyncTimersTableUpdateHandler,
// so right now it is null. However, after super.onCreate() returns, it is initialized, and
// the reference variable will be pointing to an actual object. This assignment "propagates"