1、先上一个效果图:
2、最近做了一个类似企查查的关系图,要求是绘制人物节点,节点之间关系标示,布局可缩放,单个节点可拖动。需求背景交代完毕。
3、简单说一下思路,自定义View ,继承SurfaceView,这种设计不会影响主线程的操作,surfaceView拥有独立的Cavans,绘制效率高,动画效果流畅。
4、开始上代码
第一步,画圆,这个简单,需要的是外部传入节点的信息,xy位置及半径大小。这个不难,需要注意的是节点内部的文字需要做调整,保证文字在圆心位置,至少看起来是。
//关系图 圆形
private void drawCircle(Canvas canvas, int radius, int positionX, int positionY, int circleColor, int fillColor, String text, int textColor, int textSize) {
mPaint.setColor(circleColor);
canvas.drawCircle(positionX, positionY, radius, mPaint);
mPaint.setColor(fillColor);
canvas.drawCircle(positionX, positionY, radius - 1, mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
positionY += 5;
if (text.length() > 0 && text.length() <= 5) {
canvas.drawText(text, positionX, positionY, mPaint);
} else if (text.length() > 5 && text.length() <= 10) {
canvas.drawText(text.substring(0, 3), positionX, positionY - 25, mPaint);
canvas.drawText(text.substring(3, text.length()), positionX, positionY, mPaint);
} else if (text.length() > 10 && text.length() <= 15) {
canvas.drawText(text.substring(0, 3), positionX, positionY - 25, mPaint);
canvas.drawText(text.substring(3, 8), positionX, positionY, mPaint);
canvas.drawText(text.substring(8, text.length()), positionX, positionY + 25, mPaint);
} else if (text.length() > 15) {
canvas.drawText(text.substring(0, 3), positionX, positionY - 50, mPaint);
canvas.drawText(text.substring(3, 8), positionX, positionY - 25, mPaint);
canvas.drawText(text.substring(8, 14), positionX, positionY, mPaint);
canvas.drawText(text.substring(14, text.length()), positionX, positionY + 25, mPaint);
}
canvas.save();
mPaint.reset();
}
第二步,画节点与节点间的连线,这里有个问题就是,知道两个圆心的位置,节点半径,但是需要计算连线与节点圆的交点位置。
private void caculate(int startX, int startY, int r1, int endX, int endY, int r2, String text, int textColor, int lineColor, int textSize) {
double h = Math.abs(endY - startY);
double w = Math.abs(endX - startX);
double degree = Math.toDegrees(Math.atan(w / h));
// Log.d(TAG, "CACULATE -- degree " + degree + " " + (r1 * Math.sin(Math.PI / 180 * 30)));
int x11 = 0, y11 = 0, x22 = 0, y22 = 0;
if (startX > endX) {
x11 = (int) (startX - (r1 * Math.sin(Math.PI / 180 * degree)));
x22 = (int) (endX + (r2 * Math.sin(Math.PI / 180 * degree)));
} else {
x11 = (int) (startX + (r1 * Math.sin(Math.PI / 180 * degree)));
x22 = (int) (endX - (r2 * Math.sin(Math.PI / 180 * degree)));
}
if (startY < endY) {
y11 = (int) (startY + (r1 * Math.cos(Math.PI / 180 * degree)));
y22 = (int) (endY - (r2 * Math.cos(Math.PI / 180 * degree)));
} else {
y11 = (int) (startY - (r1 * Math.cos(Math.PI / 180 * degree)));
y22 = (int) (endY + (r2 * Math.cos(Math.PI / 180 * degree)));
}
drawAL(canvas, x11, y11, x22 - 3, y22 - 3, text, textColor, lineColor, textSize);
}
我给画个草图,帮助理解 ,这就是三角形的部分知识,不懂的同学问问初中的表弟,Math类包含很多计算方法,思路就是求出一个角度,算出两圆心连线与各节点的交点位置,这里看看代码,不再赘述。
第三步,画箭头,这个不难,可以去百度找找,很多类似代码,根据需求改吧改吧。晚点我会上这个View的整体代码,这里就不贴了。
到这里,基本的绘制工作完成,不难,接下来确定三个方法,
1、画布随手指拖动
这个问题处理的方法是,确定好一个点,比如我选择屏幕中心点,作为你的基准点,在你拖动的时候,根据手指拖动的距离去改变这个点坐标,传入绘制操作里是,画布就动起来了。
class DrawThread extends Thread {
@Override
public void run() {
while (flag) {
long start = System.currentTimeMillis();
drawItems();
long end = System.currentTimeMillis();
try {
if (end - start < 50) {
Thread.sleep(50 - (end - start));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void drawItems() {
try {
canvas = mHolder.lockCanvas();
if (canvas != null) {
canvas.drawColor(bacColor);
canvas.save();
canvas.scale(rate, rate, cX, cY);
if (null != lines && lines.size() != 0) {
for (int j = 0; j < lines.size(); j++) {
for (int k = 0; k < lines.get(j).getTargetElements().size(); k++) {
caculate(centerX + lines.get(j).getResourceX(), centerY + lines.get(j).getResourceY(), lines.get(j).getResourceR(), centerX + lines.get(j).getTargetElements().get(k).getTargetX(), centerY + lines.get(j).getTargetElements().get(k).getTargetY(),
lines.get(j).getTargetElements().get(k).getTargetR() + 3, lines.get(j).getTargetElements().get(k).getText(), lines.get(j).getTargetElements().get(k).getTextColor(), lines.get(j).getTargetElements().get(k).getLineColor(), lines.get(j).getTargetElements().get(k).getTextSize());
}
}
}
if (null != circles && circles.size() > 0) {
for (int i = 0; i < circles.size(); i++) {