package com.application.zhangshi_app_android.widget;
|
|
import android.content.Context;
|
import android.graphics.Canvas;
|
import android.graphics.Color;
|
import android.graphics.Paint;
|
import android.graphics.Path;
|
import android.util.AttributeSet;
|
import android.view.LayoutInflater;
|
import android.view.View;
|
import android.widget.FrameLayout;
|
import android.widget.ImageView;
|
import android.widget.TextView;
|
|
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
import androidx.constraintlayout.utils.widget.ImageFilterView;
|
|
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.HashMap;
|
import java.util.Map;
|
|
/**
|
* @author Ljj
|
* @date 2023.08.12. 1:19
|
* @desc 家族导图 自定义布局
|
*/
|
public class HomeMindMapLayout extends FrameLayout {
|
boolean isAutoLayout = true;// 是否自动布局
|
private Paint linePaint;
|
private HomeRootBean rootMember;// 根节点成员
|
private Node rootNode;// 根节点
|
private Map<HomeRootBean, View> viewMap = new HashMap<>();// 存放所有节点的view,记录节点的view是否已被addView
|
|
private int width, height;// 当前布局的宽高
|
private int minHeight, minWidth;// 当前布局的最小宽高
|
private int padding = 100;//内容矩形 与 FrameLayout矩形 的padding
|
private static final int spouseMargin = ScreenSizeUtils.dip2px(15);// 配偶两个view之间的间隔
|
private final int verticalMargin = ScreenSizeUtils.dip2px(20);// //节点竖直方向间距
|
|
private static final int pathLength = ScreenSizeUtils.dip2px(20);// 线条长度
|
|
public HomeMindMapLayout(@NonNull Context context) {
|
this(context, null);
|
}
|
|
public HomeMindMapLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
|
super(context, attrs);
|
init();
|
}
|
|
private void init() {
|
linePaint = new Paint();
|
linePaint.setColor(Color.parseColor("#FFEBA4AA"));
|
linePaint.setStrokeWidth(ScreenSizeUtils.dip2px(2f));
|
linePaint.setStyle(Paint.Style.STROKE);
|
linePaint.setStrokeJoin(Paint.Join.ROUND);
|
linePaint.setStrokeMiter(10);
|
linePaint.setAntiAlias(true);
|
|
// 设置ViewGroup默认调用onDraw方法
|
setWillNotDraw(false);
|
}
|
|
public void setRootMember(HomeRootBean root) {
|
//等待布局加载完成后再设置数据
|
post(new Runnable() {
|
@Override
|
public void run() {
|
setRootMemberReal(root);
|
}
|
});
|
}
|
|
private void setRootMemberReal(HomeRootBean root) {
|
this.rootMember = root;
|
// 清空viewMap
|
viewMap.clear();
|
if (rootMember != null) { // 绘制根节点,在竖直方向上居中
|
if (rootMember.getSpouse() == null){
|
rootNode = new SimpleNode(getContext(),rootMember);
|
}else {
|
rootNode = new DoubleNode(getContext(),rootMember);
|
}
|
rootNode.setPosition(padding,padding);
|
//把所有节点的view添加到viewGroup中
|
addNodeView(rootNode);
|
requestLayout();
|
}
|
}
|
|
private void addNodeView(Node rootNode) {
|
for (View view : rootNode.viewList) {
|
addView(view);
|
}
|
if (rootNode.getChildren() != null && rootNode.getChildren().size() > 0) {
|
for (Node child : rootNode.getChildren()) {
|
addNodeView(child);
|
}
|
}
|
}
|
|
@Override
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
super.onLayout(changed, left, top, right, bottom);
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
if(rootNode != null){
|
// 是否自适应布局
|
if (isAutoLayout) {
|
//需要随着内容的显示内容动态显示view
|
rootNode.setPosition(padding, padding);
|
width = rootNode.getAllVisibleWidth() + padding * 2;
|
height = rootNode.getAllVisibleHeight() + padding * 2;
|
} else {
|
//不需要随着内容的显示内容动态显示view
|
width = rootNode.getAllWidth() + padding * 2;
|
height = rootNode.getAllHeight() + padding * 2;
|
}
|
}
|
setMeasuredDimension(Math.max(minWidth,width),Math.max(minHeight,height));
|
|
}
|
|
@Override
|
protected void onDraw(Canvas canvas) {
|
super.onDraw(canvas);
|
if (rootNode != null) {
|
drawNodeLine(canvas, rootNode);
|
}
|
}
|
|
private void drawNodeLine(Canvas canvas, Node rootNode) {
|
//根据根节点的可见性判断是否绘制线条
|
if (rootNode.isVisible()){
|
if (rootNode.nextPathList != null) {
|
for (Path path : rootNode.nextPathList) {
|
canvas.drawPath(path, linePaint);
|
}
|
}
|
if (rootNode.prePathList != null) {
|
for (Path path : rootNode.prePathList) {
|
canvas.drawPath(path, linePaint);
|
}
|
}
|
if (rootNode.isExpand){
|
if (rootNode.forkPathList != null) {
|
for (Path path : rootNode.forkPathList) {
|
canvas.drawPath(path, linePaint);
|
}
|
}
|
}
|
}
|
|
if (rootNode.getChildren() != null && rootNode.getChildren().size() > 0) {
|
for (Node child : rootNode.getChildren()) {
|
drawNodeLine(canvas, child);
|
}
|
}
|
|
}
|
|
private ImageView createExpandIconView() {
|
ImageView expandView = new ImageView(getContext());
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(pathLength,pathLength);
|
expandView.setLayoutParams(params);
|
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(pathLength, MeasureSpec.EXACTLY);
|
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(pathLength, MeasureSpec.EXACTLY);
|
expandView.measure(widthMeasureSpec,heightMeasureSpec);
|
return expandView;
|
}
|
|
private View createItemView(HomeRootBean member) {
|
// 使用布局填充器加载节点布局
|
LayoutInflater inflater = LayoutInflater.from(getContext());
|
View familyMemberView = inflater.inflate(R.layout.item_family_member, null, false);
|
|
familyMemberView.measure(MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST));
|
// 获取测量后的宽高
|
int width = familyMemberView.getMeasuredWidth();
|
int height = familyMemberView.getMeasuredHeight();
|
|
// 添加到父布局前再次测量
|
familyMemberView.measure(
|
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
|
);
|
|
if (member == 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(member.getIdentity()));
|
if (member.getImg() != null) {
|
GlideUtil.loadImage(member.getImg(), iv_avatar);
|
}
|
tv_name.setText(member.getNickName());
|
|
return familyMemberView;
|
}
|
|
public void setMinSize(int minWidth, int minHeight) {
|
this.minWidth = minWidth;
|
this.minHeight = minHeight;
|
}
|
|
|
|
|
}
|