Added support library for TreeViewList. Previously this code was subsumed in the...
[debian/openrocket] / android / src / pl / polidea / treeview / AbstractTreeViewAdapter.java
1 package pl.polidea.treeview;
2
3 import net.sf.openrocket.R;
4 import android.app.Activity;
5 import android.content.Context;
6 import android.database.DataSetObserver;
7 import android.graphics.drawable.Drawable;
8 import android.view.LayoutInflater;
9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.view.ViewGroup;
12 import android.widget.BaseAdapter;
13 import android.widget.FrameLayout;
14 import android.widget.FrameLayout.LayoutParams;
15 import android.widget.ImageView;
16 import android.widget.ImageView.ScaleType;
17 import android.widget.LinearLayout;
18 import android.widget.ListAdapter;
19
20 /**
21  * Adapter used to feed the table view.
22  * 
23  * @param <T>
24  *            class for ID of the tree
25  */
26 public abstract class AbstractTreeViewAdapter<T> extends BaseAdapter implements
27         ListAdapter {
28     private final TreeStateManager<T> treeStateManager;
29     private final int numberOfLevels;
30     private final LayoutInflater layoutInflater;
31
32     private int indentWidth = 0;
33     private int indicatorGravity = 0;
34     private Drawable collapsedDrawable;
35     private Drawable expandedDrawable;
36     private Drawable indicatorBackgroundDrawable;
37     private Drawable rowBackgroundDrawable;
38
39     private final OnClickListener indicatorClickListener = new OnClickListener() {
40         @Override
41         public void onClick(final View v) {
42             @SuppressWarnings("unchecked")
43             final T id = (T) v.getTag();
44             expandCollapse(id);
45         }
46     };
47
48     private boolean collapsible;
49     private final Activity activity;
50
51     public Activity getActivity() {
52         return activity;
53     }
54
55     protected TreeStateManager<T> getManager() {
56         return treeStateManager;
57     }
58
59     protected void expandCollapse(final T id) {
60         final TreeNodeInfo<T> info = treeStateManager.getNodeInfo(id);
61         if (!info.isWithChildren()) {
62             // ignore - no default action
63             return;
64         }
65         if (info.isExpanded()) {
66             treeStateManager.collapseChildren(id);
67         } else {
68             treeStateManager.expandDirectChildren(id);
69         }
70     }
71
72     private void calculateIndentWidth() {
73         if (expandedDrawable != null) {
74             indentWidth = Math.max(getIndentWidth(),
75                     expandedDrawable.getIntrinsicWidth());
76         }
77         if (collapsedDrawable != null) {
78             indentWidth = Math.max(getIndentWidth(),
79                     collapsedDrawable.getIntrinsicWidth());
80         }
81     }
82
83     public AbstractTreeViewAdapter(final Activity activity,
84             final TreeStateManager<T> treeStateManager, final int numberOfLevels) {
85         this.activity = activity;
86         this.treeStateManager = treeStateManager;
87         this.layoutInflater = (LayoutInflater) activity
88                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
89         this.numberOfLevels = numberOfLevels;
90         this.collapsedDrawable = null;
91         this.expandedDrawable = null;
92         this.rowBackgroundDrawable = null;
93         this.indicatorBackgroundDrawable = null;
94     }
95
96     @Override
97     public void registerDataSetObserver(final DataSetObserver observer) {
98         treeStateManager.registerDataSetObserver(observer);
99     }
100
101     @Override
102     public void unregisterDataSetObserver(final DataSetObserver observer) {
103         treeStateManager.unregisterDataSetObserver(observer);
104     }
105
106     @Override
107     public int getCount() {
108         return treeStateManager.getVisibleCount();
109     }
110
111     @Override
112     public Object getItem(final int position) {
113         return getItemId(position);
114     }
115
116     public T getTreeId(final int position) {
117         return treeStateManager.getVisibleList().get(position);
118     }
119
120     public TreeNodeInfo<T> getTreeNodeInfo(final int position) {
121         return treeStateManager.getNodeInfo(getTreeId(position));
122     }
123
124     @Override
125     public boolean hasStableIds() { // NOPMD
126         return true;
127     }
128
129     @Override
130     public int getItemViewType(final int position) {
131         return getTreeNodeInfo(position).getLevel();
132     }
133
134     @Override
135     public int getViewTypeCount() {
136         return numberOfLevels;
137     }
138
139     @Override
140     public boolean isEmpty() {
141         return getCount() == 0;
142     }
143
144     @Override
145     public boolean areAllItemsEnabled() { // NOPMD
146         return true;
147     }
148
149     @Override
150     public boolean isEnabled(final int position) { // NOPMD
151         return true;
152     }
153
154     protected int getTreeListItemWrapperId() {
155         return R.layout.tree_list_item_wrapper;
156     }
157
158     @Override
159     public final View getView(final int position, final View convertView,
160             final ViewGroup parent) {
161         final TreeNodeInfo<T> nodeInfo = getTreeNodeInfo(position);
162         if (convertView == null) {
163             final LinearLayout layout = (LinearLayout) layoutInflater.inflate(
164                     getTreeListItemWrapperId(), null);
165             return populateTreeItem(layout, getNewChildView(nodeInfo),
166                     nodeInfo, true);
167         } else {
168             final LinearLayout linear = (LinearLayout) convertView;
169             final FrameLayout frameLayout = (FrameLayout) linear
170                     .findViewById(R.id.treeview_list_item_frame);
171             final View childView = frameLayout.getChildAt(0);
172             updateView(childView, nodeInfo);
173             return populateTreeItem(linear, childView, nodeInfo, false);
174         }
175     }
176
177     /**
178      * Called when new view is to be created.
179      * 
180      * @param treeNodeInfo
181      *            node info
182      * @return view that should be displayed as tree content
183      */
184     public abstract View getNewChildView(TreeNodeInfo<T> treeNodeInfo);
185
186     /**
187      * Called when new view is going to be reused. You should update the view
188      * and fill it in with the data required to display the new information. You
189      * can also create a new view, which will mean that the old view will not be
190      * reused.
191      * 
192      * @param view
193      *            view that should be updated with the new values
194      * @param treeNodeInfo
195      *            node info used to populate the view
196      * @return view to used as row indented content
197      */
198     public abstract View updateView(View view, TreeNodeInfo<T> treeNodeInfo);
199
200     /**
201      * Retrieves background drawable for the node.
202      * 
203      * @param treeNodeInfo
204      *            node info
205      * @return drawable returned as background for the whole row. Might be null,
206      *         then default background is used
207      */
208     public Drawable getBackgroundDrawable(final TreeNodeInfo<T> treeNodeInfo) { // NOPMD
209         return null;
210     }
211
212     private Drawable getDrawableOrDefaultBackground(final Drawable r) {
213         if (r == null) {
214             return activity.getResources()
215                     .getDrawable(R.drawable.list_selector_background).mutate();
216         } else {
217             return r;
218         }
219     }
220
221     public final LinearLayout populateTreeItem(final LinearLayout layout,
222             final View childView, final TreeNodeInfo<T> nodeInfo,
223             final boolean newChildView) {
224         final Drawable individualRowDrawable = getBackgroundDrawable(nodeInfo);
225         layout.setBackgroundDrawable(individualRowDrawable == null ? getDrawableOrDefaultBackground(rowBackgroundDrawable)
226                 : individualRowDrawable);
227         final LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(
228                 calculateIndentation(nodeInfo), LayoutParams.FILL_PARENT);
229         final LinearLayout indicatorLayout = (LinearLayout) layout
230                 .findViewById(R.id.treeview_list_item_image_layout);
231         indicatorLayout.setGravity(indicatorGravity);
232         indicatorLayout.setLayoutParams(indicatorLayoutParams);
233         final ImageView image = (ImageView) layout
234                 .findViewById(R.id.treeview_list_item_image);
235         image.setImageDrawable(getDrawable(nodeInfo));
236         image.setBackgroundDrawable(getDrawableOrDefaultBackground(indicatorBackgroundDrawable));
237         image.setScaleType(ScaleType.CENTER);
238         image.setTag(nodeInfo.getId());
239         if (nodeInfo.isWithChildren() && collapsible) {
240             image.setOnClickListener(indicatorClickListener);
241         } else {
242             image.setOnClickListener(null);
243         }
244         layout.setTag(nodeInfo.getId());
245         final FrameLayout frameLayout = (FrameLayout) layout
246                 .findViewById(R.id.treeview_list_item_frame);
247         final FrameLayout.LayoutParams childParams = new FrameLayout.LayoutParams(
248                 LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
249         if (newChildView) {
250             frameLayout.addView(childView, childParams);
251         }
252         frameLayout.setTag(nodeInfo.getId());
253         return layout;
254     }
255
256     protected int calculateIndentation(final TreeNodeInfo<T> nodeInfo) {
257         return getIndentWidth() * (nodeInfo.getLevel() + (collapsible ? 1 : 0));
258     }
259
260     private Drawable getDrawable(final TreeNodeInfo<T> nodeInfo) {
261         if (!nodeInfo.isWithChildren() || !collapsible) {
262             return getDrawableOrDefaultBackground(indicatorBackgroundDrawable);
263         }
264         if (nodeInfo.isExpanded()) {
265             return expandedDrawable;
266         } else {
267             return collapsedDrawable;
268         }
269     }
270
271     public void setIndicatorGravity(final int indicatorGravity) {
272         this.indicatorGravity = indicatorGravity;
273     }
274
275     public void setCollapsedDrawable(final Drawable collapsedDrawable) {
276         this.collapsedDrawable = collapsedDrawable;
277         calculateIndentWidth();
278     }
279
280     public void setExpandedDrawable(final Drawable expandedDrawable) {
281         this.expandedDrawable = expandedDrawable;
282         calculateIndentWidth();
283     }
284
285     public void setIndentWidth(final int indentWidth) {
286         this.indentWidth = indentWidth;
287         calculateIndentWidth();
288     }
289
290     public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {
291         this.rowBackgroundDrawable = rowBackgroundDrawable;
292     }
293
294     public void setIndicatorBackgroundDrawable(
295             final Drawable indicatorBackgroundDrawable) {
296         this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;
297     }
298
299     public void setCollapsible(final boolean collapsible) {
300         this.collapsible = collapsible;
301     }
302
303     public void refresh() {
304         treeStateManager.refresh();
305     }
306
307     private int getIndentWidth() {
308         return indentWidth;
309     }
310
311     @SuppressWarnings("unchecked")
312     public void handleItemClick(final View view, final Object id) {
313         expandCollapse((T) id);
314     }
315
316 }