app/src/main/java/com/application/zhangshi_app_android/widget/Node.java
@@ -1,27 +1,27 @@
package com.application.zhangshi_app_android.widget;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
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.PopupWindow;
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.PopBasicInformationBinding;
import com.otaliastudios.zoom.ZoomLayout;
import com.application.zhangshi_app_android.databinding.ItemFamilyMemberBinding;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -38,7 +38,7 @@
    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); // 连线拐角 圆角半径
@@ -69,7 +69,6 @@
        if (member != null) {
            // 递归创建子节点
            if (member.getChildList() != null && member.getChildList().size() > 0) {
                // 所有可见的子节点需要的宽高总和
                for (HomeRootBean child : member.getChildList()) {
                    // 根据是否有配偶创建不同的节点
                    if (child != null) {
@@ -97,7 +96,7 @@
        if (this.parent!=null){
            throw new RuntimeException("只能初始化根节点");
        }
        //根据可见性找到最后一个第一子节点,即最上边的节点
        //根据可见性找到最后一个第一子节点,即最右上边的节点
        setViewPositionRecursion(getVisibleTopNode(), this,x,y);
    }
@@ -105,7 +104,7 @@
     * 递归设置所有节点的位置
     * @param topNode 顶部节点,这颗节点树的最上边的节点
     * @param rootNode 根节点,这颗节点树的根节点
     *                startNode 必须是 topNode 祖先节点
     *                rootNode 必须是 topNode 祖先节点
     * @param x 整颗节点树的x坐标
     * @param y 整颗节点树的y坐标
     */
@@ -117,12 +116,17 @@
        }
        //因为 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;
        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的位置,递归设置所有节点的位置
@@ -134,7 +138,7 @@
            for (Node child : parentNode.children) {
                if (child == node) {
                    centerYList.add(child.centerY);
                    currentBottom = currentBottom + node.getVisibleTopNode().top + node.getAllVisibleHeight();
                    currentBottom = currentBottom + child.getVisibleTop() + child.getAllVisibleHeight();
                    continue;
                }
                if (!child.isVisible){
@@ -149,7 +153,7 @@
            //取最大值、最小值 和 的中间
            int totalCenterY = 0;
            if (centerYList.size() > 0) {
            if (centerYList.size() > 1) {
                int min = centerYList.get(0);
                int max = centerYList.get(0);
                for (int i = 1; i < centerYList.size(); i++) {
@@ -162,6 +166,8 @@
                    }
                }
                totalCenterY = (min + max) / 2;
            }else{
                totalCenterY = centerYList.get(0);
            }
            if (parentNode.children.size() > 1) {
@@ -175,7 +181,21 @@
            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){
@@ -194,67 +214,56 @@
    // 设置对应类型节点的位置 由子类实现
    protected abstract void setViewPosition(int x, int centerY);
    // 设置分叉节点的位置 由子类实现,必须先setViewPosition才能调用
    protected abstract void setForkYList(List<Integer> yList);
    protected View createItemView(HomeRootBean bean) {
    /**
     *  创建节点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));
        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);
        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);
        // 设置节点内容
        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) {
                PopBasicInformationBinding mBinding= DataBindingUtil.inflate(inflater,R.layout.pop_basic_information,null,false);
                PopupWindow popupWindow = new PopupWindow();
                // 设置布局文件
                popupWindow.setContentView(mBinding.getRoot());
                // 为了避免部分机型不显示,需要重新设置一下宽高
                popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
                popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
                // 设置pop透明效果
                popupWindow.setBackgroundDrawable(new ColorDrawable(0x0000));
                // 设置pop出入动画
                popupWindow.setAnimationStyle(com.android.app_base.R.style.pop_operate);
                // 设置pop获取焦点,如果为false点击返回按钮会退出当前Activity,如果pop中有Editor的话,focusable必须要为true
                popupWindow.setFocusable(true);
                // 设置pop可点击,为false点击事件无效,默认为true
                popupWindow.setTouchable(true);
                // 设置点击pop外侧消失,默认为false;在focusable为true时点击外侧始终消失
                popupWindow.setOutsideTouchable(false);
                mBinding.setSelfBean(bean);
                mBinding.setSpouseBean(bean.getSpouse());
                ZoomLayout zoomLayout = (ZoomLayout) familyMemberView.getParent().getParent();
                popupWindow.setHeight(zoomLayout.getHeight());
                popupWindow.setWidth(zoomLayout.getWidth());
                popupWindow.showAtLocation((View) familyMemberView.getParent().getParent(), Gravity.BOTTOM,0,0);
                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(AppManager.getAppManager().currentActivity());
        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);
@@ -429,13 +438,15 @@
        }
        if (node.children.size() == 0){
            return node.height;
        }else {
        } else {
            int heightSum = 0;
            for (Node child : node.children) {
                if (getNodeTreeHeightRecursion(child,considerVisible)!=0){
                int childHeight = getNodeTreeHeightRecursion(child,considerVisible);
                //如果有多个子节点且子节点有高度,那么子节点之间还有竖直间隔
                if (node.children.size() > 1 && childHeight > 0){
                    heightSum += verticalMargin;
                }
                heightSum += getNodeTreeHeightRecursion(child,considerVisible);
                heightSum += childHeight;
            }
            //子节点每两个之间还有竖直间隔
            return Math.max(node.height,heightSum);
@@ -484,15 +495,11 @@
    }
    // 获取 每个 itemView 的宽度
    public int getItemViewWidth(){
        View itemView =  createItemView(null);
        return itemView.getMeasuredWidth();
    }
    public abstract int getItemViewWidth();
    // 获取 每个 itemView 的高度
    public int getItemViewHeight(){
        View itemView =  createItemView(null);
        return itemView.getMeasuredHeight();
    }
    public abstract int getItemViewHeight();
    //获取展开按钮的宽度
    public int getExpandIconWidth(){
        ImageView expandIconView = createExpandIconView();
@@ -503,4 +510,12 @@
    public int getViewWidth(){
        return viewBottom - viewTop;
    }
    public void setOnItemClickListener(HomeMindMapLayout.OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
        //所有的子节点也设置
        for (Node child : children) {
            child.setOnItemClickListener(onItemClickListener);
        }
    }
}