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 com.android.app_base.manager.AppManager;
|
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 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<Node> 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<Path> nextPathList = new ArrayList<>();//后置连线
|
protected final List<Path> prePathList = new ArrayList<>();//前置连线
|
protected final List<Path> forkPathList = new ArrayList<>();//分叉连线
|
protected final List<View> 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 根节点,这颗节点树的根节点
|
* startNode 必须是 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坐标
|
//但是 有一种情况,就是顶部节点topNode刚好是根节点rootNode的唯一子节点,且topNode没有配偶,此时topNode的高度比rootNode的高度小,所以此时的y是rootNode的y坐标
|
int centerY;
|
if (rootNode.height >= topNode.height && rootNode == topNode.parent && rootNode.children.size() == 1){
|
centerY = y + rootNode.getAllVisibleHeight()/2;
|
}else {
|
centerY = y + topNode.getAllVisibleHeight()/2;
|
}
|
topNode.setViewPosition(x + topNode.getToSpecifyNodeWidth(rootNode) - topNode.width, centerY);
|
// 根据topNode的位置,递归设置所有节点的位置
|
Node node = topNode;
|
while (node != rootNode) {
|
Node parentNode = node.parent;
|
List<Integer> centerYList = new ArrayList<>();
|
int currentBottom = 0;
|
for (Node child : parentNode.children) {
|
if (child == node) {
|
centerYList.add(child.centerY);
|
currentBottom = currentBottom + node.getVisibleTopNode().top + node.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() > 0) {
|
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;
|
}
|
|
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;
|
}
|
}
|
|
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);
|
protected abstract void setForkYList(List<Integer> yList);
|
|
/**
|
* 创建节点item视图
|
* @param bean 节点数据
|
* @param isFamily 是否是本家成员
|
* @return 节点item视图
|
*/
|
protected View createItemView(HomeRootBean bean,boolean isFamily){
|
// 使用布局填充器加载节点布局
|
LayoutInflater inflater = LayoutInflater.from(context);
|
View familyMemberView = inflater.inflate(R.layout.item_family_member, null, false);
|
familyMemberView.measure(View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST),
|
View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST));
|
|
// 防止addView时,所有这里创建的view的高宽被限制成一样的
|
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
familyMemberView.setLayoutParams(params);
|
|
if (bean == null) {
|
return familyMemberView;
|
}
|
// 获取布局中的组件
|
TextView tv_generation = familyMemberView.findViewById(R.id.tv_generation);
|
ImageFilterView iv_avatar = familyMemberView.findViewById(R.id.iv_avatar);
|
TextView tv_name = familyMemberView.findViewById(R.id.tv_name);
|
|
if (!isFamily){
|
tv_name.setTextColor(Color.parseColor("#FF8296C5"));
|
tv_name.setTypeface(Typeface.DEFAULT);
|
}
|
// 设置节点内容
|
tv_generation.setText(String.valueOf(bean.getIdentity()));
|
if (bean.getImg() != null) {
|
GlideUtil.loadImage(bean.getImg(), iv_avatar);
|
}
|
tv_name.setText(bean.getNickName());
|
tv_name.setSelected(true);
|
familyMemberView.setOnClickListener(new View.OnClickListener() {
|
@Override
|
public void onClick(View v) {
|
if (onItemClickListener != null){
|
onItemClickListener.onItemClick(familyMemberView,bean);
|
}
|
}
|
});
|
return familyMemberView;
|
}
|
protected ImageView createExpandIconView() {
|
ImageView expandView = new ImageView(AppManager.getAppManager().currentActivity());
|
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<Node> findDeepestNodes(boolean considerVisible) {
|
List<Node> deepestNodes = new ArrayList<>(); // 初始化为空列表
|
int maxDepth = -1; // 默认深度为-1,表示没有找到最深节点
|
|
Queue<Node> 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) {
|
if (getNodeTreeHeightRecursion(child,considerVisible)!=0){
|
heightSum += verticalMargin;
|
}
|
heightSum += getNodeTreeHeightRecursion(child,considerVisible);
|
}
|
//子节点每两个之间还有竖直间隔
|
return Math.max(node.height,heightSum);
|
}
|
}
|
|
public int getWidth(){
|
return width;
|
}
|
|
public int getHeight(){
|
return height;
|
}
|
|
public List<Node> 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<Path> getNextPathList() {
|
return nextPathList;
|
}
|
|
public List<Path> getPrePathList() {
|
return prePathList;
|
}
|
|
public List<View> getViewList() {
|
return viewList;
|
}
|
|
// 获取 每个 itemView 的宽度
|
public int getItemViewWidth(){
|
View itemView = createItemView(null,false);
|
return itemView.getMeasuredWidth();
|
}
|
// 获取 每个 itemView 的高度
|
public int getItemViewHeight(){
|
View itemView = createItemView(null,false);
|
return itemView.getMeasuredHeight();
|
}
|
//获取展开按钮的宽度
|
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);
|
}
|
}
|
}
|