| | |
| | | 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; |
| | |
| | | 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); // 连线拐角 圆角半径 |
| | | |
| | |
| | | if (member != null) { |
| | | // 递归创建子节点 |
| | | if (member.getChildList() != null && member.getChildList().size() > 0) { |
| | | // 所有可见的子节点需要的宽高总和 |
| | | for (HomeRootBean child : member.getChildList()) { |
| | | // 根据是否有配偶创建不同的节点 |
| | | if (child != null) { |
| | |
| | | if (this.parent!=null){ |
| | | throw new RuntimeException("只能初始化根节点"); |
| | | } |
| | | //根据可见性找到最后一个第一子节点,即最上边的节点 |
| | | //根据可见性找到最后一个第一子节点,即最右上边的节点 |
| | | setViewPositionRecursion(getVisibleTopNode(), this,x,y); |
| | | } |
| | | |
| | |
| | | * 递归设置所有节点的位置 |
| | | * @param topNode 顶部节点,这颗节点树的最上边的节点 |
| | | * @param rootNode 根节点,这颗节点树的根节点 |
| | | * startNode 必须是 topNode 祖先节点 |
| | | * rootNode 必须是 topNode 祖先节点 |
| | | * @param x 整颗节点树的x坐标 |
| | | * @param y 整颗节点树的y坐标 |
| | | */ |
| | |
| | | } |
| | | |
| | | //因为 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的位置,递归设置所有节点的位置 |
| | |
| | | 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){ |
| | |
| | | |
| | | //取最大值、最小值 和 的中间 |
| | | 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++) { |
| | |
| | | } |
| | | } |
| | | totalCenterY = (min + max) / 2; |
| | | }else{ |
| | | totalCenterY = centerYList.get(0); |
| | | } |
| | | |
| | | if (parentNode.children.size() > 1) { |
| | |
| | | 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){ |
| | |
| | | |
| | | // 设置对应类型节点的位置 由子类实现 |
| | | 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); |
| | |
| | | } |
| | | 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); |
| | |
| | | } |
| | | |
| | | // 获取 每个 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(); |
| | |
| | | public int getViewWidth(){ |
| | | return viewBottom - viewTop; |
| | | } |
| | | |
| | | public void setOnItemClickListener(HomeMindMapLayout.OnItemClickListener onItemClickListener) { |
| | | this.onItemClickListener = onItemClickListener; |
| | | //所有的子节点也设置 |
| | | for (Node child : children) { |
| | | child.setOnItemClickListener(onItemClickListener); |
| | | } |
| | | } |
| | | } |