3737 * Encapsulates fetching the forecast and displaying it as a {@link ListView} layout.
3838 */
3939public class ForecastFragment extends Fragment implements LoaderManager .LoaderCallbacks <Cursor > {
40+ private ForecastAdapter mForecastAdapter ;
41+
42+ private ListView mListView ;
43+ private int mPosition = ListView .INVALID_POSITION ;
44+
45+ private static final String SELECTED_KEY = "selected_position" ;
4046
4147 private static final int FORECAST_LOADER = 0 ;
4248 // 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
7177 static final int COL_COORD_LAT = 7 ;
7278 static final int COL_COORD_LONG = 8 ;
7379
74- private ForecastAdapter mForecastAdapter ;
75-
7680 /**
7781 * A callback interface that all activities containing this fragment must
7882 * implement. This mechanism allows activities to be notified of item
@@ -116,17 +120,18 @@ public boolean onOptionsItemSelected(MenuItem item) {
116120 @ Override
117121 public View onCreateView (LayoutInflater inflater , ViewGroup container ,
118122 Bundle savedInstanceState ) {
119- // The CursorAdapter will take data from our cursor and populate the ListView.
123+
124+ // The ForecastAdapter will take data from a source and
125+ // use it to populate the ListView it's attached to.
120126 mForecastAdapter = new ForecastAdapter (getActivity (), null , 0 );
121127
122128 View rootView = inflater .inflate (R .layout .fragment_main , container , false );
123129
124130 // Get a reference to the ListView, and attach this adapter to it.
125- ListView listView = (ListView ) rootView .findViewById (R .id .listview_forecast );
126- listView .setAdapter (mForecastAdapter );
127-
131+ mListView = (ListView ) rootView .findViewById (R .id .listview_forecast );
132+ mListView .setAdapter (mForecastAdapter );
128133 // We'll call our MainActivity
129- listView .setOnItemClickListener (new AdapterView .OnItemClickListener () {
134+ mListView .setOnItemClickListener (new AdapterView .OnItemClickListener () {
130135
131136 @ Override
132137 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
140145 locationSetting , cursor .getLong (COL_WEATHER_DATE )
141146 ));
142147 }
148+ mPosition = position ;
143149 }
144150 });
151+
152+ // If there's instance state, mine it for useful information.
153+ // The end-goal here is that the user never knows that turning their device sideways
154+ // does crazy lifecycle related things. It should feel like some stuff stretched out,
155+ // or magically appeared to take advantage of room, but data or place in the app was never
156+ // actually *lost*.
157+ if (savedInstanceState != null && savedInstanceState .containsKey (SELECTED_KEY )) {
158+ // The listview probably hasn't even been populated yet. Actually perform the
159+ // swapout in onLoadFinished.
160+ mPosition = savedInstanceState .getInt (SELECTED_KEY );
161+ }
162+
145163 return rootView ;
146164 }
147165
@@ -163,12 +181,29 @@ private void updateWeather() {
163181 weatherTask .execute (location );
164182 }
165183
184+ @ Override
185+ public void onSaveInstanceState (Bundle outState ) {
186+ // When tablets rotate, the currently selected list item needs to be saved.
187+ // When no item is selected, mPosition will be set to Listview.INVALID_POSITION,
188+ // so check for that before storing.
189+ if (mPosition != ListView .INVALID_POSITION ) {
190+ outState .putInt (SELECTED_KEY , mPosition );
191+ }
192+ super .onSaveInstanceState (outState );
193+ }
194+
166195 @ Override
167196 public Loader <Cursor > onCreateLoader (int i , Bundle bundle ) {
168- String locationSetting = Utility .getPreferredLocation (getActivity ());
197+ // This is called when a new Loader needs to be created. This
198+ // fragment only uses one loader, so we don't care about checking the id.
199+
200+ // To only show current and future dates, filter the query to return weather only for
201+ // dates after or including today.
169202
170203 // Sort order: Ascending, by date.
171204 String sortOrder = WeatherContract .WeatherEntry .COLUMN_DATE + " ASC" ;
205+
206+ String locationSetting = Utility .getPreferredLocation (getActivity ());
172207 Uri weatherForLocationUri = WeatherContract .WeatherEntry .buildWeatherLocationWithStartDate (
173208 locationSetting , System .currentTimeMillis ());
174209
@@ -181,12 +216,17 @@ public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
181216 }
182217
183218 @ Override
184- public void onLoadFinished (Loader <Cursor > cursorLoader , Cursor cursor ) {
185- mForecastAdapter .swapCursor (cursor );
219+ public void onLoadFinished (Loader <Cursor > loader , Cursor data ) {
220+ mForecastAdapter .swapCursor (data );
221+ if (mPosition != ListView .INVALID_POSITION ) {
222+ // If we don't need to restart the loader, and there's a desired position to restore
223+ // to, do so now.
224+ mListView .smoothScrollToPosition (mPosition );
225+ }
186226 }
187227
188228 @ Override
189229 public void onLoaderReset (Loader <Cursor > cursorLoader ) {
190230 mForecastAdapter .swapCursor (null );
191231 }
192- }
232+ }
0 commit comments