--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="src" path="gen"/>\r
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>\r
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>\r
+ <classpathentry kind="output" path="bin/classes"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>TreeViewList</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"\r
+ package="pl.polidea.treeview"\r
+ android:versionCode="1"\r
+ android:versionName="1.0" >\r
+\r
+ <uses-sdk android:minSdkVersion="7" />\r
+\r
+ <application/>\r
+\r
+</manifest>
\ No newline at end of file
--- /dev/null
+<html>
+<body>
+This is a small utility that provides quite configurable tree view list.
+It is based on standard android list view. It separates out different
+aspects of the tree: there is a separate list view, tree adapter, tree
+state manager and tree state builder.
+<p>
+<ul>
+ <li>Tree view provides the frame to display the view.</li>
+ <li>Adapter allows to create visual representation of each tree
+ node.</li>
+ <li>State manager provides storage for tree state (connections
+ between parents and children, collapsed/expanded state). It provides
+ all the low-level tree manipulation methods.</li>
+ <li>Tree builder allows to build tree easily providing higher
+ level methods. The tree can be build either from prepared sequentially
+ prepared list of nodes (node id, level) or using (parent/child
+ relationships).</li>
+ <p>For now only in-memory state manager is provided, but Tree State
+ Manger interface is done in the way that database tree manager even for
+ large trees is potentially supported.
+</ul>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+# cache for current jar dependecy. DO NOT EDIT.
+# format is <lastModified> <length> <SHA-1> <path>
+# Encoding is UTF-8
--- /dev/null
+/** Automatically generated file. DO NOT MODIFY */
+package pl.polidea.treeview;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
--- /dev/null
+/* AUTO-GENERATED FILE. DO NOT MODIFY.\r
+ *\r
+ * This class was automatically generated by the\r
+ * aapt tool from the resource data it found. It\r
+ * should not be modified by hand.\r
+ */\r
+\r
+package pl.polidea.treeview;\r
+\r
+public final class R {\r
+ public static final class attr {\r
+ /** <p>Must be a boolean value, either "<code>true</code>" or "<code>false</code>".\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ */\r
+ public static int collapsible=0x7f010000;\r
+ /** <p>Must be a boolean value, either "<code>true</code>" or "<code>false</code>".\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ */\r
+ public static int handle_trackball_press=0x7f010004;\r
+ /** <p>Must be a dimension value, which is a floating point number appended with a unit such as "<code>14.5sp</code>".\r
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\r
+in (inches), mm (millimeters).\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ */\r
+ public static int indent_width=0x7f010003;\r
+ /** <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ */\r
+ public static int indicator_background=0x7f010006;\r
+ /** <p>Must be one or more (separated by '|') of the following constant values.</p>\r
+<table>\r
+<colgroup align="left" />\r
+<colgroup align="left" />\r
+<colgroup align="left" />\r
+<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\r
+<tr><td><code>top</code></td><td>0x30</td><td> Push object to the top of its container, not changing its size. </td></tr>\r
+<tr><td><code>bottom</code></td><td>0x50</td><td> Push object to the bottom of its container, not changing its size. </td></tr>\r
+<tr><td><code>left</code></td><td>0x03</td><td> Push object to the left of its container, not changing its size. </td></tr>\r
+<tr><td><code>right</code></td><td>0x05</td><td> Push object to the right of its container, not changing its size. </td></tr>\r
+<tr><td><code>center_vertical</code></td><td>0x10</td><td> Place object in the vertical center of its container, not changing its size. </td></tr>\r
+<tr><td><code>fill_vertical</code></td><td>0x70</td><td> Grow the vertical size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>center_horizontal</code></td><td>0x01</td><td> Place object in the horizontal center of its container, not changing its size. </td></tr>\r
+<tr><td><code>fill_horizontal</code></td><td>0x07</td><td> Grow the horizontal size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>center</code></td><td>0x11</td><td> Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. </td></tr>\r
+<tr><td><code>fill</code></td><td>0x77</td><td> Grow the horizontal and vertical size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>clip_vertical</code></td><td>0x80</td><td> Additional option that can be set to have the top and/or bottom edges of the child clipped to its container's bounds.\r
+ The clip will be based on the vertical gravity: a top gravity will clip the bottom edge, a bottom gravity will clip the top\r
+ edge, and neither will clip both edges. </td></tr>\r
+<tr><td><code>clip_horizontal</code></td><td>0x08</td><td> Additional option that can be set to have the left and/or right edges of the child clipped to its container's bounds.\r
+ The clip will be based on the horizontal gravity: a left gravity will clip the right edge, a right gravity will clip the\r
+ left edge, and neither will clip both edges. </td></tr>\r
+</table>\r
+ */\r
+ public static int indicator_gravity=0x7f010005;\r
+ /** <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ */\r
+ public static int row_background=0x7f010007;\r
+ /** <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ */\r
+ public static int src_collapsed=0x7f010002;\r
+ /** <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ */\r
+ public static int src_expanded=0x7f010001;\r
+ }\r
+ public static final class drawable {\r
+ public static int collapsed=0x7f020000;\r
+ public static int divider=0x7f020001;\r
+ public static int expanded=0x7f020002;\r
+ public static int ic_launcher=0x7f020003;\r
+ public static int list_selector_background=0x7f020004;\r
+ public static int list_selector_background_disabled=0x7f020005;\r
+ public static int list_selector_background_focus=0x7f020006;\r
+ public static int list_selector_background_longpress=0x7f020007;\r
+ public static int list_selector_background_pressed=0x7f020008;\r
+ public static int list_selector_background_transition=0x7f020009;\r
+ }\r
+ public static final class id {\r
+ public static int bottom=0x7f040001;\r
+ public static int center=0x7f040008;\r
+ public static int center_horizontal=0x7f040006;\r
+ public static int center_vertical=0x7f040004;\r
+ public static int clip_horizontal=0x7f04000b;\r
+ public static int clip_vertical=0x7f04000a;\r
+ public static int fill=0x7f040009;\r
+ public static int fill_horizontal=0x7f040007;\r
+ public static int fill_vertical=0x7f040005;\r
+ public static int left=0x7f040002;\r
+ public static int right=0x7f040003;\r
+ public static int top=0x7f040000;\r
+ public static int treeview_list_item_frame=0x7f04000e;\r
+ public static int treeview_list_item_image=0x7f04000d;\r
+ public static int treeview_list_item_image_layout=0x7f04000c;\r
+ }\r
+ public static final class layout {\r
+ public static int tree_list_item_wrapper=0x7f030000;\r
+ }\r
+ public static final class style {\r
+ public static int treeViewListStyle=0x7f050000;\r
+ }\r
+ public static final class styleable {\r
+ /** Attributes that can be used with a TreeViewList.\r
+ <p>Includes the following attributes:</p>\r
+ <table>\r
+ <colgroup align="left" />\r
+ <colgroup align="left" />\r
+ <tr><th>Attribute</th><th>Description</th></tr>\r
+ <tr><td><code>{@link #TreeViewList_collapsible pl.polidea.treeview:collapsible}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_handle_trackball_press pl.polidea.treeview:handle_trackball_press}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_indent_width pl.polidea.treeview:indent_width}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_indicator_background pl.polidea.treeview:indicator_background}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_indicator_gravity pl.polidea.treeview:indicator_gravity}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_row_background pl.polidea.treeview:row_background}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_src_collapsed pl.polidea.treeview:src_collapsed}</code></td><td></td></tr>\r
+ <tr><td><code>{@link #TreeViewList_src_expanded pl.polidea.treeview:src_expanded}</code></td><td></td></tr>\r
+ </table>\r
+ @see #TreeViewList_collapsible\r
+ @see #TreeViewList_handle_trackball_press\r
+ @see #TreeViewList_indent_width\r
+ @see #TreeViewList_indicator_background\r
+ @see #TreeViewList_indicator_gravity\r
+ @see #TreeViewList_row_background\r
+ @see #TreeViewList_src_collapsed\r
+ @see #TreeViewList_src_expanded\r
+ */\r
+ public static final int[] TreeViewList = {\r
+ 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,\r
+ 0x7f010004, 0x7f010005, 0x7f010006, 0x7f010007\r
+ };\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#collapsible}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>Must be a boolean value, either "<code>true</code>" or "<code>false</code>".\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ @attr name android:collapsible\r
+ */\r
+ public static final int TreeViewList_collapsible = 0;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#handle_trackball_press}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>Must be a boolean value, either "<code>true</code>" or "<code>false</code>".\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ @attr name android:handle_trackball_press\r
+ */\r
+ public static final int TreeViewList_handle_trackball_press = 4;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#indent_width}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>Must be a dimension value, which is a floating point number appended with a unit such as "<code>14.5sp</code>".\r
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\r
+in (inches), mm (millimeters).\r
+<p>This may also be a reference to a resource (in the form\r
+"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or\r
+theme attribute (in the form\r
+"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")\r
+containing a value of this type.\r
+ @attr name android:indent_width\r
+ */\r
+ public static final int TreeViewList_indent_width = 3;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#indicator_background}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ @attr name android:indicator_background\r
+ */\r
+ public static final int TreeViewList_indicator_background = 6;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#indicator_gravity}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>Must be one or more (separated by '|') of the following constant values.</p>\r
+<table>\r
+<colgroup align="left" />\r
+<colgroup align="left" />\r
+<colgroup align="left" />\r
+<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\r
+<tr><td><code>top</code></td><td>0x30</td><td> Push object to the top of its container, not changing its size. </td></tr>\r
+<tr><td><code>bottom</code></td><td>0x50</td><td> Push object to the bottom of its container, not changing its size. </td></tr>\r
+<tr><td><code>left</code></td><td>0x03</td><td> Push object to the left of its container, not changing its size. </td></tr>\r
+<tr><td><code>right</code></td><td>0x05</td><td> Push object to the right of its container, not changing its size. </td></tr>\r
+<tr><td><code>center_vertical</code></td><td>0x10</td><td> Place object in the vertical center of its container, not changing its size. </td></tr>\r
+<tr><td><code>fill_vertical</code></td><td>0x70</td><td> Grow the vertical size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>center_horizontal</code></td><td>0x01</td><td> Place object in the horizontal center of its container, not changing its size. </td></tr>\r
+<tr><td><code>fill_horizontal</code></td><td>0x07</td><td> Grow the horizontal size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>center</code></td><td>0x11</td><td> Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. </td></tr>\r
+<tr><td><code>fill</code></td><td>0x77</td><td> Grow the horizontal and vertical size of the object if needed so it completely fills its container. </td></tr>\r
+<tr><td><code>clip_vertical</code></td><td>0x80</td><td> Additional option that can be set to have the top and/or bottom edges of the child clipped to its container's bounds.\r
+ The clip will be based on the vertical gravity: a top gravity will clip the bottom edge, a bottom gravity will clip the top\r
+ edge, and neither will clip both edges. </td></tr>\r
+<tr><td><code>clip_horizontal</code></td><td>0x08</td><td> Additional option that can be set to have the left and/or right edges of the child clipped to its container's bounds.\r
+ The clip will be based on the horizontal gravity: a left gravity will clip the right edge, a right gravity will clip the\r
+ left edge, and neither will clip both edges. </td></tr>\r
+</table>\r
+ @attr name android:indicator_gravity\r
+ */\r
+ public static final int TreeViewList_indicator_gravity = 5;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#row_background}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ @attr name android:row_background\r
+ */\r
+ public static final int TreeViewList_row_background = 7;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#src_collapsed}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ @attr name android:src_collapsed\r
+ */\r
+ public static final int TreeViewList_src_collapsed = 2;\r
+ /**\r
+ <p>This symbol is the offset where the {@link pl.polidea.treeview.R.attr#src_expanded}\r
+ attribute's value can be found in the {@link #TreeViewList} array.\r
+\r
+\r
+ <p>May be a reference to another resource, in the form "<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>"\r
+or to a theme attribute in the form "<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>".\r
+<p>May be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",\r
+"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".\r
+ @attr name android:src_expanded\r
+ */\r
+ public static final int TreeViewList_src_expanded = 1;\r
+ };\r
+}\r
--- /dev/null
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
--- /dev/null
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-7
+android.library=true
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:scaleType="fitXY" android:paddingLeft="5sp"
+ android:paddingRight="5sp" android:paddingBottom="2sp" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project Licensed under the
+ Apache License, Version 2.0 (the "License"); you may not use this file except
+ in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ OR CONDITIONS OF ANY KIND, either express or implied. See the License for
+ the specific language governing permissions and limitations under the License. -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_window_focused="false" android:drawable="@android:color/transparent" />
+
+ <!-- Even though these two point to the same resource, have two states so
+ the drawable will invalidate itself when coming out of pressed state. -->
+ <item android:state_focused="true" android:state_enabled="false"
+ android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled" />
+ <item android:state_focused="true" android:state_enabled="false"
+ android:drawable="@drawable/list_selector_background_disabled" />
+
+ <item android:state_focused="true" android:state_pressed="true"
+ android:drawable="@drawable/list_selector_background_transition" />
+ <item android:state_focused="false" android:state_pressed="true"
+ android:drawable="@drawable/list_selector_background_transition" />
+
+ <item android:state_focused="true"
+ android:drawable="@drawable/list_selector_background_focus" />
+
+ <!-- !!hack!! to fill StateListDrawable.mStateListState with exactly 10
+ items otherwise it will throw NPE on Android 1.6 -->
+
+ <item android:animationCache="true"
+ android:drawable="@android:color/transparent" />
+
+ <item android:animationCache="false"
+ android:drawable="@android:color/transparent" />
+
+ <item android:alwaysDrawnWithCache="false"
+ android:drawable="@android:color/transparent" />
+
+ <item android:alwaysDrawnWithCache="true"
+ android:drawable="@android:color/transparent" />
+
+
+</selector>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/list_selector_background_pressed" />
+ <item android:drawable="@drawable/list_selector_background_longpress" />
+</transition>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+ <LinearLayout android:id="@+id/treeview_list_item_image_layout" android:layout_width="80dip"
+ android:layout_height="fill_parent" android:gravity="right|center_vertical">
+ <ImageView android:id="@+id/treeview_list_item_image" android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:src="@drawable/collapsed" >
+ </ImageView>
+ </LinearLayout>
+ <FrameLayout android:id="@+id/treeview_list_item_frame" android:layout_width="fill_parent"
+ android:layout_height="fill_parent" android:layout_weight="1">
+ </FrameLayout>
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="TreeViewList">
+ <attr name="collapsible" format="boolean" />
+ <attr name="src_expanded" format="reference|color" />
+ <attr name="src_collapsed" format="reference|color" />
+ <attr name="indent_width" format="dimension" />
+ <attr name="handle_trackball_press" format="boolean" />
+ <attr name="indicator_gravity">
+ <!-- Push object to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30" />
+ <!-- Push object to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50" />
+ <!-- Push object to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03" />
+ <!-- Push object to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05" />
+ <!-- Place object in the vertical center of its container, not changing its size. -->
+ <flag name="center_vertical" value="0x10" />
+ <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill_vertical" value="0x70" />
+ <!-- Place object in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01" />
+ <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+ <flag name="fill_horizontal" value="0x07" />
+ <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+ <flag name="center" value="0x11" />
+ <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill" value="0x77" />
+ <!-- Additional option that can be set to have the top and/or bottom edges of the child clipped to its container's bounds.
+ The clip will be based on the vertical gravity: a top gravity will clip the bottom edge, a bottom gravity will clip the top
+ edge, and neither will clip both edges. -->
+ <flag name="clip_vertical" value="0x80" />
+ <!-- Additional option that can be set to have the left and/or right edges of the child clipped to its container's bounds.
+ The clip will be based on the horizontal gravity: a left gravity will clip the right edge, a right gravity will clip the
+ left edge, and neither will clip both edges. -->
+ <flag name="clip_horizontal" value="0x08" />
+ </attr>
+ <attr name="indicator_background" format="reference|color" />
+ <attr name="row_background" format="reference|color" />
+ </declare-styleable>
+</resources>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style parent="@android:attr/listViewStyle" name="treeViewListStyle">
+ <item name="android:background">@android:color/white</item>
+ <item name="android:divider">@drawable/divider</item>
+ </style>
+</resources>
--- /dev/null
+package pl.polidea.treeview;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+
+/**
+ * Adapter used to feed the table view.
+ *
+ * @param <T>
+ * class for ID of the tree
+ */
+public abstract class AbstractTreeViewAdapter<T> extends BaseAdapter implements
+ ListAdapter {
+ private final TreeStateManager<T> treeStateManager;
+ private final int numberOfLevels;
+ private final LayoutInflater layoutInflater;
+
+ private int indentWidth = 0;
+ private int indicatorGravity = 0;
+ private Drawable collapsedDrawable;
+ private Drawable expandedDrawable;
+ private Drawable indicatorBackgroundDrawable;
+ private Drawable rowBackgroundDrawable;
+
+ private final OnClickListener indicatorClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ @SuppressWarnings("unchecked")
+ final T id = (T) v.getTag();
+ expandCollapse(id);
+ }
+ };
+
+ private boolean collapsible;
+ private final Activity activity;
+
+ public Activity getActivity() {
+ return activity;
+ }
+
+ protected TreeStateManager<T> getManager() {
+ return treeStateManager;
+ }
+
+ protected void expandCollapse(final T id) {
+ final TreeNodeInfo<T> info = treeStateManager.getNodeInfo(id);
+ if (!info.isWithChildren()) {
+ // ignore - no default action
+ return;
+ }
+ if (info.isExpanded()) {
+ treeStateManager.collapseChildren(id);
+ } else {
+ treeStateManager.expandDirectChildren(id);
+ }
+ }
+
+ private void calculateIndentWidth() {
+ if (expandedDrawable != null) {
+ indentWidth = Math.max(getIndentWidth(),
+ expandedDrawable.getIntrinsicWidth());
+ }
+ if (collapsedDrawable != null) {
+ indentWidth = Math.max(getIndentWidth(),
+ collapsedDrawable.getIntrinsicWidth());
+ }
+ }
+
+ public AbstractTreeViewAdapter(final Activity activity,
+ final TreeStateManager<T> treeStateManager, final int numberOfLevels) {
+ this.activity = activity;
+ this.treeStateManager = treeStateManager;
+ this.layoutInflater = (LayoutInflater) activity
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ this.numberOfLevels = numberOfLevels;
+ this.collapsedDrawable = null;
+ this.expandedDrawable = null;
+ this.rowBackgroundDrawable = null;
+ this.indicatorBackgroundDrawable = null;
+ }
+
+ @Override
+ public void registerDataSetObserver(final DataSetObserver observer) {
+ treeStateManager.registerDataSetObserver(observer);
+ }
+
+ @Override
+ public void unregisterDataSetObserver(final DataSetObserver observer) {
+ treeStateManager.unregisterDataSetObserver(observer);
+ }
+
+ @Override
+ public int getCount() {
+ return treeStateManager.getVisibleCount();
+ }
+
+ @Override
+ public Object getItem(final int position) {
+ return getItemId(position);
+ }
+
+ public T getTreeId(final int position) {
+ return treeStateManager.getVisibleList().get(position);
+ }
+
+ public TreeNodeInfo<T> getTreeNodeInfo(final int position) {
+ return treeStateManager.getNodeInfo(getTreeId(position));
+ }
+
+ @Override
+ public boolean hasStableIds() { // NOPMD
+ return true;
+ }
+
+ @Override
+ public int getItemViewType(final int position) {
+ return getTreeNodeInfo(position).getLevel();
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return numberOfLevels;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() { // NOPMD
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final int position) { // NOPMD
+ return true;
+ }
+
+ protected int getTreeListItemWrapperId() {
+ return R.layout.tree_list_item_wrapper;
+ }
+
+ @Override
+ public final View getView(final int position, final View convertView,
+ final ViewGroup parent) {
+ final TreeNodeInfo<T> nodeInfo = getTreeNodeInfo(position);
+ if (convertView == null) {
+ final LinearLayout layout = (LinearLayout) layoutInflater.inflate(
+ getTreeListItemWrapperId(), null);
+ return populateTreeItem(layout, getNewChildView(nodeInfo),
+ nodeInfo, true);
+ } else {
+ final LinearLayout linear = (LinearLayout) convertView;
+ final FrameLayout frameLayout = (FrameLayout) linear
+ .findViewById(R.id.treeview_list_item_frame);
+ final View childView = frameLayout.getChildAt(0);
+ updateView(childView, nodeInfo);
+ return populateTreeItem(linear, childView, nodeInfo, false);
+ }
+ }
+
+ /**
+ * Called when new view is to be created.
+ *
+ * @param treeNodeInfo
+ * node info
+ * @return view that should be displayed as tree content
+ */
+ public abstract View getNewChildView(TreeNodeInfo<T> treeNodeInfo);
+
+ /**
+ * Called when new view is going to be reused. You should update the view
+ * and fill it in with the data required to display the new information. You
+ * can also create a new view, which will mean that the old view will not be
+ * reused.
+ *
+ * @param view
+ * view that should be updated with the new values
+ * @param treeNodeInfo
+ * node info used to populate the view
+ * @return view to used as row indented content
+ */
+ public abstract View updateView(View view, TreeNodeInfo<T> treeNodeInfo);
+
+ /**
+ * Retrieves background drawable for the node.
+ *
+ * @param treeNodeInfo
+ * node info
+ * @return drawable returned as background for the whole row. Might be null,
+ * then default background is used
+ */
+ public Drawable getBackgroundDrawable(final TreeNodeInfo<T> treeNodeInfo) { // NOPMD
+ return null;
+ }
+
+ private Drawable getDrawableOrDefaultBackground(final Drawable r) {
+ if (r == null) {
+ return activity.getResources()
+ .getDrawable(R.drawable.list_selector_background).mutate();
+ } else {
+ return r;
+ }
+ }
+
+ public final LinearLayout populateTreeItem(final LinearLayout layout,
+ final View childView, final TreeNodeInfo<T> nodeInfo,
+ final boolean newChildView) {
+ final Drawable individualRowDrawable = getBackgroundDrawable(nodeInfo);
+ layout.setBackgroundDrawable(individualRowDrawable == null ? getDrawableOrDefaultBackground(rowBackgroundDrawable)
+ : individualRowDrawable);
+ final LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(
+ calculateIndentation(nodeInfo), LayoutParams.FILL_PARENT);
+ final LinearLayout indicatorLayout = (LinearLayout) layout
+ .findViewById(R.id.treeview_list_item_image_layout);
+ indicatorLayout.setGravity(indicatorGravity);
+ indicatorLayout.setLayoutParams(indicatorLayoutParams);
+ final ImageView image = (ImageView) layout
+ .findViewById(R.id.treeview_list_item_image);
+ image.setImageDrawable(getDrawable(nodeInfo));
+ image.setBackgroundDrawable(getDrawableOrDefaultBackground(indicatorBackgroundDrawable));
+ image.setScaleType(ScaleType.CENTER);
+ image.setTag(nodeInfo.getId());
+ if (nodeInfo.isWithChildren() && collapsible) {
+ image.setOnClickListener(indicatorClickListener);
+ } else {
+ image.setOnClickListener(null);
+ }
+ layout.setTag(nodeInfo.getId());
+ final FrameLayout frameLayout = (FrameLayout) layout
+ .findViewById(R.id.treeview_list_item_frame);
+ final FrameLayout.LayoutParams childParams = new FrameLayout.LayoutParams(
+ LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+ if (newChildView) {
+ frameLayout.addView(childView, childParams);
+ }
+ frameLayout.setTag(nodeInfo.getId());
+ return layout;
+ }
+
+ protected int calculateIndentation(final TreeNodeInfo<T> nodeInfo) {
+ return getIndentWidth() * (nodeInfo.getLevel() + (collapsible ? 1 : 0));
+ }
+
+ private Drawable getDrawable(final TreeNodeInfo<T> nodeInfo) {
+ if (!nodeInfo.isWithChildren() || !collapsible) {
+ return getDrawableOrDefaultBackground(indicatorBackgroundDrawable);
+ }
+ if (nodeInfo.isExpanded()) {
+ return expandedDrawable;
+ } else {
+ return collapsedDrawable;
+ }
+ }
+
+ public void setIndicatorGravity(final int indicatorGravity) {
+ this.indicatorGravity = indicatorGravity;
+ }
+
+ public void setCollapsedDrawable(final Drawable collapsedDrawable) {
+ this.collapsedDrawable = collapsedDrawable;
+ calculateIndentWidth();
+ }
+
+ public void setExpandedDrawable(final Drawable expandedDrawable) {
+ this.expandedDrawable = expandedDrawable;
+ calculateIndentWidth();
+ }
+
+ public void setIndentWidth(final int indentWidth) {
+ this.indentWidth = indentWidth;
+ calculateIndentWidth();
+ }
+
+ public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {
+ this.rowBackgroundDrawable = rowBackgroundDrawable;
+ }
+
+ public void setIndicatorBackgroundDrawable(
+ final Drawable indicatorBackgroundDrawable) {
+ this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;
+ }
+
+ public void setCollapsible(final boolean collapsible) {
+ this.collapsible = collapsible;
+ }
+
+ public void refresh() {
+ treeStateManager.refresh();
+ }
+
+ private int getIndentWidth() {
+ return indentWidth;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void handleItemClick(final View view, final Object id) {
+ expandCollapse((T) id);
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Node. It is package protected so that it cannot be used outside.
+ *
+ * @param <T>
+ * type of the identifier used by the tree
+ */
+class InMemoryTreeNode<T> implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private final T id;
+ private final T parent;
+ private final int level;
+ private boolean visible = true;
+ private final List<InMemoryTreeNode<T>> children = new LinkedList<InMemoryTreeNode<T>>();
+ private List<T> childIdListCache = null;
+
+ public InMemoryTreeNode(final T id, final T parent, final int level,
+ final boolean visible) {
+ super();
+ this.id = id;
+ this.parent = parent;
+ this.level = level;
+ this.visible = visible;
+ }
+
+ public int indexOf(final T id) {
+ return getChildIdList().indexOf(id);
+ }
+
+ /**
+ * Cache is built lasily only if needed. The cache is cleaned on any
+ * structure change for that node!).
+ *
+ * @return list of ids of children
+ */
+ public synchronized List<T> getChildIdList() {
+ if (childIdListCache == null) {
+ childIdListCache = new LinkedList<T>();
+ for (final InMemoryTreeNode<T> n : children) {
+ childIdListCache.add(n.getId());
+ }
+ }
+ return childIdListCache;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public void setVisible(final boolean visible) {
+ this.visible = visible;
+ }
+
+ public int getChildrenListSize() {
+ return children.size();
+ }
+
+ public synchronized InMemoryTreeNode<T> add(final int index, final T child,
+ final boolean visible) {
+ childIdListCache = null;
+ // Note! top levell children are always visible (!)
+ final InMemoryTreeNode<T> newNode = new InMemoryTreeNode<T>(child,
+ getId(), getLevel() + 1, getId() == null ? true : visible);
+ children.add(index, newNode);
+ return newNode;
+ }
+
+ /**
+ * Note. This method should technically return unmodifiable collection, but
+ * for performance reason on small devices we do not do it.
+ *
+ * @return children list
+ */
+ public List<InMemoryTreeNode<T>> getChildren() {
+ return children;
+ }
+
+ public synchronized void clearChildren() {
+ children.clear();
+ childIdListCache = null;
+ }
+
+ public synchronized void removeChild(final T child) {
+ final int childIndex = indexOf(child);
+ if (childIndex != -1) {
+ children.remove(childIndex);
+ childIdListCache = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "InMemoryTreeNode [id=" + getId() + ", parent=" + getParent()
+ + ", level=" + getLevel() + ", visible=" + visible
+ + ", children=" + children + ", childIdListCache="
+ + childIdListCache + "]";
+ }
+
+ T getId() {
+ return id;
+ }
+
+ T getParent() {
+ return parent;
+ }
+
+ int getLevel() {
+ return level;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package pl.polidea.treeview;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.database.DataSetObserver;
+
+/**
+ * In-memory manager of tree state.
+ *
+ * @param <T>
+ * type of identifier
+ */
+public class InMemoryTreeStateManager<T> implements TreeStateManager<T> {
+ private static final long serialVersionUID = 1L;
+ private final Map<T, InMemoryTreeNode<T>> allNodes = new HashMap<T, InMemoryTreeNode<T>>();
+ private final InMemoryTreeNode<T> topSentinel = new InMemoryTreeNode<T>(
+ null, null, -1, true);
+ private transient List<T> visibleListCache = null; // lasy initialised
+ private transient List<T> unmodifiableVisibleList = null;
+ private boolean visibleByDefault = true;
+ private final transient Set<DataSetObserver> observers = new HashSet<DataSetObserver>();
+
+ private synchronized void internalDataSetChanged() {
+ visibleListCache = null;
+ unmodifiableVisibleList = null;
+ for (final DataSetObserver observer : observers) {
+ observer.onChanged();
+ }
+ }
+
+ /**
+ * If true new nodes are visible by default.
+ *
+ * @param visibleByDefault
+ * if true, then newly added nodes are expanded by default
+ */
+ public void setVisibleByDefault(final boolean visibleByDefault) {
+ this.visibleByDefault = visibleByDefault;
+ }
+
+ private InMemoryTreeNode<T> getNodeFromTreeOrThrow(final T id) {
+ if (id == null) {
+ throw new NodeNotInTreeException("(null)");
+ }
+ final InMemoryTreeNode<T> node = allNodes.get(id);
+ if (node == null) {
+ throw new NodeNotInTreeException(id.toString());
+ }
+ return node;
+ }
+
+ private InMemoryTreeNode<T> getNodeFromTreeOrThrowAllowRoot(final T id) {
+ if (id == null) {
+ return topSentinel;
+ }
+ return getNodeFromTreeOrThrow(id);
+ }
+
+ private void expectNodeNotInTreeYet(final T id) {
+ final InMemoryTreeNode<T> node = allNodes.get(id);
+ if (node != null) {
+ throw new NodeAlreadyInTreeException(id.toString(), node.toString());
+ }
+ }
+
+ @Override
+ public synchronized TreeNodeInfo<T> getNodeInfo(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrow(id);
+ final List<InMemoryTreeNode<T>> children = node.getChildren();
+ boolean expanded = false;
+ if (!children.isEmpty() && children.get(0).isVisible()) {
+ expanded = true;
+ }
+ return new TreeNodeInfo<T>(id, node.getLevel(), !children.isEmpty(),
+ node.isVisible(), expanded);
+ }
+
+ @Override
+ public synchronized List<T> getChildren(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ return node.getChildIdList();
+ }
+
+ @Override
+ public synchronized T getParent(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ return node.getParent();
+ }
+
+ private boolean getChildrenVisibility(final InMemoryTreeNode<T> node) {
+ boolean visibility;
+ final List<InMemoryTreeNode<T>> children = node.getChildren();
+ if (children.isEmpty()) {
+ visibility = visibleByDefault;
+ } else {
+ visibility = children.get(0).isVisible();
+ }
+ return visibility;
+ }
+
+ @Override
+ public synchronized void addBeforeChild(final T parent, final T newChild,
+ final T beforeChild) {
+ expectNodeNotInTreeYet(newChild);
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
+ final boolean visibility = getChildrenVisibility(node);
+ // top nodes are always expanded.
+ if (beforeChild == null) {
+ final InMemoryTreeNode<T> added = node.add(0, newChild, visibility);
+ allNodes.put(newChild, added);
+ } else {
+ final int index = node.indexOf(beforeChild);
+ final InMemoryTreeNode<T> added = node.add(index == -1 ? 0 : index,
+ newChild, visibility);
+ allNodes.put(newChild, added);
+ }
+ if (visibility) {
+ internalDataSetChanged();
+ }
+ }
+
+ @Override
+ public synchronized void addAfterChild(final T parent, final T newChild,
+ final T afterChild) {
+ expectNodeNotInTreeYet(newChild);
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);
+ final boolean visibility = getChildrenVisibility(node);
+ if (afterChild == null) {
+ final InMemoryTreeNode<T> added = node.add(
+ node.getChildrenListSize(), newChild, visibility);
+ allNodes.put(newChild, added);
+ } else {
+ final int index = node.indexOf(afterChild);
+ final InMemoryTreeNode<T> added = node.add(
+ index == -1 ? node.getChildrenListSize() : index, newChild,
+ visibility);
+ allNodes.put(newChild, added);
+ }
+ if (visibility) {
+ internalDataSetChanged();
+ }
+ }
+
+ @Override
+ public synchronized void removeNodeRecursively(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ final boolean visibleNodeChanged = removeNodeRecursively(node);
+ final T parent = node.getParent();
+ final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
+ parentNode.removeChild(id);
+ if (visibleNodeChanged) {
+ internalDataSetChanged();
+ }
+ }
+
+ private boolean removeNodeRecursively(final InMemoryTreeNode<T> node) {
+ boolean visibleNodeChanged = false;
+ for (final InMemoryTreeNode<T> child : node.getChildren()) {
+ if (removeNodeRecursively(child)) {
+ visibleNodeChanged = true;
+ }
+ }
+ node.clearChildren();
+ if (node.getId() != null) {
+ allNodes.remove(node.getId());
+ if (node.isVisible()) {
+ visibleNodeChanged = true;
+ }
+ }
+ return visibleNodeChanged;
+ }
+
+ private void setChildrenVisibility(final InMemoryTreeNode<T> node,
+ final boolean visible, final boolean recursive) {
+ for (final InMemoryTreeNode<T> child : node.getChildren()) {
+ child.setVisible(visible);
+ if (recursive) {
+ setChildrenVisibility(child, visible, true);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void expandDirectChildren(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ setChildrenVisibility(node, true, false);
+ internalDataSetChanged();
+ }
+
+ @Override
+ public synchronized void expandEverythingBelow(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ setChildrenVisibility(node, true, true);
+ internalDataSetChanged();
+ }
+
+ @Override
+ public synchronized void collapseChildren(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ if (node == topSentinel) {
+ for (final InMemoryTreeNode<T> n : topSentinel.getChildren()) {
+ setChildrenVisibility(n, false, true);
+ }
+ } else {
+ setChildrenVisibility(node, false, true);
+ }
+ internalDataSetChanged();
+ }
+
+ @Override
+ public synchronized T getNextSibling(final T id) {
+ final T parent = getParent(id);
+ final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
+ boolean returnNext = false;
+ for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
+ if (returnNext) {
+ return child.getId();
+ }
+ if (child.getId().equals(id)) {
+ returnNext = true;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized T getPreviousSibling(final T id) {
+ final T parent = getParent(id);
+ final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);
+ final T previousSibling = null;
+ for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {
+ if (child.getId().equals(id)) {
+ return previousSibling;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized boolean isInTree(final T id) {
+ return allNodes.containsKey(id);
+ }
+
+ @Override
+ public synchronized int getVisibleCount() {
+ return getVisibleList().size();
+ }
+
+ @Override
+ public synchronized List<T> getVisibleList() {
+ T currentId = null;
+ if (visibleListCache == null) {
+ visibleListCache = new ArrayList<T>(allNodes.size());
+ do {
+ currentId = getNextVisible(currentId);
+ if (currentId == null) {
+ break;
+ } else {
+ visibleListCache.add(currentId);
+ }
+ } while (true);
+ }
+ if (unmodifiableVisibleList == null) {
+ unmodifiableVisibleList = Collections
+ .unmodifiableList(visibleListCache);
+ }
+ return unmodifiableVisibleList;
+ }
+
+ public synchronized T getNextVisible(final T id) {
+ final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);
+ if (!node.isVisible()) {
+ return null;
+ }
+ final List<InMemoryTreeNode<T>> children = node.getChildren();
+ if (!children.isEmpty()) {
+ final InMemoryTreeNode<T> firstChild = children.get(0);
+ if (firstChild.isVisible()) {
+ return firstChild.getId();
+ }
+ }
+ final T sibl = getNextSibling(id);
+ if (sibl != null) {
+ return sibl;
+ }
+ T parent = node.getParent();
+ do {
+ if (parent == null) {
+ return null;
+ }
+ final T parentSibling = getNextSibling(parent);
+ if (parentSibling != null) {
+ return parentSibling;
+ }
+ parent = getNodeFromTreeOrThrow(parent).getParent();
+ } while (true);
+ }
+
+ @Override
+ public synchronized void registerDataSetObserver(
+ final DataSetObserver observer) {
+ observers.add(observer);
+ }
+
+ @Override
+ public synchronized void unregisterDataSetObserver(
+ final DataSetObserver observer) {
+ observers.remove(observer);
+ }
+
+ @Override
+ public int getLevel(final T id) {
+ return getNodeFromTreeOrThrow(id).getLevel();
+ }
+
+ @Override
+ public Integer[] getHierarchyDescription(final T id) {
+ final int level = getLevel(id);
+ final Integer[] hierarchy = new Integer[level + 1];
+ int currentLevel = level;
+ T currentId = id;
+ T parent = getParent(currentId);
+ while (currentLevel >= 0) {
+ hierarchy[currentLevel--] = getChildren(parent).indexOf(currentId);
+ currentId = parent;
+ parent = getParent(parent);
+ }
+ return hierarchy;
+ }
+
+ private void appendToSb(final StringBuilder sb, final T id) {
+ if (id != null) {
+ final TreeNodeInfo<T> node = getNodeInfo(id);
+ final int indent = node.getLevel() * 4;
+ final char[] indentString = new char[indent];
+ Arrays.fill(indentString, ' ');
+ sb.append(indentString);
+ sb.append(node.toString());
+ sb.append(Arrays.asList(getHierarchyDescription(id)).toString());
+ sb.append("\n");
+ }
+ final List<T> children = getChildren(id);
+ for (final T child : children) {
+ appendToSb(sb, child);
+ }
+ }
+
+ @Override
+ public synchronized String toString() {
+ final StringBuilder sb = new StringBuilder();
+ appendToSb(sb, null);
+ return sb.toString();
+ }
+
+ @Override
+ public synchronized void clear() {
+ allNodes.clear();
+ topSentinel.clearChildren();
+ internalDataSetChanged();
+ }
+
+ @Override
+ public void refresh() {
+ internalDataSetChanged();
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+/**
+ * The node being added is already in the tree.
+ *
+ */
+public class NodeAlreadyInTreeException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public NodeAlreadyInTreeException(final String id, final String oldNode) {
+ super("The node has already been added to the tree: " + id + ". Old node is:" + oldNode);
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+/**
+ * This exception is thrown when the tree does not contain node requested.
+ *
+ */
+public class NodeNotInTreeException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NodeNotInTreeException(final String id) {
+ super("The tree does not contain the node specified: " + id);
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+import android.util.Log;
+
+/**
+ * Allows to build tree easily in sequential mode (you have to know levels of
+ * all the tree elements upfront). You should rather use this class rather than
+ * manager if you build initial tree from some external data source.
+ *
+ * @param <T>
+ */
+public class TreeBuilder<T> {
+ private static final String TAG = TreeBuilder.class.getSimpleName();
+
+ private final TreeStateManager<T> manager;
+
+ private T lastAddedId = null;
+ private int lastLevel = -1;
+
+ public TreeBuilder(final TreeStateManager<T> manager) {
+ this.manager = manager;
+ }
+
+ public void clear() {
+ manager.clear();
+ }
+
+ /**
+ * Adds new relation to existing tree. Child is set as the last child of the
+ * parent node. Parent has to already exist in the tree, child cannot yet
+ * exist. This method is mostly useful in case you add entries layer by
+ * layer - i.e. first top level entries, then children for all parents, then
+ * grand-children and so on.
+ *
+ * @param parent
+ * parent id
+ * @param child
+ * child id
+ */
+ public synchronized void addRelation(final T parent, final T child) {
+ Log.d(TAG, "Adding relation parent:" + parent + " -> child: " + child);
+ manager.addAfterChild(parent, child, null);
+ lastAddedId = child;
+ lastLevel = manager.getLevel(child);
+ }
+
+ /**
+ * Adds sequentially new node. Using this method is the simplest way of
+ * building tree - if you have all the elements in the sequence as they
+ * should be displayed in fully-expanded tree. You can combine it with add
+ * relation - for example you can add information about few levels using
+ * {@link addRelation} and then after the right level is added as parent,
+ * you can continue adding them using sequential operation.
+ *
+ * @param id
+ * id of the node
+ * @param level
+ * its level
+ */
+ public synchronized void sequentiallyAddNextNode(final T id, final int level) {
+ Log.d(TAG, "Adding sequentiall node " + id + " at level " + level);
+ if (lastAddedId == null) {
+ addNodeToParentOneLevelDown(null, id, level);
+ } else {
+ if (level <= lastLevel) {
+ final T parent = findParentAtLevel(lastAddedId, level - 1);
+ addNodeToParentOneLevelDown(parent, id, level);
+ } else {
+ addNodeToParentOneLevelDown(lastAddedId, id, level);
+ }
+ }
+ }
+
+ /**
+ * Find parent of the node at the level specified.
+ *
+ * @param node
+ * node from which we start
+ * @param levelToFind
+ * level which we are looking for
+ * @return the node found (null if it is topmost node).
+ */
+ private T findParentAtLevel(final T node, final int levelToFind) {
+ T parent = manager.getParent(node);
+ while (parent != null) {
+ if (manager.getLevel(parent) == levelToFind) {
+ break;
+ }
+ parent = manager.getParent(parent);
+ }
+ return parent;
+ }
+
+ /**
+ * Adds note to parent at the level specified. But it verifies that the
+ * level is one level down than the parent!
+ *
+ * @param parent
+ * parent parent
+ * @param id
+ * new node id
+ * @param level
+ * should always be parent's level + 1
+ */
+ private void addNodeToParentOneLevelDown(final T parent, final T id,
+ final int level) {
+ if (parent == null && level != 0) {
+ throw new TreeConfigurationException("Trying to add new id " + id
+ + " to top level with level != 0 (" + level + ")");
+ }
+ if (parent != null && manager.getLevel(parent) != level - 1) {
+ throw new TreeConfigurationException("Trying to add new id " + id
+ + " <" + level + "> to " + parent + " <"
+ + manager.getLevel(parent)
+ + ">. The difference in levels up is bigger than 1.");
+ }
+ manager.addAfterChild(parent, id, null);
+ setLastAdded(id, level);
+ }
+
+ private void setLastAdded(final T id, final int level) {
+ lastAddedId = id;
+ lastLevel = level;
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+/**
+ * Exception thrown when there is a problem with configuring tree.
+ *
+ */
+public class TreeConfigurationException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public TreeConfigurationException(final String detailMessage) {
+ super(detailMessage);
+ }
+
+}
--- /dev/null
+package pl.polidea.treeview;
+
+/**
+ * Information about the node.
+ *
+ * @param <T>
+ * type of the id for the tree
+ */
+public class TreeNodeInfo<T> {
+ private final T id;
+ private final int level;
+ private final boolean withChildren;
+ private final boolean visible;
+ private final boolean expanded;
+
+ /**
+ * Creates the node information.
+ *
+ * @param id
+ * id of the node
+ * @param level
+ * level of the node
+ * @param withChildren
+ * whether the node has children.
+ * @param visible
+ * whether the tree node is visible.
+ * @param expanded
+ * whether the tree node is expanded
+ *
+ */
+ public TreeNodeInfo(final T id, final int level,
+ final boolean withChildren, final boolean visible,
+ final boolean expanded) {
+ super();
+ this.id = id;
+ this.level = level;
+ this.withChildren = withChildren;
+ this.visible = visible;
+ this.expanded = expanded;
+ }
+
+ public T getId() {
+ return id;
+ }
+
+ public boolean isWithChildren() {
+ return withChildren;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public boolean isExpanded() {
+ return expanded;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ @Override
+ public String toString() {
+ return "TreeNodeInfo [id=" + id + ", level=" + level
+ + ", withChildren=" + withChildren + ", visible=" + visible
+ + ", expanded=" + expanded + "]";
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package pl.polidea.treeview;
+
+import java.io.Serializable;
+import java.util.List;
+
+import android.database.DataSetObserver;
+
+/**
+ * Manages information about state of the tree. It only keeps information about
+ * tree elements, not the elements themselves.
+ *
+ * @param <T>
+ * type of the identifier for nodes in the tree
+ */
+public interface TreeStateManager<T> extends Serializable {
+
+ /**
+ * Returns array of integers showing the location of the node in hierarchy.
+ * It corresponds to heading numbering. {0,0,0} in 3 level node is the first
+ * node {0,0,1} is second leaf (assuming that there are two leaves in first
+ * subnode of the first node).
+ *
+ * @param id
+ * id of the node
+ * @return textual description of the hierarchy in tree for the node.
+ */
+ Integer[] getHierarchyDescription(T id);
+
+ /**
+ * Returns level of the node.
+ *
+ * @param id
+ * id of the node
+ * @return level in the tree
+ */
+ int getLevel(T id);
+
+ /**
+ * Returns information about the node.
+ *
+ * @param id
+ * node id
+ * @return node info
+ */
+ TreeNodeInfo<T> getNodeInfo(T id);
+
+ /**
+ * Returns children of the node.
+ *
+ * @param id
+ * id of the node or null if asking for top nodes
+ * @return children of the node
+ */
+ List<T> getChildren(T id);
+
+ /**
+ * Returns parent of the node.
+ *
+ * @param id
+ * id of the node
+ * @return parent id or null if no parent
+ */
+ T getParent(T id);
+
+ /**
+ * Adds the node before child or at the beginning.
+ *
+ * @param parent
+ * id of the parent node. If null - adds at the top level
+ * @param newChild
+ * new child to add if null - adds at the beginning.
+ * @param beforeChild
+ * child before which to add the new child
+ */
+ void addBeforeChild(T parent, T newChild, T beforeChild);
+
+ /**
+ * Adds the node after child or at the end.
+ *
+ * @param parent
+ * id of the parent node. If null - adds at the top level.
+ * @param newChild
+ * new child to add. If null - adds at the end.
+ * @param afterChild
+ * child after which to add the new child
+ */
+ void addAfterChild(T parent, T newChild, T afterChild);
+
+ /**
+ * Removes the node and all children from the tree.
+ *
+ * @param id
+ * id of the node to remove or null if all nodes are to be
+ * removed.
+ */
+ void removeNodeRecursively(T id);
+
+ /**
+ * Expands all children of the node.
+ *
+ * @param id
+ * node which children should be expanded. cannot be null (top
+ * nodes are always expanded!).
+ */
+ void expandDirectChildren(T id);
+
+ /**
+ * Expands everything below the node specified. Might be null - then expands
+ * all.
+ *
+ * @param id
+ * node which children should be expanded or null if all nodes
+ * are to be expanded.
+ */
+ void expandEverythingBelow(T id);
+
+ /**
+ * Collapse children.
+ *
+ * @param id
+ * id collapses everything below node specified. If null,
+ * collapses everything but top-level nodes.
+ */
+ void collapseChildren(T id);
+
+ /**
+ * Returns next sibling of the node (or null if no further sibling).
+ *
+ * @param id
+ * node id
+ * @return the sibling (or null if no next)
+ */
+ T getNextSibling(T id);
+
+ /**
+ * Returns previous sibling of the node (or null if no previous sibling).
+ *
+ * @param id
+ * node id
+ * @return the sibling (or null if no previous)
+ */
+ T getPreviousSibling(T id);
+
+ /**
+ * Checks if given node is already in tree.
+ *
+ * @param id
+ * id of the node
+ * @return true if node is already in tree.
+ */
+ boolean isInTree(T id);
+
+ /**
+ * Count visible elements.
+ *
+ * @return number of currently visible elements.
+ */
+ int getVisibleCount();
+
+ /**
+ * Returns visible node list.
+ *
+ * @return return the list of all visible nodes in the right sequence
+ */
+ List<T> getVisibleList();
+
+ /**
+ * Registers observers with the manager.
+ *
+ * @param observer
+ * observer
+ */
+ void registerDataSetObserver(final DataSetObserver observer);
+
+ /**
+ * Unregisters observers with the manager.
+ *
+ * @param observer
+ * observer
+ */
+ void unregisterDataSetObserver(final DataSetObserver observer);
+
+ /**
+ * Cleans tree stored in manager. After this operation the tree is empty.
+ *
+ */
+ void clear();
+
+ /**
+ * Refreshes views connected to the manager.
+ */
+ void refresh();
+}
--- /dev/null
+package pl.polidea.treeview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+/**
+ * Tree view, expandable multi-level.
+ *
+ * <pre>
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_collapsible
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_expanded
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_collapsed
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indent_width
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_handle_trackball_press
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_gravity
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_background
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_row_background
+ * </pre>
+ */
+public class TreeViewList extends ListView {
+ private static final int DEFAULT_COLLAPSED_RESOURCE = R.drawable.collapsed;
+ private static final int DEFAULT_EXPANDED_RESOURCE = R.drawable.expanded;
+ private static final int DEFAULT_INDENT = 0;
+ private static final int DEFAULT_GRAVITY = Gravity.LEFT
+ | Gravity.CENTER_VERTICAL;
+ private Drawable expandedDrawable;
+ private Drawable collapsedDrawable;
+ private Drawable rowBackgroundDrawable;
+ private Drawable indicatorBackgroundDrawable;
+ private int indentWidth = 0;
+ private int indicatorGravity = 0;
+ private AbstractTreeViewAdapter< ? > treeAdapter;
+ private boolean collapsible;
+ private boolean handleTrackballPress;
+
+ public TreeViewList(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.style.treeViewListStyle);
+ }
+
+ public TreeViewList(final Context context) {
+ this(context, null);
+ }
+
+ public TreeViewList(final Context context, final AttributeSet attrs,
+ final int defStyle) {
+ super(context, attrs, defStyle);
+ parseAttributes(context, attrs);
+ }
+
+ private void parseAttributes(final Context context, final AttributeSet attrs) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.TreeViewList);
+ expandedDrawable = a.getDrawable(R.styleable.TreeViewList_src_expanded);
+ if (expandedDrawable == null) {
+ expandedDrawable = context.getResources().getDrawable(
+ DEFAULT_EXPANDED_RESOURCE);
+ }
+ collapsedDrawable = a
+ .getDrawable(R.styleable.TreeViewList_src_collapsed);
+ if (collapsedDrawable == null) {
+ collapsedDrawable = context.getResources().getDrawable(
+ DEFAULT_COLLAPSED_RESOURCE);
+ }
+ indentWidth = a.getDimensionPixelSize(
+ R.styleable.TreeViewList_indent_width, DEFAULT_INDENT);
+ indicatorGravity = a.getInteger(
+ R.styleable.TreeViewList_indicator_gravity, DEFAULT_GRAVITY);
+ indicatorBackgroundDrawable = a
+ .getDrawable(R.styleable.TreeViewList_indicator_background);
+ rowBackgroundDrawable = a
+ .getDrawable(R.styleable.TreeViewList_row_background);
+ collapsible = a.getBoolean(R.styleable.TreeViewList_collapsible, true);
+ handleTrackballPress = a.getBoolean(
+ R.styleable.TreeViewList_handle_trackball_press, true);
+ }
+
+ @Override
+ public void setAdapter(final ListAdapter adapter) {
+ if (!(adapter instanceof AbstractTreeViewAdapter)) {
+ throw new TreeConfigurationException(
+ "The adapter is not of TreeViewAdapter type");
+ }
+ treeAdapter = (AbstractTreeViewAdapter< ? >) adapter;
+ syncAdapter();
+ super.setAdapter(treeAdapter);
+ }
+
+ private void syncAdapter() {
+ treeAdapter.setCollapsedDrawable(collapsedDrawable);
+ treeAdapter.setExpandedDrawable(expandedDrawable);
+ treeAdapter.setIndicatorGravity(indicatorGravity);
+ treeAdapter.setIndentWidth(indentWidth);
+ treeAdapter.setIndicatorBackgroundDrawable(indicatorBackgroundDrawable);
+ treeAdapter.setRowBackgroundDrawable(rowBackgroundDrawable);
+ treeAdapter.setCollapsible(collapsible);
+ if (handleTrackballPress) {
+ setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(final AdapterView< ? > parent,
+ final View view, final int position, final long id) {
+ treeAdapter.handleItemClick(view, view.getTag());
+ }
+ });
+ } else {
+ setOnClickListener(null);
+ }
+
+ }
+
+ public void setExpandedDrawable(final Drawable expandedDrawable) {
+ this.expandedDrawable = expandedDrawable;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setCollapsedDrawable(final Drawable collapsedDrawable) {
+ this.collapsedDrawable = collapsedDrawable;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {
+ this.rowBackgroundDrawable = rowBackgroundDrawable;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setIndicatorBackgroundDrawable(
+ final Drawable indicatorBackgroundDrawable) {
+ this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setIndentWidth(final int indentWidth) {
+ this.indentWidth = indentWidth;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setIndicatorGravity(final int indicatorGravity) {
+ this.indicatorGravity = indicatorGravity;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setCollapsible(final boolean collapsible) {
+ this.collapsible = collapsible;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public void setHandleTrackballPress(final boolean handleTrackballPress) {
+ this.handleTrackballPress = handleTrackballPress;
+ syncAdapter();
+ treeAdapter.refresh();
+ }
+
+ public Drawable getExpandedDrawable() {
+ return expandedDrawable;
+ }
+
+ public Drawable getCollapsedDrawable() {
+ return collapsedDrawable;
+ }
+
+ public Drawable getRowBackgroundDrawable() {
+ return rowBackgroundDrawable;
+ }
+
+ public Drawable getIndicatorBackgroundDrawable() {
+ return indicatorBackgroundDrawable;
+ }
+
+ public int getIndentWidth() {
+ return indentWidth;
+ }
+
+ public int getIndicatorGravity() {
+ return indicatorGravity;
+ }
+
+ public boolean isCollapsible() {
+ return collapsible;
+ }
+
+ public boolean isHandleTrackballPress() {
+ return handleTrackballPress;
+ }
+
+}
--- /dev/null
+<html>
+<body>
+This is a small utility that provides quite configurable tree view list.
+It is based on standard android list view. It separates out different
+aspects of the tree: there is a separate list view, tree adapter, tree
+state manager and tree state builder.
+<p>
+<ul>
+ <li>Tree view provides the frame to display the view.</li>
+ <li>Adapter allows to create visual representation of each tree
+ node.</li>
+ <li>State manager provides storage for tree state (connections
+ between parents and children, collapsed/expanded state). It provides
+ all the low-level tree manipulation methods.</li>
+ <li>Tree builder allows to build tree easily providing higher
+ level methods. The tree can be build either from prepared sequentially
+ prepared list of nodes (node id, level) or using (parent/child
+ relationships).</li>
+ <p>For now only in-memory state manager is provided, but Tree State
+ Manger interface is done in the way that database tree manager even for
+ large trees is potentially supported.
+</ul>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+/**
+ * Provides expandable Tree View implementation.
+ */
+package pl.polidea.treeview;
\ No newline at end of file