diff --git a/doric-android/devkit/src/main/AndroidManifest.xml b/doric-android/devkit/src/main/AndroidManifest.xml
index c99562c9..a58da216 100644
--- a/doric-android/devkit/src/main/AndroidManifest.xml
+++ b/doric-android/devkit/src/main/AndroidManifest.xml
@@ -11,5 +11,6 @@
android:name=".ui.DoricDevActivity"
android:theme="@style/Theme.Design.Light.NoActionBar" />
+
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
index d9c685b5..51fe0516 100644
--- a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
@@ -21,7 +21,6 @@ import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -50,6 +49,8 @@ import pub.doric.devkit.qrcode.DisplayUtil;
import pub.doric.devkit.qrcode.activity.CaptureActivity;
import pub.doric.devkit.qrcode.activity.CodeUtils;
+import static pub.doric.devkit.ui.DoricShowNodeTreeActivity.DORIC_CONTEXT_ID_KEY;
+
public class DoricDevActivity extends AppCompatActivity implements DoricDev.StatusCallback {
private int REQUEST_CODE = 100;
private ContextCellAdapter cellAdapter;
@@ -363,6 +364,9 @@ public class DoricDevActivity extends AppCompatActivity implements DoricDev.Stat
builder.show();
}
});
+ ArrayList list = new ArrayList<>();
+ list.add("View source");
+ list.add("Show node tree");
if (DoricDev.getInstance().isInDevMode()) {
if (context.getDriver() instanceof DoricDebugDriver) {
actionMap.put("Stop debugging", new DialogInterface.OnClickListener() {
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricShowNodeTreeActivity.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricShowNodeTreeActivity.java
new file mode 100644
index 00000000..60173977
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricShowNodeTreeActivity.java
@@ -0,0 +1,100 @@
+package pub.doric.devkit.ui;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import pub.doric.DoricContext;
+import pub.doric.DoricContextManager;
+import pub.doric.devkit.R;
+import pub.doric.devkit.ui.treeview.DoricViewNodeLayoutItemType;
+import pub.doric.devkit.ui.treeview.DoricViewNodeTreeViewBinder;
+import pub.doric.devkit.ui.treeview.TreeNode;
+import pub.doric.devkit.ui.treeview.TreeViewAdapter;
+import pub.doric.shader.GroupNode;
+import pub.doric.shader.ViewNode;
+
+public class DoricShowNodeTreeActivity extends AppCompatActivity {
+
+ public static final String DORIC_CONTEXT_ID_KEY = "DORIC_CONTEXT_ID";
+
+ private RecyclerView rv;
+ private TreeViewAdapter adapter;
+
+ private DoricContext doricContext;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ String contextId = getIntent().getStringExtra(DORIC_CONTEXT_ID_KEY);
+ doricContext = DoricContextManager.getContext(contextId);
+
+ setContentView(R.layout.layout_show_node_tree);
+ initView();
+ initData();
+ }
+
+ private void initData() {
+ List nodes = new ArrayList<>();
+ TreeNode root = new TreeNode<>(new DoricViewNodeLayoutItemType(doricContext.getRootNode()));
+
+ Queue viewQueue = new LinkedList<>();
+ Queue treeQueue = new LinkedList<>();
+
+ viewQueue.offer(doricContext.getRootNode());
+ treeQueue.offer(root);
+
+ while (!viewQueue.isEmpty()) {
+ ViewNode viewNode = viewQueue.poll();
+ TreeNode treeNode = treeQueue.poll();
+ if (viewNode instanceof GroupNode) {
+ GroupNode groupNode = (GroupNode) viewNode;
+ for (int i = 0; i != groupNode.getChildNodes().size(); i++) {
+ assert treeNode != null;
+ TreeNode temp = new TreeNode(new DoricViewNodeLayoutItemType((ViewNode) groupNode.getChildNodes().get(i)));
+ treeNode.addChild(temp);
+
+ viewQueue.offer((ViewNode) groupNode.getChildNodes().get(i));
+ treeQueue.offer(temp);
+ }
+ }
+ }
+
+ nodes.add(root);
+
+ rv.setLayoutManager(new LinearLayoutManager(this));
+ adapter = new TreeViewAdapter(nodes, Collections.singletonList(new DoricViewNodeTreeViewBinder()));
+ // whether collapse child nodes when their parent node was close.
+// adapter.ifCollapseChildWhileCollapseParent(true);
+ adapter.setOnTreeNodeListener(new TreeViewAdapter.OnTreeNodeListener() {
+ @Override
+ public boolean onClick(TreeNode node, RecyclerView.ViewHolder holder) {
+ if (!node.isLeaf()) {
+ //Update and toggle the node.
+ onToggle(!node.isExpand(), holder);
+// if (!node.isExpand())
+// adapter.collapseBrotherNode(node);
+ }
+ return false;
+ }
+
+ @Override
+ public void onToggle(boolean isExpand, RecyclerView.ViewHolder holder) {
+ DoricViewNodeTreeViewBinder.ViewHolder viewNodeTreeViewBinder = (DoricViewNodeTreeViewBinder.ViewHolder) holder;
+ }
+ });
+ rv.setAdapter(adapter);
+ }
+
+ private void initView() {
+ rv = findViewById(R.id.show_node_tree_rv);
+ }
+}
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeLayoutItemType.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeLayoutItemType.java
new file mode 100644
index 00000000..ed832ffc
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeLayoutItemType.java
@@ -0,0 +1,17 @@
+package pub.doric.devkit.ui.treeview;
+
+import pub.doric.devkit.R;
+import pub.doric.shader.ViewNode;
+
+public class DoricViewNodeLayoutItemType implements LayoutItemType {
+ private final ViewNode viewNode;
+
+ public DoricViewNodeLayoutItemType(ViewNode viewNode) {
+ this.viewNode = viewNode;
+ }
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.layout_show_node_tree_cell;
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeTreeViewBinder.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeTreeViewBinder.java
new file mode 100644
index 00000000..8ae12939
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/DoricViewNodeTreeViewBinder.java
@@ -0,0 +1,30 @@
+package pub.doric.devkit.ui.treeview;
+
+import android.view.View;
+
+import pub.doric.devkit.R;
+
+public class DoricViewNodeTreeViewBinder extends TreeViewBinder {
+
+ @Override
+ public ViewHolder provideViewHolder(View itemView) {
+ return new ViewHolder(itemView);
+ }
+
+ @Override
+ public void bindView(ViewHolder holder, int position, TreeNode node) {
+ DoricViewNodeLayoutItemType layoutItemType = (DoricViewNodeLayoutItemType) node.getContent();
+ }
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.layout_show_node_tree_cell;
+ }
+
+ public static class ViewHolder extends TreeViewBinder.ViewHolder {
+
+ public ViewHolder(View rootView) {
+ super(rootView);
+ }
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/LayoutItemType.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/LayoutItemType.java
new file mode 100644
index 00000000..0c4c505f
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/LayoutItemType.java
@@ -0,0 +1,5 @@
+package pub.doric.devkit.ui.treeview;
+
+public interface LayoutItemType {
+ int getLayoutId();
+}
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeNode.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeNode.java
new file mode 100644
index 00000000..99d34211
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeNode.java
@@ -0,0 +1,145 @@
+package pub.doric.devkit.ui.treeview;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TreeNode implements Cloneable {
+ private T content;
+ private TreeNode parent;
+ private List childList;
+ private boolean isExpand;
+ private boolean isLocked;
+ //the tree high
+ private int height = UNDEFINE;
+
+ private static final int UNDEFINE = -1;
+
+ public TreeNode(@NonNull T content) {
+ this.content = content;
+ this.childList = new ArrayList<>();
+ }
+
+ public int getHeight() {
+ if (isRoot())
+ height = 0;
+ else if (height == UNDEFINE)
+ height = parent.getHeight() + 1;
+ return height;
+ }
+
+ public boolean isRoot() {
+ return parent == null;
+ }
+
+ public boolean isLeaf() {
+ return childList == null || childList.isEmpty();
+ }
+
+ public void setContent(T content) {
+ this.content = content;
+ }
+
+ public T getContent() {
+ return content;
+ }
+
+ public List getChildList() {
+ return childList;
+ }
+
+ public void setChildList(List childList) {
+ this.childList.clear();
+ for (TreeNode treeNode : childList) {
+ addChild(treeNode);
+ }
+ }
+
+ public TreeNode addChild(TreeNode node) {
+ if (childList == null)
+ childList = new ArrayList<>();
+ childList.add(node);
+ node.parent = this;
+ return this;
+ }
+
+ public boolean toggle() {
+ isExpand = !isExpand;
+ return isExpand;
+ }
+
+ public void collapse() {
+ if (isExpand) {
+ isExpand = false;
+ }
+ }
+
+ public void collapseAll() {
+ if (childList == null || childList.isEmpty()) {
+ return;
+ }
+ for (TreeNode child : this.childList) {
+ child.collapseAll();
+ }
+ }
+
+ public void expand() {
+ if (!isExpand) {
+ isExpand = true;
+ }
+ }
+
+ public void expandAll() {
+ expand();
+ if (childList == null || childList.isEmpty()) {
+ return;
+ }
+ for (TreeNode child : this.childList) {
+ child.expandAll();
+ }
+ }
+
+ public boolean isExpand() {
+ return isExpand;
+ }
+
+ public void setParent(TreeNode parent) {
+ this.parent = parent;
+ }
+
+ public TreeNode getParent() {
+ return parent;
+ }
+
+ public TreeNode lock() {
+ isLocked = true;
+ return this;
+ }
+
+ public TreeNode unlock() {
+ isLocked = false;
+ return this;
+ }
+
+ public boolean isLocked() {
+ return isLocked;
+ }
+
+ @Override
+ public String toString() {
+ return "TreeNode{" +
+ "content=" + this.content +
+ ", parent=" + (parent == null ? "null" : parent.getContent().toString()) +
+ ", childList=" + (childList == null ? "null" : childList.toString()) +
+ ", isExpand=" + isExpand +
+ '}';
+ }
+
+ @Override
+ protected TreeNode clone() throws CloneNotSupportedException {
+ TreeNode clone = new TreeNode<>(this.content);
+ clone.isExpand = this.isExpand;
+ return clone;
+ }
+}
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeViewAdapter.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeViewAdapter.java
new file mode 100644
index 00000000..e7438ab4
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/treeview/TreeViewAdapter.java
@@ -0,0 +1,325 @@
+package pub.doric.devkit.ui.treeview;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class TreeViewAdapter extends RecyclerView.Adapter {
+ private static final String KEY_IS_EXPAND = "IS_EXPAND";
+ private final List extends TreeViewBinder> viewBinders;
+ private List displayNodes;
+ private int padding = 30;
+ private OnTreeNodeListener onTreeNodeListener;
+ private boolean toCollapseChild;
+
+ public TreeViewAdapter(List extends TreeViewBinder> viewBinders) {
+ this(null, viewBinders);
+ }
+
+ public TreeViewAdapter(List nodes, List extends TreeViewBinder> viewBinders) {
+ displayNodes = new ArrayList<>();
+ if (nodes != null)
+ findDisplayNodes(nodes);
+ this.viewBinders = viewBinders;
+ }
+
+ /**
+ * 从nodes的结点中寻找展开了的非叶结点,添加到displayNodes中。
+ *
+ * @param nodes 基准点
+ */
+ private void findDisplayNodes(List nodes) {
+ for (TreeNode node : nodes) {
+ displayNodes.add(node);
+ if (!node.isLeaf() && node.isExpand())
+ findDisplayNodes(node.getChildList());
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return displayNodes.get(position).getContent().getLayoutId();
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(viewType, parent, false);
+ if (viewBinders.size() == 1)
+ return viewBinders.get(0).provideViewHolder(v);
+ for (TreeViewBinder viewBinder : viewBinders) {
+ if (viewBinder.getLayoutId() == viewType)
+ return viewBinder.provideViewHolder(v);
+ }
+ return viewBinders.get(0).provideViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List