From 1e95ccd1ce1dc50ef079028d39462939cc9c8e8f Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 5 Feb 2015 13:23:37 -0800 Subject: [PATCH] 5.12 Coding Task to Restore scroll position on rotation (quiz) --- .../sunshine/app/ForecastFragment.java | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/example/android/sunshine/app/ForecastFragment.java b/app/src/main/java/com/example/android/sunshine/app/ForecastFragment.java index 3d20bb3e3..9b41e5aa0 100644 --- a/app/src/main/java/com/example/android/sunshine/app/ForecastFragment.java +++ b/app/src/main/java/com/example/android/sunshine/app/ForecastFragment.java @@ -37,6 +37,12 @@ * Encapsulates fetching the forecast and displaying it as a {@link ListView} layout. */ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCallbacks { + private ForecastAdapter mForecastAdapter; + + private ListView mListView; + private int mPosition = ListView.INVALID_POSITION; + + private static final String SELECTED_KEY = "selected_position"; private static final int FORECAST_LOADER = 0; // For the forecast view we're showing only a small subset of the stored data. @@ -71,8 +77,6 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa static final int COL_COORD_LAT = 7; static final int COL_COORD_LONG = 8; - private ForecastAdapter mForecastAdapter; - /** * A callback interface that all activities containing this fragment must * implement. This mechanism allows activities to be notified of item @@ -116,17 +120,18 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // The CursorAdapter will take data from our cursor and populate the ListView. + + // The ForecastAdapter will take data from a source and + // use it to populate the ListView it's attached to. mForecastAdapter = new ForecastAdapter(getActivity(), null, 0); View rootView = inflater.inflate(R.layout.fragment_main, container, false); // Get a reference to the ListView, and attach this adapter to it. - ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast); - listView.setAdapter(mForecastAdapter); - + mListView = (ListView) rootView.findViewById(R.id.listview_forecast); + mListView.setAdapter(mForecastAdapter); // We'll call our MainActivity - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int position, long l) { @@ -140,8 +145,21 @@ public void onItemClick(AdapterView adapterView, View view, int position, lon locationSetting, cursor.getLong(COL_WEATHER_DATE) )); } + mPosition = position; } }); + + // If there's instance state, mine it for useful information. + // The end-goal here is that the user never knows that turning their device sideways + // does crazy lifecycle related things. It should feel like some stuff stretched out, + // or magically appeared to take advantage of room, but data or place in the app was never + // actually *lost*. + if (savedInstanceState != null && savedInstanceState.containsKey(SELECTED_KEY)) { + // The listview probably hasn't even been populated yet. Actually perform the + // swapout in onLoadFinished. + mPosition = savedInstanceState.getInt(SELECTED_KEY); + } + return rootView; } @@ -163,12 +181,29 @@ private void updateWeather() { weatherTask.execute(location); } + @Override + public void onSaveInstanceState(Bundle outState) { + // When tablets rotate, the currently selected list item needs to be saved. + // When no item is selected, mPosition will be set to Listview.INVALID_POSITION, + // so check for that before storing. + if (mPosition != ListView.INVALID_POSITION) { + outState.putInt(SELECTED_KEY, mPosition); + } + super.onSaveInstanceState(outState); + } + @Override public Loader onCreateLoader(int i, Bundle bundle) { - String locationSetting = Utility.getPreferredLocation(getActivity()); + // This is called when a new Loader needs to be created. This + // fragment only uses one loader, so we don't care about checking the id. + + // To only show current and future dates, filter the query to return weather only for + // dates after or including today. // Sort order: Ascending, by date. String sortOrder = WeatherContract.WeatherEntry.COLUMN_DATE + " ASC"; + + String locationSetting = Utility.getPreferredLocation(getActivity()); Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate( locationSetting, System.currentTimeMillis()); @@ -181,12 +216,17 @@ public Loader onCreateLoader(int i, Bundle bundle) { } @Override - public void onLoadFinished(Loader cursorLoader, Cursor cursor) { - mForecastAdapter.swapCursor(cursor); + public void onLoadFinished(Loader loader, Cursor data) { + mForecastAdapter.swapCursor(data); + if (mPosition != ListView.INVALID_POSITION) { + // If we don't need to restart the loader, and there's a desired position to restore + // to, do so now. + mListView.smoothScrollToPosition(mPosition); + } } @Override public void onLoaderReset(Loader cursorLoader) { mForecastAdapter.swapCursor(null); } -} +} \ No newline at end of file