# OrgMapView
**Repository Path**: kavin_tian/OrgMapView
## Basic Information
- **Project Name**: OrgMapView
- **Description**: OrgMapView组织架构图
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-04-01
- **Last Updated**: 2023-11-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 组织架构图
摘要:近期项目中需要实现组织架构图,搜索了大半天网上也没有体验良好、功能能够满足项目需要的demo,无奈只能自己写。实现思路是通过自定义view的方式,通过计算每一项的开始坐标来在canvas上定位,通过onTouchEvent监听手势的移动和缩放在进行画布的缩放和位移,实现项目需求。
## 需求
1. **支持横竖屏切换**;
2. **支持选中状态切换** ;
3. item项 **字体颜色**、**背景颜色**、**选中颜色**、**字体大小**、**横向间距**、**纵向间距**(不考虑连线)自定义;
4. 每**行的高度**取决于改行中的内容最大值,内容居中显示;
5. 支持**手势**滑动、缩放;
6. **缩放**比例最大值,最小值;
## 设计稿如下

## 实现思路
首先数据源必须是树形结构,数据源只有一个跟结点,每个节点都可能有多个子节点,以此类推。
- 对数据源进行遍历,计算出最大行数和最大列数,目的是计算出所画布局的高drawHeight宽drawWidth
- 遍历解析出每个节点所在的行数和列数,方便计算节点的起始位置sX sY,这样就可以根据该行的行高直接画出给item项
- 第一种情况,如果显示内容的高宽都比view可视范围高宽小的话滑动是不考虑滑动和缩放
- 第二种情况,如果高宽其中有一项的值大于view可视范围的实际高宽,则计算出能全部看到范围的最小缩放比例,用于手动缩放时无限缩小的限制
- 左上角是原点(0,0),画的起点也是从原点开始,所以默认进来如果是上面第二种情况 则需要将画布canvas移动到中间位置,所以translateX translateY 至关重要
- 点击事件在onTouchEvent中实现,获取点击屏幕的x、y坐标遍历数据源跟所有item项所占区域坐标进行比较,判断是否是该项的选中,通过接口回调传递事件
## 实现步骤
## 属性定义
```java
```
## 数据源解析
采用递归的方式计算出总行数、总列数、所有叶子结点坐在的列数
```java
/**
* 递归解析数据
* a,计算出总列数和总行数
* b,所有叶子结点项设置所在列的值
*
* @param data 数据源
*/
private void paraseDataLineAndLastNodeRow(MorgDataBean data) {
if (data == null || data.getChilds() == null || data.getChilds().size() == 0) {
return;
}
for (MorgDataBean d : data.getChilds()) {
/*自己所处的行数=父节点所在的行数+1
从上到下的顺序*/
d.setCurrentLine(data.getCurrentLine() + 1);
/*总行数也是根据行数的增加而增加的*/
if (data.getCurrentLine() + 1 > totalLineCount) {
totalLineCount = data.getCurrentLine() + 1;
}
/*所有分支的叶子结点数 即该叶子结点项所在的列数*/
if (d.getChilds() == null || d.getChilds().size() == 0) {
/*列数+1*/
totalRowCount++;
d.setCurrentRow(totalRowCount);
}
paraseDataLineAndLastNodeRow(d);
}
}
```
倒叙遍历该节点的父节点坐在的列数
```java
/*倒序遍历*/
for (int i = totalLineCount; i > 0; i--) {
paraseDataNodeRow(i, data);
}
```
```java
/**
* 目的修改 该级父对象即data 的所在列数
*
* @param level 遍历的级数 需要倒数遍历 从大到小的顺序
* @param data 数据源
*/
private void paraseDataNodeRow(int level, MorgDataBean data) {
if (data == null) {
return;
}
if (data.getChilds() == null) {
return;
}
for (MorgDataBean d : data.getChilds()) {
if (d.getCurrentLine() == level && data.getCurrentRow() == 0) {
data.setCurrentRow((d.getCurrentRow() + data.getChilds().get(data.getChilds().size() - 1).getCurrentRow()) / 2.0f);
} else {
paraseDataNodeRow(level, d);
}
}
}
```
计算每行最大的内容长度,用来确定行最高值
```java
/**
* 计算每行line 的最大内容长度
*/
private void paraseDataContentMaxLength4Line(MorgDataBean data) {
if (data == null) {
return;
}
for (MorgDataBean d : data.getChilds()) {
if (!isEmpty(d.getOrgname()) && contentMaxLength4line.length > d.getCurrentLine()) {
contentMaxLength4line[d.getCurrentLine()] = Math.max(contentMaxLength4line[d.getCurrentLine()], d.getOrgname().trim().length());
contentMaxHeigth4line[d.getCurrentLine()] = getTotalContentHeight(contentMaxLength4line[d.getCurrentLine()]);
}
if (d.getChilds() != null && d.getChilds().size() > 0) {
paraseDataContentMaxLength4Line(d);
}
}
}
```
实现效果如下:

# 依赖
```
dependencies {
implementation 'com.gitee.kavin_tian:OrgMapView:1.2.0'
}
```
# 使用
```
```
```
public class MainActivity extends AppCompatActivity {
OrgMapView orgMapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
orgMapView = findViewById(R.id.orgMapView);
/*设置数据源*/
/* String val = orgMapView.getOriginalFundData(this);
String json = "";
Gson gson = new Gson();
MorgDataBean data = gson.fromJson(val, MorgDataBean.class);*/
Gson gson = new Gson();
MorgDataBean data = gson.fromJson(getJSON(), MorgDataBean.class);
data.setOrgnameShow(data.getOrgname());
orgMapView.setData(data);
}
public String getJSON() {
String json = "{" +
" 'childs': [" +
" {" +
" 'childs': [" +
" {" +
" 'childs': []," +
" 'cid': 11," +
" 'cuuid': 23," +
" 'orgname': '施工公司'" +
" }," +
" {" +
" 'childs': []," +
" 'cid': 11," +
" 'cuuid': 24," +
" 'orgname': '后勤保障运输公司'" +
" }" +
" ]," +
" 'cid': 1," +
" 'cuuid': 11," +
" 'orgname': '京油二十九公司'" +
" }," +
" {" +
" 'childs': []," +
" 'cid': 1," +
" 'cuuid': 12," +
" 'orgname': '京油二十八公司你懂得'" +
" }" +
" ]," +
" 'cid': 0," +
" 'cuuid': 1," +
" 'orgname': '中国石油总公司'" +
"}";
return json;
}
}
```
存在问题:点击事件的时间间隔没有处理的很好,希望小伙伴能够给予好的意见和方案,欢迎评论转载