00ed6d5fcec953ee90a36b215864f750ca45a364
[debian/openrocket] / android / src / net / sf / openrocket / android / util / ExpandableListFragment.java
1 package net.sf.openrocket.android.util;\r
2 \r
3 import android.os.Bundle;\r
4 import android.os.Handler;\r
5 import android.view.ContextMenu;\r
6 import android.view.ContextMenu.ContextMenuInfo;\r
7 import android.view.Gravity;\r
8 import android.view.LayoutInflater;\r
9 import android.view.View;\r
10 import android.view.View.OnCreateContextMenuListener;\r
11 import android.view.ViewGroup;\r
12 import android.view.animation.AnimationUtils;\r
13 import android.widget.AdapterView;\r
14 import android.widget.ExpandableListAdapter;\r
15 import android.widget.ExpandableListView;\r
16 import android.widget.FrameLayout;\r
17 import android.widget.ListAdapter;\r
18 import android.widget.ListView;\r
19 import android.widget.TextView;\r
20 \r
21 import com.actionbarsherlock.app.SherlockFragment;\r
22 \r
23 /**\r
24  * \r
25  * Pulled from https://gist.github.com/1316903\r
26  * \r
27  * This class has originally been taken from\r
28  * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for-compatibility-package\r
29  * and then modified by Manfred Moser <manfred@simpligility.com> to get it to work with the v4 r4 compatibility\r
30  * library. With inspirations from the library source.\r
31  *\r
32  * All ASLv2 licensed.\r
33  */\r
34 public class ExpandableListFragment extends SherlockFragment\r
35 implements OnCreateContextMenuListener, ExpandableListView.OnChildClickListener,\r
36 ExpandableListView.OnGroupCollapseListener, ExpandableListView.OnGroupExpandListener\r
37 {\r
38 \r
39         static final int INTERNAL_EMPTY_ID = 0x00ff0001;\r
40 \r
41         final private Handler mHandler = new Handler();\r
42 \r
43         final private Runnable mRequestFocus = new Runnable() {\r
44                 public void run() {\r
45                         mList.focusableViewAvailable(mList);\r
46                 }\r
47         };\r
48 \r
49         final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {\r
50                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {\r
51                         onListItemClick((ListView) parent, v, position, id);\r
52                 }\r
53         };\r
54 \r
55         ExpandableListAdapter mAdapter;\r
56         ExpandableListView mList;\r
57         View mEmptyView;\r
58         TextView mStandardEmptyView;\r
59         View mListContainer;\r
60         boolean mSetEmptyText;\r
61         boolean mListShown;\r
62         boolean mFinishedStart = false;\r
63 \r
64         public ExpandableListFragment() {\r
65         }\r
66 \r
67         /**\r
68          * Provide default implementation to return a simple list view. Subclasses\r
69          * can override to replace with their own layout. If doing so, the\r
70          * returned view hierarchy <em>must</em> have a ListView whose id\r
71          * is {@link android.R.id#list android.R.id.list} and can optionally\r
72          * have a sibling view id {@link android.R.id#empty android.R.id.empty}\r
73          * that is to be shown when the list is empty.\r
74          * <p/>\r
75          * <p>If you are overriding this method with your own custom content,\r
76          * consider including the standard layout {@link android.R.layout#list_content}\r
77          * in your layout file, so that you continue to retain all of the standard\r
78          * behavior of ListFragment. In particular, this is currently the only\r
79          * way to have the built-in indeterminant progress state be shown.\r
80          */\r
81         @Override\r
82         public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\r
83                 FrameLayout root = new FrameLayout(getActivity());\r
84 \r
85                 TextView tv = new TextView(getActivity());\r
86                 tv.setId(INTERNAL_EMPTY_ID);\r
87                 tv.setGravity(Gravity.CENTER);\r
88                 root.addView(tv,\r
89                                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));\r
90 \r
91                 ExpandableListView lv = new ExpandableListView(getActivity());\r
92                 lv.setId(android.R.id.list);\r
93                 lv.setDrawSelectorOnTop(false);\r
94                 lv.setOnChildClickListener(this);\r
95                 lv.setOnGroupExpandListener(this);\r
96                 lv.setOnGroupCollapseListener(this);\r
97 \r
98                 root.addView(lv, new FrameLayout.LayoutParams(\r
99                                 ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));\r
100 \r
101                 ListView.LayoutParams lp =\r
102                                 new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);\r
103                 root.setLayoutParams(lp);\r
104 \r
105                 return root;\r
106         }\r
107 \r
108         /**\r
109          * Attach to list view once the view hierarchy has been created.\r
110          */\r
111         @Override\r
112         public void onViewCreated(View view, Bundle savedInstanceState) {\r
113                 super.onViewCreated(view, savedInstanceState);\r
114                 ensureList();\r
115         }\r
116 \r
117         /** Detach from list view. */\r
118         @Override\r
119         public void onDestroyView() {\r
120                 mHandler.removeCallbacks(mRequestFocus);\r
121                 mList = null;\r
122                 super.onDestroyView();\r
123         }\r
124 \r
125         /**\r
126          * This method will be called when an item in the list is selected.\r
127          * Subclasses should override. Subclasses can call\r
128          * getListView().getItemAtPosition(position) if they need to access the\r
129          * data associated with the selected item.\r
130          * @param l The ListView where the click happened\r
131          * @param v The view that was clicked within the ListView\r
132          * @param position The position of the view in the list\r
133          * @param id The row id of the item that was clicked\r
134          */\r
135         public void onListItemClick(ListView l, View v, int position, long id) {\r
136         }\r
137 \r
138         /** Provide the cursor for the list view. */\r
139         public void setListAdapter(ExpandableListAdapter adapter) {\r
140                 boolean hadAdapter = mAdapter != null;\r
141                 mAdapter = adapter;\r
142                 if (mList != null) {\r
143                         mList.setAdapter((ExpandableListAdapter) null);\r
144                         mList.setAdapter(adapter);\r
145                         if (!mListShown && !hadAdapter) {\r
146                                 // The list was hidden, and previously didn't have an\r
147                                 // adapter. It is now time to show it.\r
148                                 setListShown(true, getView().getWindowToken() != null);\r
149                         }\r
150                 }\r
151         }\r
152 \r
153         /**\r
154          * Set the currently selected list item to the specified\r
155          * position with the adapter's data\r
156          */\r
157         public void setSelection(int position) {\r
158                 ensureList();\r
159                 mList.setSelection(position);\r
160         }\r
161 \r
162         public long getSelectedPosition() {\r
163                 ensureList();\r
164                 return mList.getSelectedPosition();\r
165         }\r
166 \r
167         public long getSelectedId() {\r
168                 ensureList();\r
169                 return mList.getSelectedId();\r
170         }\r
171 \r
172         public ExpandableListView getExpandableListView() {\r
173                 ensureList();\r
174                 return mList;\r
175         }\r
176 \r
177         /**\r
178          * The default content for a ListFragment has a TextView that can\r
179          * be shown when the list is empty. If you would like to have it\r
180          * shown, call this method to supply the text it should use.\r
181          */\r
182         public void setEmptyText(CharSequence text) {\r
183                 ensureList();\r
184                 if (mStandardEmptyView == null) {\r
185                         throw new IllegalStateException("Can't be used with a custom content view");\r
186                 }\r
187                 mStandardEmptyView.setText(text);\r
188                 if (!mSetEmptyText) {\r
189                         mList.setEmptyView(mStandardEmptyView);\r
190                         mSetEmptyText = true;\r
191                 }\r
192         }\r
193 \r
194         /**\r
195          * Control whether the list is being displayed. You can make it not\r
196          * displayed if you are waiting for the initial data to show in it. During\r
197          * this time an indeterminant progress indicator will be shown instead.\r
198          * <p/>\r
199          * <p>Applications do not normally need to use this themselves. The default\r
200          * behavior of ListFragment is to start with the list not being shown, only\r
201          * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.\r
202          * If the list at that point had not been shown, when it does get shown\r
203          * it will be do without the user ever seeing the hidden state.\r
204          * @param shown If true, the list view is shown; if false, the progress\r
205          * indicator. The initial value is true.\r
206          */\r
207         public void setListShown(boolean shown) {\r
208                 setListShown(shown, true);\r
209         }\r
210 \r
211         /**\r
212          * Like {@link #setListShown(boolean)}, but no animation is used when\r
213          * transitioning from the previous state.\r
214          */\r
215         public void setListShownNoAnimation(boolean shown) {\r
216                 setListShown(shown, false);\r
217         }\r
218 \r
219         /**\r
220          * Control whether the list is being displayed. You can make it not\r
221          * displayed if you are waiting for the initial data to show in it. During\r
222          * this time an indeterminant progress indicator will be shown instead.\r
223          * @param shown If true, the list view is shown; if false, the progress\r
224          * indicator. The initial value is true.\r
225          * @param animate If true, an animation will be used to transition to the\r
226          * new state.\r
227          */\r
228         private void setListShown(boolean shown, boolean animate) {\r
229                 ensureList();\r
230                 if (mListShown == shown) {\r
231                         return;\r
232                 }\r
233                 mListShown = shown;\r
234                 if (mListContainer != null) {\r
235                         if (shown) {\r
236                                 if (animate) {\r
237                                         mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));\r
238                                 }\r
239                                 mListContainer.setVisibility(View.VISIBLE);\r
240                         } else {\r
241                                 if (animate) {\r
242                                         mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out));\r
243                                 }\r
244                                 mListContainer.setVisibility(View.GONE);\r
245                         }\r
246                 }\r
247         }\r
248 \r
249         /** Get the ListAdapter associated with this activity's ListView. */\r
250         public ExpandableListAdapter getExpandableListAdapter() {\r
251                 return mAdapter;\r
252         }\r
253 \r
254         private void ensureList() {\r
255                 if (mList != null) {\r
256                         return;\r
257                 }\r
258                 View root = getView();\r
259                 if (root == null) {\r
260                         throw new IllegalStateException("Content view not yet created");\r
261                 }\r
262                 if (root instanceof ExpandableListView) {\r
263                         mList = (ExpandableListView) root;\r
264                 } else {\r
265                         mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID);\r
266                         if (mStandardEmptyView == null) {\r
267                                 mEmptyView = root.findViewById(android.R.id.empty);\r
268                         }\r
269                         mListContainer = root.findViewById(android.R.id.list);\r
270                         View rawListView = root.findViewById(android.R.id.list);\r
271                         if (!(rawListView instanceof ExpandableListView)) {\r
272                                 if (rawListView == null) {\r
273                                         throw new RuntimeException("Your content must have a ExpandableListView whose id attribute is " +\r
274                                                         "'android.R.id.list'");\r
275                                 }\r
276                                 throw new RuntimeException("Content has view with id attribute 'android.R.id.list' " +\r
277                                                 "that is not a ExpandableListView class");\r
278                         }\r
279                         mList = (ExpandableListView) rawListView;\r
280                         if (mEmptyView != null) {\r
281                                 mList.setEmptyView(mEmptyView);\r
282                         }\r
283                 }\r
284                 mListShown = true;\r
285                 mList.setOnItemClickListener(mOnClickListener);\r
286                 if (mAdapter != null) {\r
287                         setListAdapter(mAdapter);\r
288                 } else {\r
289                         // We are starting without an adapter, so assume we won't\r
290                         // have our data right away and start with the progress indicator.\r
291                         setListShown(false, false);\r
292                 }\r
293                 mHandler.post(mRequestFocus);\r
294         }\r
295 \r
296         @Override\r
297         public void onGroupExpand(int arg0) {\r
298                 // TODO Auto-generated method stub\r
299 \r
300         }\r
301 \r
302         @Override\r
303         public void onGroupCollapse(int arg0) {\r
304                 // TODO Auto-generated method stub\r
305 \r
306         }\r
307 \r
308         @Override\r
309         public boolean onChildClick(ExpandableListView arg0, View arg1, int arg2, int arg3, long arg4) {\r
310                 // TODO Auto-generated method stub\r
311                 return false;\r
312         }\r
313 \r
314         @Override\r
315         public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {\r
316         }\r
317 \r
318         public void onContentChanged() {\r
319                 View emptyView = getView().findViewById(android.R.id.empty);\r
320                 mList = (ExpandableListView) getView().findViewById(android.R.id.list);\r
321                 if (mList == null) {\r
322                         throw new RuntimeException(\r
323                                         "Your content must have a ExpandableListView whose id attribute is " + "'android.R.id.list'");\r
324                 }\r
325                 if (emptyView != null) {\r
326                         mList.setEmptyView(emptyView);\r
327                 }\r
328                 mList.setOnChildClickListener(this);\r
329                 mList.setOnGroupExpandListener(this);\r
330                 mList.setOnGroupCollapseListener(this);\r
331 \r
332                 if (mFinishedStart) {\r
333                         setListAdapter(mAdapter);\r
334                 }\r
335                 mFinishedStart = true;\r
336         }\r
337 }\r
338 \r