前言

最近在做一个项目,其中有一个页面是要做一个类似于个人分享关系图的效果。于是找了网上很多demo,借鉴的一些思路和方法,打算自己动手撸一个出来。

效果

demo

Github项目demo

整体思路

  1. 创建自定义的view继承framelayout捆绑布局relativelayout,放置一个绘制线的布局,和一个绘制头像的布局(用来add动态创建的iamgeview),这里默认给予3000*3000。
  2. 获取数据,根据degree分别筛选出不同层次的实体类list。
  3. 绘制第一层头像(即中心点):默认中心点的坐标为1500*1500,以及set4个边缘的坐标(以正方形考虑),并绘制头像。
  4. 绘制第二层头像:获取第二层数据,360度除以个数,计算出角度,根据角度,计算出中心点,并绘制头像。
  5. 绘制第三层头像:根据第二层的角度和坐标,计算出角度和中心点,并绘制头像。(重点需要头像去重)
  6. 根据坐标点画线,并设置长按和缩放事件。

所以在我的自定义view中包含2个view(设置了默认布局大小3000*3000)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!--线布局--><com.littletree.mypersonalconnectionmap.customview.LineImageViewandroid:id="@+id/lineImageView"android:layout_width="3000dp"android:layout_height="3000dp" /><!--圆形头像布局--><FrameLayoutandroid:id="@+id/layouPoints"android:layout_width="3000dp"android:layout_height="3000dp"android:layout_gravity="center_vertical" />
</RelativeLayout>
复制代码

常量的配置信息

FrameLayout layouPoints;  //用于添加头像的布局
private LineImageView lineImageView;  //画线以及中心头像的小圆(项目ui图的需要)private List<AtmanRelation> sourceList = new ArrayList<>(); //包含1,2,3层的实体类(接口返回的数据)
private ArrayList<PointLeo> points = new ArrayList<>();//用于连线的。
private ArrayList<RectPoint> rects = new ArrayList<>();//用于判断重叠的
private ArrayList<RelationBean> reList_2 = new ArrayList<>();//第二度关系的集合,目的是 为了绘制完一度后再绘制二度private int width_degress_one = PUtil.dip2px(getContext(), 85);  //第一层头像直径
private int width_degress_two = PUtil.dip2px(getContext(), 50);  //第二层头像直径
private int width_degress_three = PUtil.dip2px(getContext(), 30);   //第三层头像直径
private Context mcontext;private int mAngleNum = 120;   //第三层扩散的角度private int LastCenterX;  //理论中心点为1500dp处
private int LastCenterY;  //理论中心点为1500dp处
复制代码

绘制中心点的头像

ArrayList<AtmanRelation> myAtmanList = getDegree(0);  //getDegree()为获取第几层数据的方法
myAtmanList.get(0).setY_center(LastCenterX);
myAtmanList.get(0).setX_center(LastCenterY);
myAtmanList.get(0).setRectPoint(new RectPoint(LastCenterX - width_degress_one / 2, LastCenterY - width_degress_one / 2, LastCenterX + width_degress_one / 2, LastCenterY + width_degress_one / 2));
showAvatar(LastCenterX - width_degress_one / 2, LastCenterY- width_degress_one / 2, myAtmanList.get(0)); //showAvatar()为绘制头像
复制代码

绘制第二层

根据第一层(中心点)坐标,以及第二层的个数,用360度分割,根据所得的角度计算出第二层各自的坐标,获取坐标还得判断是否位置重复,如果位置被占用就增加一个固定长度重复判断,直至成功。再用该点去计算第三层的数据。

int circle = width_degress_one;//中心头像的直径
ArrayList<AtmanRelation> oneAtmanList = getDegree(1);
int number = oneAtmanList.size();//当前第二层的数量
int x_center = LastCenterX;
int y_center = LastCenterY;
for (int i = 0; i < oneAtmanList.size(); i++) {AtmanRelation itemBean = oneAtmanList.get(i);double jiaod = 360 / ((double) oneAtmanList.size());   //计算出每个头像间隔的角度double currentJD = 0 + jiaod * (i + 1);   //每个头像的角度int X1 = 0;int Y1 = 0;//根据角度和上一层中心点的坐标,计算出自己的坐标if (currentJD != 0 && currentJD != 180 && currentJD != 360) {X1 = x_center + (int) (circle * (Math.cos(Math.PI * currentJD / 180)));Y1 = y_center + (int) (circle * (Math.sin(Math.PI * currentJD / 180)));} else {if (currentJD == 0 || currentJD == 360) {X1 = x_center + circle;Y1 = y_center;} else {Y1 = y_center;X1 = x_center - circle;}}int trueX1 = 0;int trueY1 = 0;//当前点的所在区域RectPoint leoPoint;trueX1 = X1 - width_degress_two / 2;trueY1 = Y1 - width_degress_two / 2;leoPoint = new RectPoint(trueX1, trueY1, trueX1 + width_degress_two, trueY1 + width_degress_two); //范围的4个坐标if (checkHaveRect(leoPoint)) {//检查是否有重叠,如有重复,要增加一个固定的长度再次循环,直到不再重复repetAdd(x_center, y_center, circle, currentJD, 1, itemBean);} else {//false 没有重叠,则直接添加showAvatar(trueX1, trueY1, itemBean);//point记录数据,point最后用于连线points.add(new PointLeo(x_center, y_center, X1, Y1, itemBean.getType(), itemBean.getDegree(), true));// 把当前的中心点坐标  和当前点的所在区域存储带rects中,到时候要用于判断是否位置重复itemBean.setX_center(X1);itemBean.setY_center(Y1);//这边增加一点范围,因为ui需要,布局并不只有头像,还有名称之类,可自行调节itemBean.setRectPoint(new RectPoint(trueX1 - PUtil.dip2px(getContext(), 16), trueY1 - PUtil.dip2px(getContext(), 16),trueX1 + PUtil.dip2px(getContext(), 16), trueY1 + PUtil.dip2px(getContext(), 16)));rects.add(leoPoint);/*** 这里是添加成功关系以后,进行的下一层 关系的绘制* */ArrayList<AtmanRelation> sonList = getsonList(itemBean);//获取该点下一层的数据if (sonList.size() > 0) {int numberNex = sonList.size();//下一层关系有几个。int circle2 = width_degress_three;//存储到relist_2中reList_2.add(new RelationBean(circle2, numberNex, X1, Y1, currentJD, 2, sonList)); }}if (i == number - 1) {if (reList_2.size() > 0) {for (int j = 0; j < reList_2.size(); j++) {RelationBean relationBean = reList_2.get(j);if (relationBean.getNumberNex() > 0) {//绘制第三层addNexPoint(relationBean.getCircle(), relationBean.getNumberNex(), relationBean.getX1(), relationBean.getY1(), relationBean.getTrueJD(), relationBean.getRelation(), relationBean.getAtmanRelations(), j);}}}}}
复制代码

绘制第三层

该方法与绘制第二层的区别在于获取角度,是在原角度的基础上减去扩散角度的一半。 double trueJD = nowJD % 360 - mAngleNum/2; 如果设置第四层,第五层,可以继续沿用该方法

public void addNexPoint(int circleNex, double numberNex, int x_centerNex, int y_centerNex, double nowJD, int relation, ArrayList<AtmanRelation> atmanRelations, int index) {int circle = circleNex;//半径的长度double number = numberNex + 1;//当前关系个数。当然要加上父类那条线int x_center = x_centerNex;//目前坐标中心点int y_center = y_centerNex;//目前坐标重新点double trueJD = nowJD % 360 - mAngleNum/2;for (int i = 0; i < numberNex; i++) {AtmanRelation itemBean = atmanRelations.get(i);//相隔的角度就是double jiaod = mAngleNum / number;double currentJD = trueJD + jiaod * (i + 1);int X1 = 0;int Y1 = 0;if (currentJD != 0 && currentJD != 180 && currentJD != 360) {//别面有错误出现X1 = x_center + (int) (circle * (Math.cos(Math.PI * currentJD / 180)));Y1 = y_center + (int) (circle * (Math.sin(Math.PI * currentJD / 180)));} else {if (currentJD == 0 || currentJD == 360) {X1 = x_center + circle;Y1 = y_center;} else {Y1 = y_center;X1 = x_center - circle;}}int trueX1 = 0;int trueY1 = 0;//当前点的所在区域则是RectPoint leoPoint;if (0 == itemBean.getDegree()) {trueX1 = X1 - width_degress_one / 2;//减去自身控件的长度trueY1 = Y1 - width_degress_one / 2;//减去自身控件的长度leoPoint = new RectPoint(trueX1 - width_degress_one, trueY1 - width_degress_one,trueX1 + width_degress_one, trueY1 + width_degress_one);} else if (1 == itemBean.getDegree()) {trueX1 = X1 - width_degress_two / 2;//减去自身控件的长度trueY1 = Y1 - width_degress_two / 2;//减去自身控件的长度leoPoint = new RectPoint(trueX1, trueY1,trueX1 + width_degress_two, trueY1 + width_degress_two);} else {trueX1 = X1 - width_degress_three / 2;//减去自身控件的长度trueY1 = Y1 - width_degress_three / 2;//减去自身控件的长度leoPoint = new RectPoint(trueX1, trueY1,trueX1 + width_degress_three, trueY1 + width_degress_three + PUtil.dip2px(getContext(), 20));}if (checkHaveRect(leoPoint)) {//检查是否有重叠,true为有重叠。要增加半径的长度repetAdd(x_center, y_center, circle, currentJD, relation, itemBean);} else {//false 没有重叠,则直接添加showAvatar(trueX1, trueY1, itemBean);points.add(new PointLeo(x_center, y_center, X1, Y1, itemBean.getType(), itemBean.getDegree(), true));itemBean.setX_center(X1);itemBean.setY_center(Y1);//这边增加比上一层略少的范围,越往外圆越小itemBean.setRectPoint(new RectPoint(trueX1 - PUtil.dip2px(getContext(), 8), trueY1 - PUtil.dip2px(getContext(), 8),trueX1 + PUtil.dip2px(getContext(), 8), trueY1 + PUtil.dip2px(getContext(), 8)));rects.add(leoPoint);}}}
复制代码

检查是否重复方法

只要循环判断是否在范围内即可。

public boolean checkHaveRect(RectPoint rectPoint) {for (int i = 0; i < rects.size(); i++) {RectPoint currenRect = rects.get(i);if (rectPoint.getLeft_x() > currenRect.getRigth_x()|| rectPoint.getLeft_top_y() > currenRect.getRight_bottom_y()|| rectPoint.getRight_bottom_y() < currenRect.getLeft_top_y()|| rectPoint.getRigth_x() < currenRect.getLeft_x()) {} else {return true;//只要不符合上面条件  则有重叠}}return false;//没有包含在内
}
复制代码

添加头像方法

如果只要头像可用imageview代替,这边需要添加背景和文字,我使用了supertextview

SuperTextView imageView_bottom_yy = new SuperTextView(getContext());
imageView_bottom_yy.setUrlImage(itemBean.getVisitorAvatar());
LayoutParams leoParams_bottom_yy = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layouPoints.addView(imageView_bottom_yy, leoParams_bottom_yy);
layouPoints.addView(textview_name, Params_textview_name);
复制代码

画线布局

在所有点绘制好后,传入存储的点坐标,即可绘制

public class LineImageView extends View {//testprivate Paint mPaint;private Paint PointPaint;  //8个点private ArrayList<PointLeo> points;public LineImageView(Context context) {this(context, null);}public LineImageView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public LineImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(getResources().getColor(R.color.relationship_line));mPaint.setStyle(Paint.Style.STROKE);//设置填充样式mPaint.setStrokeWidth(1);//设置画笔宽度PointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);PointPaint.setColor(getResources().getColor(R.color.relationship_line));PointPaint.setStyle(Paint.Style.STROKE);//设置填充样式PointPaint.setStrokeWidth(PUtil.dip2px(getContext(), 5));//设置画笔宽度}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (points != null) {for (int i = 0; i < points.size(); i++) {PointLeo pointLeo = points.get(i);PathEffect pathEffect = new DashPathEffect(new float[]{10, 10}, 2);mPaint.setPathEffect(pathEffect);Path linePath = new Path();linePath.moveTo(pointLeo.getX(), pointLeo.getY());linePath.lineTo(pointLeo.getX1(), pointLeo.getY1());canvas.drawPath(linePath, mPaint);}}}public void setLines(ArrayList<PointLeo> points) {this.points = points;invalidate();}
}
复制代码

添加缩放和长按手势

因为需要上下左右滑动,在我的自定义view外我套了一层HVScrollView 然后在HVScrollView上添加了缩放和长按手势。

//缩放手势
public class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {@Overridepublic boolean onScale(ScaleGestureDetector detector) {float previousSpan = detector.getPreviousSpan();float currentSpan = detector.getCurrentSpan();if (currentSpan < previousSpan) {// 缩小scale = preScale - (previousSpan - currentSpan) / 1000;if (scale < 0.2f) {scale = 0.2f;}} else {// 放大scale = preScale + (currentSpan - previousSpan) / 1000;if (scale > 1.0f) {scale = 1.0f;}}// 缩放viewshaderImage.setScaleX(scale);shaderImage.setScaleY(scale);return false;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {preScale = scale;//记录本次缩放比例}
}//长按手势
public class LongPressGestureListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic void onLongPress(MotionEvent e) {//保存图片dialogdialog_twobutton_notitle.show();}
}
复制代码

添加到hvscrollview中

mScaleGestureDetector = new ScaleGestureDetector(PersonalConnectionMapActivity.this,new ScaleGestureListener());mLongPressGestureListener = new GestureDetector(PersonalConnectionMapActivity.this,new LongPressGestureListener());```
hvScrollView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {if (moreFinger) {mScaleGestureDetector.onTouchEvent(motionEvent);}mLongPressGestureListener.onTouchEvent(motionEvent);switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN://第一个手指按下事件break;case MotionEvent.ACTION_POINTER_DOWN://第二个手指按下事件moreFinger = true;break;case MotionEvent.ACTION_UP:moreFinger = false;mhandler.sendEmptyMessage(12);//手指放开来个回弹效果break;}return moreFinger;}
});
```
复制代码

在使用过程中遇到的问题

如果数据量过小,设置的滑动布局却为3000*3000,空白占比太大,保存的图片也有很多留白。

如果数据量过大,数据不会显示全。

解决办法

事先需要根据数据计算出最大宽高设置给hvscrollview,然后开始绘制。 计算的过程和绘制过程基本相同,就不再贴出。

hvScrollView.post(new Runnable() {@Overridepublic void run() {shaderImage.setVisibility(View.GONE);shaderImage.setmAngleNum(mAngleNum);shaderImage.setmDiameter(mDiameter_one,mDiameter_two,mDiameter_three);shaderImage.getmaxXYRange(atmanRelationList);shaderImage.changeViewRangle(hvScrollView.getWidth(),hvScrollView.getHeight());shaderImage.setSourceList(atmanRelationList);tryScale(scale, 0.9f);}
});
复制代码

结尾

效果基本是实现了,不过还是有问题存在,如果数据量过大,容易造成内存溢出。(虽然我这边项目数据不大) 本人也试过用view的ondraw在绘制显示,从而解决内存溢出的问题,不过效果不太理想,并且项目需求是需要整体保存图片

作者:littletree_风中追风
链接:https://juejin.cn/post/7123133679274033189
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

实现人物关系图谱,人物影响力图相关推荐

  1. PersonGraphDataSet近十万的开放人物关系图谱项目

    PersonGraphDataSet PersonGraphDataSet, nearly 10 thousand person2person relationship facts that buil ...

  2. Python + Neo4j(安装)可视化分析漫威十年人物关系图谱

    目录 1 数据爬取 1.1 漫威人物关系图谱网站 1.2 爬取人物关系数据 2 Neo4j的安装及服务启动 2.1 Neo4j下载安装 2.2 开启Neo4j服务 3 数据准备 3.1 加入列名 3. ...

  3. BERT辅助金融领域人物关系图谱构建

    摘要 现有的人员简历信息抽取方法无法针对金融公告中非结构化人员简历进行人员属性以及事件的抽取,无法发现金融公告中跨文档的人员之间关系.针对以上问题,将非结构化的人员简历抽取成结构化的人员信息模板,提出 ...

  4. 在图数据库Neo4j中创建红楼梦人物关系图谱

    在图数据库Neo4j中创建红楼梦人物关系图谱 1.加载csv数据文件 load csv from 'file:///triples.csv' as linecreate (:role {name:li ...

  5. [转载]红楼梦四大家族人物关系图谱(12系列图表)_RWERWERWE_96921_新浪博客

    原文地址:红楼梦四大家族人物关系图谱(12系列图表) 作者: 静心聆听62 红楼梦主要人物关系 01红楼梦四大家族人物关系图谱 02红楼梦人物关系简图 03红楼梦人物关系详图 04红楼梦四大家族奴隶图 ...

  6. Python来袭,教你用Neo4j构建“复联4”人物关系图谱!没有剧透!【文末赠书】...

    讲技术之前先引个好玩的热点话题,带你与时代接轨~ 漫威系列电影<复仇者联盟4>上映 你去看了吗? 小编就盼着周末去好好享受 谁剧透,跟谁急! 复仇者联盟 之 绝对不剧透 漫威英雄们为了不让 ...

  7. 知识图谱初探(二)三体人物关系图谱构建

    <三体>电视剧正在热播,忽然觉得如果用知识图谱来构建下其中的人物关系,想必应该有趣! 那我们就开始吧! 首先列一下其中的相关人物 叶文洁 叶文雪 沙瑞山 杨冬 杨卫宁 白沐霖 邵琳 叶哲泰 ...

  8. D3.js实现人物关系图谱有移动、拖拽、放大功能

    原文链接:https://www.jianshu.com/p/d768505cab78 在此基础上加了点东西 实现了关系图的移动.拖拽.放大功能.以及数据的分类等等.代码稍显繁琐,另外案例中的d3.j ...

  9. 采集+图谱可视化|手把手教你采集明星人物关系并进行图谱展示

    大家好,我是阿辰~ 今天教大家获取采集(某度百科)的明星人物关系数据,并进行图谱可视化展示. 亮点(难点): 1.动态查询(输入任意明星名字就可以查询该明星关系人物). 2.图谱展示(以及key-va ...

最新文章

  1. Java开发最常犯的10个错误,打死都不要犯!
  2. Tomcat部署web应用的三种方式
  3. 掌门教育微服务体系 Solar 第 3 弹:Nacos 企业级落地下篇
  4. [Android]用架构师角度看插件化(3)-Replugin 需要占坑跳转?
  5. Kindeditor放置两个调用readonly错误
  6. LeetCode—207. 课程表
  7. Docker CE/EE 原生支持Kubernetes
  8. SQL Sever 刪除重複數據只剩一條
  9. 网络计算机热词,2017年的首个网络热词就这样被刷屏了!
  10. html 设置font size,css font-size属性说明
  11. CentOS 7 GNOME经典桌面 拼音输入法设置
  12. 光纤传输设备如何选择?光纤网络的优缺点分析
  13. top在linux的命令,Linux命令详解之–top命令 | Linux大学
  14. Pdf 转 word 和 word 转 pdf 等
  15. android多国语言---简写对应国家code
  16. python爬京东延迟加载_python大规模爬取京东
  17. wikioi 1219 骑士游历
  18. python 添加半透明水印_怎么利用python给图片添加半透明水印
  19. Java EE入门教程系列第三章JavaBean(二)——JavaBean在JSP中的应用
  20. 20175202 葛旭阳 MyCP

热门文章

  1. Rust轻量级I/O库mio
  2. iTOP-4412开发板-10.1寸屏幕硬件修改支持排线和HDMI
  3. “墨子号”卫星已上天:未来每部手机都会由量子芯片加密
  4. 倒车雷达的缺陷以及可能的改良方法
  5. 在线查询12306账号是否泄露
  6. OA系统开发人事模块关于请假跨月的处理
  7. appium java 虫师_appium新手入门(9)—— appium API 之应用操作
  8. h264播放工具-VLC
  9. java程序设计任务驱动式教程_java程序设计任务驱动式教程保持良好的交流答案...
  10. flickr php,php – 获取Flickr集