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