811a5a4a |
1 | /* |
2 | * Copyright (C) 2011 The Android Open Source Project |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | package android.support.v2.app; |
18 | |
19 | import android.os.Bundle; |
20 | import android.os.Handler; |
21 | import android.view.Gravity; |
22 | import android.view.LayoutInflater; |
23 | import android.view.View; |
24 | import android.view.ViewGroup; |
25 | import android.view.animation.AnimationUtils; |
26 | import android.widget.AdapterView; |
27 | import android.widget.FrameLayout; |
28 | import android.widget.ListAdapter; |
29 | import android.widget.ListView; |
30 | import android.widget.TextView; |
31 | |
32 | /** |
33 | * Static library support version of the framework's {@link android.app.ListFragment}. |
34 | * Used to write apps that run on platforms prior to Android 3.0. When running |
35 | * on Android 3.0 or above, this implementation is still used; it does not try |
36 | * to switch to the framework's implementation. See the framework SDK |
37 | * documentation for a class overview. |
38 | */ |
39 | public class ListFragment extends Fragment { |
40 | static final int INTERNAL_EMPTY_ID = 0x00ff0001; |
41 | |
42 | final private Handler mHandler = new Handler(); |
43 | |
44 | final private Runnable mRequestFocus = new Runnable() { |
45 | public void run() { |
46 | mList.focusableViewAvailable(mList); |
47 | } |
48 | }; |
49 | |
50 | final private AdapterView.OnItemClickListener mOnClickListener |
51 | = new AdapterView.OnItemClickListener() { |
52 | public void onItemClick(AdapterView<?> parent, View v, int position, long id) { |
53 | onListItemClick((ListView)parent, v, position, id); |
54 | } |
55 | }; |
56 | |
57 | ListAdapter mAdapter; |
58 | ListView mList; |
59 | View mEmptyView; |
60 | TextView mStandardEmptyView; |
61 | View mProgressContainer; |
62 | View mListContainer; |
63 | boolean mSetEmptyText; |
64 | boolean mListShown; |
65 | |
66 | public ListFragment() { |
67 | } |
68 | |
69 | /** |
70 | * Provide default implementation to return a simple list view. Subclasses |
71 | * can override to replace with their own layout. If doing so, the |
72 | * returned view hierarchy <em>must</em> have a ListView whose id |
73 | * is {@link android.R.id#list android.R.id.list} and can optionally |
74 | * have a sibling view id {@link android.R.id#empty android.R.id.empty} |
75 | * that is to be shown when the list is empty. |
76 | * |
77 | * <p>If you are overriding this method with your own custom content, |
78 | * consider including the standard layout {@link android.R.layout#list_content} |
79 | * in your layout file, so that you continue to retain all of the standard |
80 | * behavior of ListFragment. In particular, this is currently the only |
81 | * way to have the built-in indeterminant progress state be shown. |
82 | */ |
83 | @Override |
84 | public View onCreateView(LayoutInflater inflater, ViewGroup container, |
85 | Bundle savedInstanceState) { |
86 | FrameLayout root = new FrameLayout(getActivity()); |
87 | |
88 | TextView tv = new TextView(getActivity()); |
89 | tv.setId(INTERNAL_EMPTY_ID); |
90 | tv.setGravity(Gravity.CENTER); |
91 | root.addView(tv, new FrameLayout.LayoutParams( |
92 | ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); |
93 | |
94 | ListView lv = new ListView(getActivity()); |
95 | lv.setId(android.R.id.list); |
96 | lv.setDrawSelectorOnTop(false); |
97 | root.addView(lv, new FrameLayout.LayoutParams( |
98 | ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); |
99 | |
100 | ListView.LayoutParams lp = new ListView.LayoutParams( |
101 | ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT); |
102 | root.setLayoutParams(lp); |
103 | |
104 | return root; |
105 | } |
106 | |
107 | /** |
108 | * Attach to list view once Fragment is ready to run. |
109 | */ |
110 | @Override |
111 | public void onActivityCreated(Bundle savedInstanceState) { |
112 | super.onActivityCreated(savedInstanceState); |
113 | ensureList(); |
114 | } |
115 | |
116 | /** |
117 | * Detach from list view. |
118 | */ |
119 | @Override |
120 | public void onDestroyView() { |
121 | mHandler.removeCallbacks(mRequestFocus); |
122 | mList = null; |
123 | super.onDestroyView(); |
124 | } |
125 | |
126 | /** |
127 | * This method will be called when an item in the list is selected. |
128 | * Subclasses should override. Subclasses can call |
129 | * getListView().getItemAtPosition(position) if they need to access the |
130 | * data associated with the selected item. |
131 | * |
132 | * @param l The ListView where the click happened |
133 | * @param v The view that was clicked within the ListView |
134 | * @param position The position of the view in the list |
135 | * @param id The row id of the item that was clicked |
136 | */ |
137 | public void onListItemClick(ListView l, View v, int position, long id) { |
138 | } |
139 | |
140 | /** |
141 | * Provide the cursor for the list view. |
142 | */ |
143 | public void setListAdapter(ListAdapter adapter) { |
144 | boolean hadAdapter = mAdapter != null; |
145 | mAdapter = adapter; |
146 | if (mList != null) { |
147 | mList.setAdapter(adapter); |
148 | if (!mListShown && !hadAdapter) { |
149 | // The list was hidden, and previously didn't have an |
150 | // adapter. It is now time to show it. |
151 | setListShown(true, getView().getWindowToken() != null); |
152 | } |
153 | } |
154 | } |
155 | |
156 | /** |
157 | * Set the currently selected list item to the specified |
158 | * position with the adapter's data |
159 | * |
160 | * @param position |
161 | */ |
162 | public void setSelection(int position) { |
163 | ensureList(); |
164 | mList.setSelection(position); |
165 | } |
166 | |
167 | /** |
168 | * Get the position of the currently selected list item. |
169 | */ |
170 | public int getSelectedItemPosition() { |
171 | ensureList(); |
172 | return mList.getSelectedItemPosition(); |
173 | } |
174 | |
175 | /** |
176 | * Get the cursor row ID of the currently selected list item. |
177 | */ |
178 | public long getSelectedItemId() { |
179 | ensureList(); |
180 | return mList.getSelectedItemId(); |
181 | } |
182 | |
183 | /** |
184 | * Get the activity's list view widget. |
185 | */ |
186 | public ListView getListView() { |
187 | ensureList(); |
188 | return mList; |
189 | } |
190 | |
191 | /** |
192 | * The default content for a ListFragment has a TextView that can |
193 | * be shown when the list is empty. If you would like to have it |
194 | * shown, call this method to supply the text it should use. |
195 | */ |
196 | public void setEmptyText(CharSequence text) { |
197 | ensureList(); |
198 | if (mStandardEmptyView == null) { |
199 | throw new IllegalStateException("Can't be used with a custom content view"); |
200 | } |
201 | mStandardEmptyView.setText(text); |
202 | if (!mSetEmptyText) { |
203 | mList.setEmptyView(mStandardEmptyView); |
204 | mSetEmptyText = true; |
205 | } |
206 | } |
207 | |
208 | /** |
209 | * Control whether the list is being displayed. You can make it not |
210 | * displayed if you are waiting for the initial data to show in it. During |
211 | * this time an indeterminant progress indicator will be shown instead. |
212 | * |
213 | * <p>Applications do not normally need to use this themselves. The default |
214 | * behavior of ListFragment is to start with the list not being shown, only |
215 | * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}. |
216 | * If the list at that point had not been shown, when it does get shown |
217 | * it will be do without the user ever seeing the hidden state. |
218 | * |
219 | * @param shown If true, the list view is shown; if false, the progress |
220 | * indicator. The initial value is true. |
221 | */ |
222 | public void setListShown(boolean shown) { |
223 | setListShown(shown, true); |
224 | } |
225 | |
226 | /** |
227 | * Like {@link #setListShown(boolean)}, but no animation is used when |
228 | * transitioning from the previous state. |
229 | */ |
230 | public void setListShownNoAnimation(boolean shown) { |
231 | setListShown(shown, false); |
232 | } |
233 | |
234 | /** |
235 | * Control whether the list is being displayed. You can make it not |
236 | * displayed if you are waiting for the initial data to show in it. During |
237 | * this time an indeterminant progress indicator will be shown instead. |
238 | * |
239 | * @param shown If true, the list view is shown; if false, the progress |
240 | * indicator. The initial value is true. |
241 | * @param animate If true, an animation will be used to transition to the |
242 | * new state. |
243 | */ |
244 | private void setListShown(boolean shown, boolean animate) { |
245 | ensureList(); |
246 | if (mProgressContainer == null) { |
247 | throw new IllegalStateException("Can't be used with a custom content view"); |
248 | } |
249 | if (mListShown == shown) { |
250 | return; |
251 | } |
252 | mListShown = shown; |
253 | if (shown) { |
254 | if (animate) { |
255 | mProgressContainer.startAnimation(AnimationUtils.loadAnimation( |
256 | getActivity(), android.R.anim.fade_out)); |
257 | mListContainer.startAnimation(AnimationUtils.loadAnimation( |
258 | getActivity(), android.R.anim.fade_in)); |
259 | } |
260 | mProgressContainer.setVisibility(View.GONE); |
261 | mListContainer.setVisibility(View.VISIBLE); |
262 | } else { |
263 | if (animate) { |
264 | mProgressContainer.startAnimation(AnimationUtils.loadAnimation( |
265 | getActivity(), android.R.anim.fade_in)); |
266 | mListContainer.startAnimation(AnimationUtils.loadAnimation( |
267 | getActivity(), android.R.anim.fade_out)); |
268 | } |
269 | mProgressContainer.setVisibility(View.VISIBLE); |
270 | mListContainer.setVisibility(View.GONE); |
271 | } |
272 | } |
273 | |
274 | /** |
275 | * Get the ListAdapter associated with this activity's ListView. |
276 | */ |
277 | public ListAdapter getListAdapter() { |
278 | return mAdapter; |
279 | } |
280 | |
281 | private void ensureList() { |
282 | if (mList != null) { |
283 | return; |
284 | } |
285 | View root = getView(); |
286 | if (root == null) { |
287 | throw new IllegalStateException("Content view not yet created"); |
288 | } |
289 | if (root instanceof ListView) { |
290 | mList = (ListView)root; |
291 | } else { |
292 | mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID); |
293 | if (mStandardEmptyView == null) { |
294 | mEmptyView = root.findViewById(android.R.id.empty); |
295 | } |
296 | mProgressContainer = null; //root.findViewById(com.android.internal.R.id.progressContainer); |
297 | mListContainer = null; //root.findViewById(com.android.internal.R.id.listContainer); |
298 | View rawListView = root.findViewById(android.R.id.list); |
299 | if (!(rawListView instanceof ListView)) { |
300 | if (rawListView == null) { |
301 | throw new RuntimeException( |
302 | "Your content must have a ListView whose id attribute is " + |
303 | "'android.R.id.list'"); |
304 | } |
305 | throw new RuntimeException( |
306 | "Content has view with id attribute 'android.R.id.list' " |
307 | + "that is not a ListView class"); |
308 | } |
309 | mList = (ListView)rawListView; |
310 | if (mEmptyView != null) { |
311 | mList.setEmptyView(mEmptyView); |
312 | } |
313 | } |
314 | mListShown = true; |
315 | mList.setOnItemClickListener(mOnClickListener); |
316 | if (mAdapter != null) { |
317 | setListAdapter(mAdapter); |
318 | } else { |
319 | // We are starting without an adapter, so assume we won't |
320 | // have our data right away and start with the progress indicator. |
321 | if (mProgressContainer != null) { |
322 | setListShown(false, false); |
323 | } |
324 | } |
325 | mHandler.post(mRequestFocus); |
326 | } |
327 | } |