1 package pl.polidea.treeview;
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;
20 * Adapter used to feed the table view.
23 * class for ID of the tree
25 public abstract class AbstractTreeViewAdapter<T> extends BaseAdapter implements
27 private final TreeStateManager<T> treeStateManager;
28 private final int numberOfLevels;
29 private final LayoutInflater layoutInflater;
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;
38 private final OnClickListener indicatorClickListener = new OnClickListener() {
40 public void onClick(final View v) {
41 @SuppressWarnings("unchecked")
42 final T id = (T) v.getTag();
47 private boolean collapsible;
48 private final Activity activity;
50 public Activity getActivity() {
54 protected TreeStateManager<T> getManager() {
55 return treeStateManager;
58 protected void expandCollapse(final T id) {
59 final TreeNodeInfo<T> info = treeStateManager.getNodeInfo(id);
60 if (!info.isWithChildren()) {
61 // ignore - no default action
64 if (info.isExpanded()) {
65 treeStateManager.collapseChildren(id);
67 treeStateManager.expandDirectChildren(id);
71 private void calculateIndentWidth() {
72 if (expandedDrawable != null) {
73 indentWidth = Math.max(getIndentWidth(),
74 expandedDrawable.getIntrinsicWidth());
76 if (collapsedDrawable != null) {
77 indentWidth = Math.max(getIndentWidth(),
78 collapsedDrawable.getIntrinsicWidth());
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;
96 public void registerDataSetObserver(final DataSetObserver observer) {
97 treeStateManager.registerDataSetObserver(observer);
101 public void unregisterDataSetObserver(final DataSetObserver observer) {
102 treeStateManager.unregisterDataSetObserver(observer);
106 public int getCount() {
107 return treeStateManager.getVisibleCount();
111 public Object getItem(final int position) {
112 return getItemId(position);
115 public T getTreeId(final int position) {
116 return treeStateManager.getVisibleList().get(position);
119 public TreeNodeInfo<T> getTreeNodeInfo(final int position) {
120 return treeStateManager.getNodeInfo(getTreeId(position));
124 public boolean hasStableIds() { // NOPMD
129 public int getItemViewType(final int position) {
130 return getTreeNodeInfo(position).getLevel();
134 public int getViewTypeCount() {
135 return numberOfLevels;
139 public boolean isEmpty() {
140 return getCount() == 0;
144 public boolean areAllItemsEnabled() { // NOPMD
149 public boolean isEnabled(final int position) { // NOPMD
153 protected int getTreeListItemWrapperId() {
154 return R.layout.tree_list_item_wrapper;
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),
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);
177 * Called when new view is to be created.
179 * @param treeNodeInfo
181 * @return view that should be displayed as tree content
183 public abstract View getNewChildView(TreeNodeInfo<T> treeNodeInfo);
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
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
197 public abstract View updateView(View view, TreeNodeInfo<T> treeNodeInfo);
200 * Retrieves background drawable for the node.
202 * @param treeNodeInfo
204 * @return drawable returned as background for the whole row. Might be null,
205 * then default background is used
207 public Drawable getBackgroundDrawable(final TreeNodeInfo<T> treeNodeInfo) { // NOPMD
211 private Drawable getDrawableOrDefaultBackground(final Drawable r) {
213 return activity.getResources()
214 .getDrawable(R.drawable.list_selector_background).mutate();
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);
241 image.setOnClickListener(null);
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);
249 frameLayout.addView(childView, childParams);
251 frameLayout.setTag(nodeInfo.getId());
255 protected int calculateIndentation(final TreeNodeInfo<T> nodeInfo) {
256 return getIndentWidth() * (nodeInfo.getLevel() + (collapsible ? 1 : 0));
259 private Drawable getDrawable(final TreeNodeInfo<T> nodeInfo) {
260 if (!nodeInfo.isWithChildren() || !collapsible) {
261 return getDrawableOrDefaultBackground(indicatorBackgroundDrawable);
263 if (nodeInfo.isExpanded()) {
264 return expandedDrawable;
266 return collapsedDrawable;
270 public void setIndicatorGravity(final int indicatorGravity) {
271 this.indicatorGravity = indicatorGravity;
274 public void setCollapsedDrawable(final Drawable collapsedDrawable) {
275 this.collapsedDrawable = collapsedDrawable;
276 calculateIndentWidth();
279 public void setExpandedDrawable(final Drawable expandedDrawable) {
280 this.expandedDrawable = expandedDrawable;
281 calculateIndentWidth();
284 public void setIndentWidth(final int indentWidth) {
285 this.indentWidth = indentWidth;
286 calculateIndentWidth();
289 public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {
290 this.rowBackgroundDrawable = rowBackgroundDrawable;
293 public void setIndicatorBackgroundDrawable(
294 final Drawable indicatorBackgroundDrawable) {
295 this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;
298 public void setCollapsible(final boolean collapsible) {
299 this.collapsible = collapsible;
302 public void refresh() {
303 treeStateManager.refresh();
306 private int getIndentWidth() {
310 @SuppressWarnings("unchecked")
311 public void handleItemClick(final View view, final Object id) {
312 expandCollapse((T) id);