package com.application.zhangshi_app_android.widget; import android.content.Context; import android.graphics.Color; import android.graphics.Path; import android.graphics.Typeface; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import androidx.constraintlayout.utils.widget.ImageFilterView; import androidx.core.content.res.ResourcesCompat; import androidx.databinding.DataBindingUtil; import com.android.app_base.manager.AppManager; import com.android.app_base.manager.UserManager; import com.android.app_base.utils.GlideUtil; import com.android.app_base.utils.ScreenSizeUtils; import com.application.zhangshi_app_android.R; import com.application.zhangshi_app_android.bean.HomeRootBean; import com.application.zhangshi_app_android.databinding.ItemFamilyMemberBinding; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; /** * @author Ljj * @date 2023.08.13. 6:47 * @desc */ public abstract class Node{ protected Context context; protected HomeRootBean member;//成员 protected Node parent;//父节点 protected final List children = new ArrayList<>(); //子节点 protected HomeMindMapLayout.OnItemClickListener onItemClickListener; protected static final int pathLength = ScreenSizeUtils.dip2px(20);// 连线的一般长度 protected static final int cornerRadius = ScreenSizeUtils.dip2px(5); // 连线拐角 圆角半径 protected static final int spouseMargin = ScreenSizeUtils.dip2px(15);// 配偶两个view之间的间隔 protected static final int verticalMargin = ScreenSizeUtils.dip2px(20);// //节点竖直方向间距 protected static final int horizontalMargin = 2 * pathLength;// 分叉节点之间的间距 protected boolean isExpand = true;//是否展开子节点 protected boolean isVisible = true;//是否可见 protected int width, height;// 当前节点的宽高 protected int viewLeft;// view的左边 protected int viewRight;// view右边 protected int viewTop;// view的上边 protected int viewBottom;// view的下边 protected int left;// 节点左边 protected int right;// 节点右边 protected int top;// 节点顶部 protected int bottom;// 节点底部 protected int centerY;// 节点中间Y protected final List nextPathList = new ArrayList<>();//后置连线 protected final List prePathList = new ArrayList<>();//前置连线 protected final List forkPathList = new ArrayList<>();//分叉连线 protected final List viewList = new ArrayList<>(); public Node(Context context,HomeRootBean member){ this.context = context; this.member = member; if (member != null) { // 递归创建子节点 if (member.getChildList() != null && member.getChildList().size() > 0) { for (HomeRootBean child : member.getChildList()) { // 根据是否有配偶创建不同的节点 if (child != null) { Node node; if (child.getSpouse() != null) { node = new DoubleNode(context,child); } else { node = new SimpleNode(context,child); } node.parent = this; children.add(node); } } } } } /** * 初始化所有节点 * @param x 所有节点最左边的x坐标 * @param y 所有节点最高的y坐标 */ public void setPosition(int x,int y){ if (this.parent!=null){ throw new RuntimeException("只能初始化根节点"); } //根据可见性找到最后一个第一子节点,即最右上边的节点 setViewPositionRecursion(getVisibleTopNode(), this,x,y); } /** * 递归设置所有节点的位置 * @param topNode 顶部节点,这颗节点树的最上边的节点 * @param rootNode 根节点,这颗节点树的根节点 * rootNode 必须是 topNode 祖先节点 * @param x 整颗节点树的x坐标 * @param y 整颗节点树的y坐标 */ public void setViewPositionRecursion(Node topNode,Node rootNode,int x,int y){ if (topNode == rootNode) { //如果顶部节点就是根节点,那么根节点的位置就是x,y,不需要继续递归 rootNode.setViewPosition(x, y + rootNode.getAllVisibleHeight()/2); return; } //因为 y 是整颗节点树的y坐标,正常情况下,y都会是 顶部节点topNode 的y坐标 int centerY = y + topNode.getAllVisibleHeight()/2; //但是 有一种情况,就是顶部节点topNode没有配偶,如果topNode的祖先节点有配偶,且没有多子孙,此时topNode的高度比有配偶的祖先节点的高度小,所以此时的y是高度最高的祖先节点的y坐标 if (topNode instanceof SimpleNode){ Node tempNode = topNode.parent; while (tempNode.children.size() == 1){ if (tempNode.height > topNode.height){ centerY = y + tempNode.getAllVisibleHeight()/2; break; } tempNode = tempNode.parent; } } topNode.setViewPosition(x + topNode.getToSpecifyNodeWidth(rootNode) - topNode.width, centerY); // 根据topNode的位置,递归设置所有节点的位置 Node node = topNode; while (node != rootNode) { Node parentNode = node.parent; List centerYList = new ArrayList<>(); int currentBottom = 0; for (Node child : parentNode.children) { if (child == node) { centerYList.add(child.centerY); currentBottom = currentBottom + child.getVisibleTop() + child.getAllVisibleHeight(); continue; } if (!child.isVisible){ continue; } // 递归设置以child为根节点的所有节点的位置 setViewPositionRecursion(child.getVisibleTopNode(), child, node.left,currentBottom + verticalMargin); currentBottom = currentBottom + verticalMargin + child.getAllVisibleHeight(); centerYList.add(child.centerY); } //取最大值、最小值 和 的中间 int totalCenterY = 0; if (centerYList.size() > 1) { int min = centerYList.get(0); int max = centerYList.get(0); for (int i = 1; i < centerYList.size(); i++) { int centerY1 = centerYList.get(i); if (centerY1 < min) { min = centerY1; } if (centerY1 > max) { max = centerY1; } } totalCenterY = (min + max) / 2; }else{ totalCenterY = centerYList.get(0); } if (parentNode.children.size() > 1) { // 设置父节点的位置 parentNode.setViewPosition(node.left - horizontalMargin - parentNode.width, totalCenterY); // 设置分叉节点的位置 parentNode.setForkYList(centerYList); }else { parentNode.setViewPosition(node.left - parentNode.width, totalCenterY); } node = parentNode; } } //获取 以当前节点为根节点的树 的最顶部的y坐标 public int getVisibleTop(){ if (!isVisible){ return 0; } if (!isExpand){ return top; } if (children.size() > 0){ return Math.min(top,children.get(0).getVisibleTop()); } return top; } //获取 以当前节点为根节点的树 的最深一节点 public Node getVisibleTopNode(){ Node topNode; if (!isVisible){ return null; } if (!isExpand){ return this; } if (children.size() > 0) { topNode = children.get(0).getVisibleTopNode(); } else { topNode = this; } return topNode; } // 设置对应类型节点的位置 由子类实现 protected abstract void setViewPosition(int x, int centerY); // 设置分叉节点的位置 由子类实现,必须先setViewPosition才能调用 protected abstract void setForkYList(List yList); /** * 创建节点item视图 * @param bean 节点数据 * @param isFamily 是否是本家成员 * @return 节点item视图 */ protected View createItemView(HomeRootBean bean,boolean isFamily){ // 使用布局填充器加载节点布局 LayoutInflater inflater = LayoutInflater.from(context); ItemFamilyMemberBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_family_member, null, false); View familyMemberView = binding.getRoot(); // 防止addView时,所有这里创建的view的高宽被限制成一样的 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); familyMemberView.setLayoutParams(params); familyMemberView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (onItemClickListener != null){ onItemClickListener.onItemClick(familyMemberView,bean,isFamily); } } }); if (bean != null) { binding.setBean(bean); if (!isFamily){ binding.tvName.setTextColor(Color.parseColor("#FF8296C5")); //获取字体 Typeface typeface = ResourcesCompat.getFont(context, com.android.app_base.R.font.roboto_regular); binding.tvName.setTypeface(typeface); } // 使用databinding无法测量view的带内容的准确宽高,所以这里手动设置一下 binding.tvGeneration.setText(bean.getIdentity()+""); if (bean.getNickName().equals(UserManager.getInstance().getUserName())){ binding.cvContainer.setCardBackgroundColor(Color.parseColor("#FFCEE4FD")); binding.ivMyself.setVisibility(View.VISIBLE); } } familyMemberView.measure(View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST)); return familyMemberView; } protected ImageView createExpandIconView() { ImageView expandView = new ImageView(context); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(pathLength,pathLength); expandView.setLayoutParams(params); int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(pathLength, View.MeasureSpec.EXACTLY); int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(pathLength, View.MeasureSpec.EXACTLY); expandView.measure(widthMeasureSpec,heightMeasureSpec); return expandView; } public void setVisible(boolean visible) { isVisible = visible; setViewVisible(visible); if (!visible){//不可见时 子节点也不可见 for (Node child : children) { child.setVisible(false); } }else { //可见时,子节点可见性由是否展开决定 for (Node child : children) { child.setVisible(isExpand); } } } private void setViewVisible(boolean visible){ for (View view : viewList) { view.setVisibility(visible?View.VISIBLE:View.GONE); } } //计算从根节点到当前节点的 所经过 的 分叉节点数(有多个子节点的节点) public int calculateForkCountRecursion(Node node){ if (node == null) { return 0; } if (node.parent == null){ return 0; } if (node.parent.children.size() > 1){ return 1 + calculateForkCountRecursion(node.parent); }else { return calculateForkCountRecursion(node.parent); } } public int getAllWidth(){ int maxWidth = 0; for (Node deepestVisibleNode : findDeepestNodes(false)) { int width = deepestVisibleNode.getToSpecifyNodeWidth(null); if (width > maxWidth){ maxWidth = width; } } return maxWidth; } //获取 ”显示出 以当前节点为根节点 的 所有可见节点 “所需的宽度 public int getAllVisibleWidth(){ int maxWidth = 0; if (isVisible){ for (Node deepestVisibleNode : findDeepestNodes(true)) { int width = deepestVisibleNode.getToSpecifyNodeWidth(null); if (width > maxWidth){ maxWidth = width; } } } return maxWidth; } //获取当前节点到只指定节点的全部宽度 (指定节点为null时,表示到根节点) public int getToSpecifyNodeWidth(Node specifyNode){ int totalWidth = width; Node currentNode = parent; if (specifyNode == null){ while (currentNode != null){ totalWidth += currentNode.width; if (currentNode.children.size() > 1){ // totalWidth += horizontalMargin; } currentNode = currentNode.parent; } }else { boolean flag = false; while (currentNode != null){ totalWidth += currentNode.width; if (currentNode.children.size() > 1){ totalWidth += horizontalMargin; } if (currentNode == specifyNode){ flag = true; break; } currentNode = currentNode.parent; } if (!flag){ totalWidth = 0; } } return totalWidth; } // 寻找当前可见的最深节点列表,true表示只考虑可见节点,false表示考虑所有节点 public List findDeepestNodes(boolean considerVisible) { List deepestNodes = new ArrayList<>(); // 初始化为空列表 int maxDepth = -1; // 默认深度为-1,表示没有找到最深节点 Queue queue = new LinkedList<>(); queue.offer(this); while (!queue.isEmpty()) { Node current = queue.poll(); int currentDepth = current.getDepth(); // 根据传入的参数判断是否考虑可见节点 if (!considerVisible || (considerVisible && current.isVisible)) { if (currentDepth >= maxDepth) { // 如果当前节点的深度大于等于maxDepth,更新maxDepth if (currentDepth > maxDepth) { maxDepth = currentDepth; deepestNodes.clear(); // 清空之前的结果 } deepestNodes.add(current); } } // 只有当前节点可见时才继续遍历其子节点 if (!considerVisible || (considerVisible && current.isVisible)) { for (Node child : current.children) { if (child.parent != null) { queue.offer(child); } } } } return deepestNodes; } // 获取节点的深度 public int getDepth() { int depth = 0; Node currentNode = this; while (currentNode.parent != null) { depth++; currentNode = currentNode.parent; } return depth; } public int getAllHeight(){ return getNodeTreeHeightRecursion(this,false); } //获取 ”显示出 以当前节点为根节点 的 所有可见节点 “所需的高度 public int getAllVisibleHeight(){ return isVisible? getNodeTreeHeightRecursion(this,true):0; } // 递归获取节点的可见高度总和,true表示考虑节点的可见性(即获取可见节点),false表示不考虑(即获取全部节点) public int getNodeTreeHeightRecursion(Node node, boolean considerVisible){ if (node == null){ return 0; } if (considerVisible) { if (!node.isVisible) { return 0; } if (!node.isExpand) { return node.height; } } if (node.children.size() == 0){ return node.height; } else { int heightSum = 0; for (Node child : node.children) { int childHeight = getNodeTreeHeightRecursion(child,considerVisible); //如果有多个子节点且子节点有高度,那么子节点之间还有竖直间隔 if (node.children.size() > 1 && childHeight > 0){ heightSum += verticalMargin; } heightSum += childHeight; } //子节点每两个之间还有竖直间隔 return Math.max(node.height,heightSum); } } public int getWidth(){ return width; } public int getHeight(){ return height; } public List getChildren() { return children; } public void addChild(Node node){ children.add(node); } public boolean isExpand() { return isExpand; } public void setExpand(boolean expand) { isExpand = expand; } public boolean isVisible() { return isVisible; } public List getNextPathList() { return nextPathList; } public List getPrePathList() { return prePathList; } public List getViewList() { return viewList; } // 获取 每个 itemView 的宽度 public abstract int getItemViewWidth(); // 获取 每个 itemView 的高度 public abstract int getItemViewHeight(); //获取展开按钮的宽度 public int getExpandIconWidth(){ ImageView expandIconView = createExpandIconView(); return expandIconView.getMeasuredWidth(); } public int getViewWidth(){ return viewBottom - viewTop; } public void setOnItemClickListener(HomeMindMapLayout.OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; //所有的子节点也设置 for (Node child : children) { child.setOnItemClickListener(onItemClickListener); } } }