diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..63fd154 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,115 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + + +## [Unreleased] +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + + +## [v1.4.0] - 2017-12-03 +First version after fork of original AndroidTreeView. + +**Changes TBD** + + +## [v1.3.x] - Not released +Version was not released as only used in a different fork of AndroidTreeView. + +---- + +> The following releases are a best guess of the history of the original AndroidTreeView. Because there are no tags in the repository, the dates of the releases might be a couple of days off (because sometimes it is not clear whether a release was actually defined when the version code was changed or whether additional changes were part of the version) + +## [v1.2.9] - 2016-05-03 +Latest released version of original AndroidTreeView before fork. + +*no code changes* + + +## [v1.2.8] - 2016-04-25 +### Added +* Allow deactivating auto-toggling mode of tree nodes + +### Changed +* Expose some fields of AndroidTreeView-class to subclasses + + +## [v1.2.7] - 2015-11-05 +### Changed +* Removed allowBackup in library, see bmelnychuk/AndroidTreeView#38 + + +## [v1.2.6] - 2015-09-24 +### Added +* Added click and long click listener + +### Changed +* Node IDs get generated differently + + +## [v1.2.5] - 2015-06-02 +### Added +* 2D scrolling mode + *Keep in mind this comes with few limitations: you won't be able not place views on right side like alignParentRight. Everything should be align left. Is not enabled by default* +* Background of selected items coupled to ```?android:attr/selectableItemBackground``` + + +## [v1.2.4] - 2015-04-11 +### Fixed +* Different padding issues: [Padding issues](https://github.com/bmelnychuk/AndroidTreeView/issues/1), [Padding left on android < 5.0](https://github.com/bmelnychuk/AndroidTreeView/issues/11) + + +## [v1.2.3] - 2015-03-21 +### Fixed +* [Restore state issue](https://github.com/bmelnychuk/AndroidTreeView/issues/7) + + +## [v1.2.2] - 2015-03-13 +### Added +* Animation for expand and collapse + + +## [v1.2.1] - 2015-02-19 +### Fixed +* Fire listener before collapsing/expanding + + +## [v1.2.0] - 2015-02-18 +### Added +* Dynamic addition/removal of tree nodes + + +## [v1.1.0] - 2015-02-16 +### Added +* Selection mode for nodes + + +## v1.0.0 - 2015-02-14 +Initial version + + +[Unreleased]: https://github.com/jleuser/AndroidTreeView/compare/v1.4.0...HEAD +[v1.4.0]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.9...v1.4.0 +[v1.2.9]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.8...v1.2.9 +[v1.2.8]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.7...v1.2.8 +[v1.2.7]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.6...v1.2.7 +[v1.2.6]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.5...v1.2.6 +[v1.2.5]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.4...v1.2.5 +[v1.2.4]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.3...v1.2.4 +[v1.2.3]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.2...v1.2.3 +[v1.2.2]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.1...v1.2.2 +[v1.2.1]: https://github.com/jleuser/AndroidTreeView/compare/v1.2.0...v1.2.1 +[v1.2.0]: https://github.com/jleuser/AndroidTreeView/compare/v1.1.0...v1.2.0 +[v1.1.0]: https://github.com/jleuser/AndroidTreeView/compare/v1.0.0...v1.1.0 diff --git a/README.md b/README.md index 89074e2..a04f183 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,26 @@ AndroidTreeView ==================== +[![Release](https://jitpack.io/v/jleuser/AndroidTreeView.svg)](https://jitpack.io/#jleuser/AndroidTreeView) + +This is a fork of the original [AndroidTreeView](https://github.com/bmelnychuk/AndroidTreeView) library provided by [Bogdan Melnychuk](https://github.com/bmelnychuk). + +Compared to the original library, some of the pull requests to the original library have been merged into this version. + +This fork can be used in your build files thanks to jitpack.io. You have to add these lines in your `build.gradle` file: + + ```java +repositories { + maven { url "https://jitpack.io" } +} + +dependencies { + implementation 'com.github.jleuser:AndroidTreeView:v1.4.0' +} + ``` + +## Original readme + ### Recent changes @@ -24,6 +44,8 @@ Tree view implementation for android + 3. Save state after rotation + 4. Selection mode for nodes + 5. Dynamic add/remove node ++ 6. Auto scroll to selected leaf ++ 7. Auto scroll to expanded node ### Known Limitations + For Android 4.0 (+/- nearest version) if you have too deep view hierarchy and with tree its easily possible, your app may crash @@ -41,7 +63,7 @@ Tree view implementation for android ### Integration -**1)** Add library as a dependency to your project +**1)** Add library as a dependency to your project ```compile 'com.github.bmelnychuk:atv:1.2.+'``` @@ -60,10 +82,10 @@ Create and add your nodes (use your custom object as constructor param) ``` **3)** Add tree view to layout -```java +```java AndroidTreeView tView = new AndroidTreeView(getActivity(), root); containerView.addView(tView.getView()); -``` +``` The simplest but not styled tree is ready. Now you can see ```parent``` node as root of your tree **4)** Custom view for nodes @@ -78,7 +100,7 @@ public class MyHolder extends TreeNode.BaseNodeViewHolder { final View view = inflater.inflate(R.layout.layout_profile_node, null, false); TextView tvValue = (TextView) view.findViewById(R.id.node_value); tvValue.setText(value.text); - + return view; } ... @@ -89,14 +111,14 @@ public class MyHolder extends TreeNode.BaseNodeViewHolder { } ``` -**5)** Connect view holder with node -```java +**5)** Connect view holder with node +```java IconTreeItem nodeItem = new IconTreeItem(); TreeNode child1 = new TreeNode(nodeItem).setViewHolder(new MyHolder(mContext)); ``` -**6)** Consider using -```java +**6)** Consider using +```java TreeNode.setClickListener(TreeNodeClickListener listener); AndroidTreeView.setDefaultViewHolder AndroidTreeView.setDefaultNodeClickListener diff --git a/app/build.gradle b/app/build.gradle index 6d144bb..c1395fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:21.0.3' - compile 'com.github.johnkil.print:print:1.2.2' + compile 'com.android.support:appcompat-v7:27.0.2' + compile 'com.github.johnkil.print:print:1.3.1' compile project(':library') } diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java b/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java index f74bea2..f83bfd4 100644 --- a/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java +++ b/app/src/main/java/com/unnamed/b/atv/sample/activity/MainActivity.java @@ -3,7 +3,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -14,6 +14,7 @@ import com.unnamed.b.atv.sample.fragment.FolderStructureFragment; import com.unnamed.b.atv.sample.fragment.SelectableTreeFragment; import com.unnamed.b.atv.sample.fragment.TwoDScrollingArrowExpandFragment; +import com.unnamed.b.atv.sample.fragment.TwoDScrollingArrowExpandNodeFragment; import com.unnamed.b.atv.sample.fragment.TwoDScrollingFragment; import java.util.ArrayList; @@ -21,7 +22,7 @@ import java.util.List; -public class MainActivity extends ActionBarActivity { +public class MainActivity extends AppCompatActivity { @Override @@ -36,6 +37,7 @@ protected void onCreate(Bundle savedInstanceState) { listItems.put("Selectable Nodes", SelectableTreeFragment.class); listItems.put("2d scrolling", TwoDScrollingFragment.class); listItems.put("Expand with arrow only", TwoDScrollingArrowExpandFragment.class); + listItems.put("Expand with arrow one node only", TwoDScrollingArrowExpandNodeFragment.class); final List list = new ArrayList(listItems.keySet()); diff --git a/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java b/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java index c28f65b..fed41db 100644 --- a/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java +++ b/app/src/main/java/com/unnamed/b/atv/sample/activity/SingleFragmentActivity.java @@ -2,14 +2,14 @@ import android.app.Fragment; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import com.unnamed.b.atv.sample.R; /** * Created by Bogdan Melnychuk on 2/12/15. */ -public class SingleFragmentActivity extends ActionBarActivity { +public class SingleFragmentActivity extends AppCompatActivity { public final static String FRAGMENT_PARAM = "fragment"; @Override diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java index bc64194..14a4264 100644 --- a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandFragment.java @@ -46,7 +46,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa tView.setDefaultNodeClickListener(TwoDScrollingArrowExpandFragment.this); tView.setDefaultViewHolder(ArrowExpandSelectableHeaderHolder.class); containerView.addView(tView.getView()); - tView.setUseAutoToggle(false); + tView.setExpansionAutoToggle(false); tView.expandAll(); diff --git a/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandNodeFragment.java b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandNodeFragment.java new file mode 100644 index 0000000..fccb279 --- /dev/null +++ b/app/src/main/java/com/unnamed/b/atv/sample/fragment/TwoDScrollingArrowExpandNodeFragment.java @@ -0,0 +1,88 @@ +package com.unnamed.b.atv.sample.fragment; + +import android.app.Fragment; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.unnamed.b.atv.model.TreeNode; +import com.unnamed.b.atv.sample.R; +import com.unnamed.b.atv.sample.holder.ArrowExpandSelectableHeaderHolder; +import com.unnamed.b.atv.sample.holder.IconTreeItemHolder; +import com.unnamed.b.atv.view.AndroidTreeView; + +/** + * Created by Bogdan Melnychuk on 2/12/15 modified by Szigeti Peter 2/2/16. + */ +public class TwoDScrollingArrowExpandNodeFragment extends Fragment implements TreeNode.TreeNodeClickListener{ + private static final String NAME = "Very long name for folder"; + private AndroidTreeView tView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_selectable_nodes, null, false); + rootView.findViewById(R.id.status).setVisibility(View.GONE); + ViewGroup containerView = (ViewGroup) rootView.findViewById(R.id.container); + + TreeNode root = TreeNode.root(); + + TreeNode s1 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Folder with very long name ")).setViewHolder( + new ArrowExpandSelectableHeaderHolder(getActivity())); + TreeNode s2 = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, "Another folder with very long name")).setViewHolder( + new ArrowExpandSelectableHeaderHolder(getActivity())); + + fillFolder(s1); + TreeNode nodeToExpand = fillFolder(s2); + + root.addChildren(s1, s2); + + tView = new AndroidTreeView(getActivity(), root); + tView.setDefaultAnimation(true); + tView.setUse2dScroll(true); + tView.setDefaultContainerStyle(R.style.TreeNodeStyleCustom); + tView.setDefaultNodeClickListener(TwoDScrollingArrowExpandNodeFragment.this); + tView.setDefaultViewHolder(ArrowExpandSelectableHeaderHolder.class); + containerView.addView(tView.getView()); + + tView.setAutoScrollToExpandedNode(true); + tView.setAutoScrollToSelectedLeafs(true); + tView.setLeafSelectionAutoToggle(true); + + tView.expandNode(s1); + tView.expandNodeIncludingParents(nodeToExpand, true); + + if (savedInstanceState != null) { + String state = savedInstanceState.getString("tState"); + if (!TextUtils.isEmpty(state)) { + tView.restoreState(state); + } + } + return rootView; + } + + private TreeNode fillFolder(TreeNode folder) { + TreeNode currentNode = folder; + TreeNode file = null; + for (int i = 0; i < 6; i++) { + file = new TreeNode(new IconTreeItemHolder.IconTreeItem(R.string.ic_folder, NAME + " " + i)); + currentNode.addChild(file); + currentNode = file; + } + return file; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("tState", tView.getSaveState()); + } + + @Override + public void onClick(TreeNode node, Object value) { + Toast toast = Toast.makeText(getActivity(), ((IconTreeItemHolder.IconTreeItem)value).text, Toast.LENGTH_SHORT); + toast.show(); + } +} diff --git a/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java b/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java index c58d084..841ab35 100644 --- a/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java +++ b/app/src/main/java/com/unnamed/b/atv/sample/holder/ArrowExpandSelectableHeaderHolder.java @@ -42,7 +42,7 @@ public View createNodeView(final TreeNode node, IconTreeItemHolder.IconTreeItem arrowView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - tView.toggleNode(node); + tView.toggleNodeExpansion(node); } }); diff --git a/app/src/main/res/values-v17/styles.xml b/app/src/main/res/values-v17/styles.xml new file mode 100644 index 0000000..b672c61 --- /dev/null +++ b/app/src/main/res/values-v17/styles.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/build.gradle b/build.gradle index ebf706b..9716401 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:3.0.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -22,6 +23,7 @@ allprojects { repositories { jcenter() + google() } } diff --git a/gradle.properties b/gradle.properties index ddb8205..765496e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,14 +17,14 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=1.2.9 -VERSION_CODE=11 +VERSION_NAME=1.3.0 +VERSION_CODE=12 -ANDROID_BUILD_MIN_SDK_VERSION=11 -ANDROID_BUILD_TARGET_SDK_VERSION=21 -ANDROID_BUILD_SDK_VERSION=21 -ANDROID_BUILD_TOOLS_VERSION=21.1.2 +ANDROID_BUILD_MIN_SDK_VERSION=14 +ANDROID_BUILD_TARGET_SDK_VERSION=27 +ANDROID_BUILD_SDK_VERSION=27 +ANDROID_BUILD_TOOLS_VERSION=27.0.1 GROUP=com.github.bmelnychuk POM_DESCRIPTION=Tree View implementation for android diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c71e76..7db30d9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index 92ecb9f..ee1b633 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -19,7 +19,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:21.0.3' + compile 'com.android.support:appcompat-v7:27.0.2' } apply from: '../maven_push.gradle' diff --git a/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java b/library/src/main/java/com/unnamed/b/atv/holder/SimpleViewHolder.java old mode 100644 new mode 100755 diff --git a/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java b/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java old mode 100644 new mode 100755 index dbf33f8..41a6285 --- a/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java +++ b/library/src/main/java/com/unnamed/b/atv/model/TreeNode.java @@ -1,6 +1,7 @@ package com.unnamed.b.atv.model; import android.content.Context; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -53,6 +54,23 @@ public TreeNode addChild(TreeNode childNode) { return this; } + /** + * Adds a child node at give position + * @param childNode + * @param position Fist position is 0. If -1, child is inserted at the end + * @return + */ + public TreeNode addChild(TreeNode childNode, int position) { + childNode.mParent = this; + childNode.mId = generateId(); + + if(position == -1 || position > children.size()) + position = childNode.size(); + + children.add(position, childNode); + return this; + } + public TreeNode addChildren(TreeNode... nodes) { for (TreeNode n : nodes) { addChild(n); @@ -97,6 +115,10 @@ public boolean isLeaf() { return size() == 0; } + public boolean isBranch() { + return size() > 0; + } + public Object getValue() { return mValue; } @@ -112,6 +134,9 @@ public TreeNode setExpanded(boolean expanded) { public void setSelected(boolean selected) { mSelected = selected; + if(mViewHolder != null) { + mViewHolder.toggleSelection(mSelected); + } } public boolean isSelected() { @@ -191,6 +216,18 @@ public BaseNodeViewHolder getViewHolder() { return mViewHolder; } + public boolean isInitialized() { + return mViewHolder != null ? mViewHolder.isInitialized() : false; + } + + public View getView() { + return mViewHolder != null ? mViewHolder.getView() : null; + } + + public Boolean hasView() { + return getView() != null; + } + public boolean isFirstChild() { if (!isRoot()) { List parentChildren = mParent.children; @@ -225,9 +262,11 @@ public static abstract class BaseNodeViewHolder { private View mView; protected int containerStyle; protected Context context; + protected LayoutInflater layoutInflater; public BaseNodeViewHolder(Context context) { this.context = context; + this.layoutInflater = LayoutInflater.from(context); } public View getView() { @@ -280,5 +319,9 @@ public void toggle(boolean active) { public void toggleSelectionMode(boolean editModeEnabled) { // empty } + + public void toggleSelection(boolean isSelected) { + + } } } diff --git a/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java b/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java old mode 100644 new mode 100755 index 222a43a..a11b365 --- a/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java +++ b/library/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java @@ -5,6 +5,7 @@ import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.animation.Animation; import android.view.animation.Transformation; import android.widget.LinearLayout; @@ -26,8 +27,8 @@ public class AndroidTreeView { private static final String NODES_PATH_SEPARATOR = ";"; - protected TreeNode mRoot; - private Context mContext; + protected Context mContext; + private TreeNode mRoot; private boolean applyForRoot; private int containerStyle = 0; private Class defaultViewHolderClass = SimpleViewHolder.class; @@ -36,7 +37,12 @@ public class AndroidTreeView { private boolean mSelectionModeEnabled; private boolean mUseDefaultAnimation = false; private boolean use2dScroll = false; - private boolean enableAutoToggle = true; + private boolean enableExpansionAutoToggle = true; + private boolean enableSelectionsAutoToggle = true; + private ViewGroup mRootView; + private TreeNode currentSelectedLeaf; + private boolean autoScrollToExpandedNode = true; + private boolean autoScrollToSelectedLeafs = false; public AndroidTreeView(Context context) { mContext = context; @@ -51,6 +57,14 @@ public AndroidTreeView(Context context, TreeNode root) { mContext = context; } + public void setAutoScrollToSelectedLeafs(boolean autoScrollToSelectedLeafs) { + this.autoScrollToSelectedLeafs = autoScrollToSelectedLeafs; + } + + public void setAutoScrollToExpandedNode(boolean autoScrollToExpandedNode) { + this.autoScrollToExpandedNode = autoScrollToExpandedNode; + } + public void setDefaultAnimation(boolean defaultAnimation) { this.mUseDefaultAnimation = defaultAnimation; } @@ -72,12 +86,16 @@ public boolean is2dScrollEnabled() { return use2dScroll; } - public void setUseAutoToggle(boolean enableAutoToggle) { - this.enableAutoToggle = enableAutoToggle; + public void setExpansionAutoToggle(boolean enableAutoToggle) { + this.enableExpansionAutoToggle = enableAutoToggle; + } + + public boolean isExpansionAutoToggleEnabled() { + return enableExpansionAutoToggle; } - public boolean isAutoToggleEnabled() { - return enableAutoToggle; + public void setLeafSelectionAutoToggle(boolean enableSelectionsAutoToggle) { + this.enableSelectionsAutoToggle = enableSelectionsAutoToggle; } public void setDefaultViewHolder(Class viewHolder) { @@ -104,12 +122,11 @@ public void collapseAll() { public View getView(int style) { - final ViewGroup view; if (style > 0) { ContextThemeWrapper newContext = new ContextThemeWrapper(mContext, style); - view = use2dScroll ? new TwoDScrollView(newContext) : new ScrollView(newContext); + mRootView = use2dScroll ? new TwoDScrollView(newContext) : new ScrollView(newContext); } else { - view = use2dScroll ? new TwoDScrollView(mContext) : new ScrollView(mContext); + mRootView = use2dScroll ? new TwoDScrollView(mContext) : new ScrollView(mContext); } Context containerContext = mContext; @@ -120,7 +137,7 @@ public View getView(int style) { viewTreeItems.setId(R.id.tree_items); viewTreeItems.setOrientation(LinearLayout.VERTICAL); - view.addView(viewTreeItems); + mRootView.addView(viewTreeItems); mRoot.setViewHolder(new TreeNode.BaseNodeViewHolder(mContext) { @Override @@ -135,7 +152,7 @@ public ViewGroup getNodeItemsView() { }); expandNode(mRoot, false); - return view; + return mRootView; } public View getView() { @@ -150,18 +167,25 @@ public void expandLevel(int level) { } private void expandLevel(TreeNode node, int level) { + boolean lastAutoScrollEnabled = autoScrollToExpandedNode; + autoScrollToExpandedNode = false; if (node.getLevel() <= level) { expandNode(node, false); } for (TreeNode n : node.getChildren()) { expandLevel(n, level); } + autoScrollToExpandedNode = lastAutoScrollEnabled; } public void expandNode(TreeNode node) { expandNode(node, false); } + public void collapseNodeWithSubnodes(TreeNode node) { + collapseNode(node, true); + } + public void collapseNode(TreeNode node) { collapseNode(node, false); } @@ -203,13 +227,21 @@ private void getSaveState(TreeNode root, StringBuilder sBuilder) { } } - public void toggleNode(TreeNode node) { + public void toggleNodeExpansion(TreeNode node) { if (node.isExpanded()) { collapseNode(node, false); } else { expandNode(node, false); } + } + private void toggleLeafSelection(TreeNode node) { + if (node.isLeaf()) { + if (currentSelectedLeaf != null) { + selectNode(currentSelectedLeaf, false); + } + selectNode(node, !node.isSelected()); + } } private void collapseNode(TreeNode node, final boolean includeSubnodes) { @@ -238,7 +270,7 @@ private void expandNode(final TreeNode node, boolean includeSubnodes) { parentViewHolder.toggle(true); for (final TreeNode n : node.getChildren()) { - addNode(parentViewHolder.getNodeItemsView(), n); + addNode(parentViewHolder.getNodeItemsView(), n, -1); if (n.isExpanded() || includeSubnodes) { expandNode(n, includeSubnodes); @@ -249,14 +281,83 @@ private void expandNode(final TreeNode node, boolean includeSubnodes) { expand(parentViewHolder.getNodeItemsView()); } else { parentViewHolder.getNodeItemsView().setVisibility(View.VISIBLE); + parentViewHolder.getNodeItemsView().getLayoutParams().height = + LinearLayout.LayoutParams.WRAP_CONTENT; + parentViewHolder.getNodeItemsView().requestLayout(); + } + + if (node != mRoot) { + if ((node.isLeaf() && autoScrollToSelectedLeafs) || + (node.isBranch() && autoScrollToExpandedNode)) { + scrollToNode(node); + } } } - private void addNode(ViewGroup container, final TreeNode n) { + public void expandNodeIncludingParents(TreeNode node, boolean autoScroll) { + List parents = getParents(node); + for (TreeNode parentNode : parents) { + boolean lastAutoScrollEnabled = autoScrollToExpandedNode; + autoScrollToExpandedNode = false; + expandNode(parentNode); + autoScrollToExpandedNode = lastAutoScrollEnabled; + } + if (autoScroll) { + scrollToNode(node); + } + } + + private List getParents(TreeNode node) { + List parents = new ArrayList<>(); + TreeNode parent = node; + while (parent != mRoot) { + parents.add(0, parent); + parent = parent.getParent(); + } + return parents; + } + + public void scrollToNode(final TreeNode node) { + if (node.isInitialized()) { + if(node.getView().getHeight() == 0) { + node.getView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + node.getView().getViewTreeObserver().removeGlobalOnLayoutListener(this); + scrollToNode(node); + } + }); + return; + } + int yToScroll = ((int) node.getView().getY()); + ViewGroup parent = ((ViewGroup) node.getView().getParent()); + while (parent != mRootView) { + yToScroll += parent.getY(); + parent = ((ViewGroup) parent.getParent()); + } + if (mRootView instanceof TwoDScrollView) { + ((TwoDScrollView) mRootView).smoothScrollTo(0, yToScroll); + } else if (mRootView instanceof ScrollView) { + ((ScrollView) mRootView).smoothScrollTo(0, yToScroll); + } + } + } + + /** + * Add the view corresponding to TreeNode to the View Group + * @param container + * @param n + */ + private void addNode(ViewGroup container, final TreeNode n, int index) { final TreeNode.BaseNodeViewHolder viewHolder = getViewHolderForNode(n); final View nodeView = viewHolder.getView(); - container.addView(nodeView); + + if(index == -1) + container.addView(nodeView); + else + container.addView(nodeView, index); + if (mSelectionModeEnabled) { viewHolder.toggleSelectionMode(mSelectionModeEnabled); } @@ -264,28 +365,24 @@ private void addNode(ViewGroup container, final TreeNode n) { nodeView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + toggleAll(n); if (n.getClickListener() != null) { n.getClickListener().onClick(n, n.getValue()); } else if (nodeClickListener != null) { nodeClickListener.onClick(n, n.getValue()); } - if (enableAutoToggle) { - toggleNode(n); - } } }); nodeView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { + toggleAll(n); if (n.getLongClickListener() != null) { return n.getLongClickListener().onLongClick(n, n.getValue()); } else if (nodeLongClickListener != null) { return nodeLongClickListener.onLongClick(n, n.getValue()); } - if (enableAutoToggle) { - toggleNode(n); - } return false; } }); @@ -294,6 +391,15 @@ public boolean onLongClick(View view) { //------------------------------------------------------------ // Selection methods + public void toggleAll(TreeNode node) { + if (enableExpansionAutoToggle) { + toggleNodeExpansion(node); + } + if (enableSelectionsAutoToggle) { + toggleLeafSelection(node); + } + } + public void setSelectionModeEnabled(boolean selectionModeEnabled) { if (!selectionModeEnabled) { // TODO fix double iteration over tree @@ -370,6 +476,13 @@ private void makeAllSelection(boolean selected, boolean skipCollapsed) { public void selectNode(TreeNode node, boolean selected) { if (mSelectionModeEnabled) { + if (node.isLeaf()) { + if (selected) { + currentSelectedLeaf = node; + } else if (node == currentSelectedLeaf) { + currentSelectedLeaf = null; + } + } node.setSelected(selected); toogleSelectionForNode(node, true); } @@ -467,11 +580,26 @@ public boolean willChangeBounds() { //----------------------------------------------------------------- //Add / Remove + /** + * Add the node in the end + * @param parent + * @param nodeToAdd + */ public void addNode(TreeNode parent, final TreeNode nodeToAdd) { - parent.addChild(nodeToAdd); + addNode(parent, nodeToAdd, -1); + } + + /** + * Adds the node at given position + * @param parent + * @param nodeToAdd + * @param position if -1, node is add in the last + */ + public void addNode(TreeNode parent, final TreeNode nodeToAdd, int position) { + parent.addChild(nodeToAdd, position); if (parent.isExpanded()) { final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(parent); - addNode(parentViewHolder.getNodeItemsView(), nodeToAdd); + addNode(parentViewHolder.getNodeItemsView(), nodeToAdd, position); } } diff --git a/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java b/library/src/main/java/com/unnamed/b/atv/view/TreeNodeWrapperView.java old mode 100644 new mode 100755 diff --git a/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java b/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java old mode 100644 new mode 100755 index 298e060..ef638c1 --- a/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java +++ b/library/src/main/java/com/unnamed/b/atv/view/TwoDScrollView.java @@ -567,9 +567,9 @@ private View findFocusableViewInBounds(boolean topFocus, int top, int bottom, bo * component is a good candidate for focus, this scrollview reclaims the * focus.

* - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} + * @param direction the scroll direction: {@link View#FOCUS_UP} * to go the top of the view or - * {@link android.view.View#FOCUS_DOWN} to go the bottom + * {@link View#FOCUS_DOWN} to go the bottom * @return true if the key event is consumed by this method, false otherwise */ public boolean fullScroll(int direction, boolean horizontal) { @@ -610,9 +610,9 @@ public boolean fullScroll(int direction, boolean horizontal) { * to a component visible in this area. If no component can be focused in * the new visible area, the focus is reclaimed by this scrollview.

* - * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} + * @param direction the scroll direction: {@link View#FOCUS_UP} * to go upward - * {@link android.view.View#FOCUS_DOWN} to downward + * {@link View#FOCUS_DOWN} to downward * @param top the top offset of the new area to be made visible * @param bottom the bottom offset of the new area to be made visible * @return true if the key event is consumed by this method, false otherwise @@ -945,7 +945,7 @@ public void requestChildFocus(View child, View focused) { * When looking for focus in children of a scroll view, need to be a little * more careful not to give focus to something that is scrolled off screen. *

- * This is more expensive than the default {@link android.view.ViewGroup} + * This is more expensive than the default {@link ViewGroup} * implementation, otherwise this behavior might have been made the default. */ @Override