先看效果图
实际运行很流畅,运行内存1M左右
最近脑抽,想实现一个亲戚关系图谱的应用,但始终没有找到合适的开源控件,于是就看到一篇《利用递归算法和堆栈实现android思维导图大纲图的动态绘制》实现类似效果的文章,并附上效果图
于是就想,想这种的自定义控件貌似在线上很少可看到(我自己是没有看到过)
那么在Android端实现起来,需要怎么写
经过一番思考
1.继承ViewGroup来写?然后线怎么绘制。。
2.线的绘制需要Canvas,用到画布画笔
3.有了画布画笔,线的绘制需要开始xy,结束xy
4.绘制是动态的,ViewGroup的话需要用到异步来绘制
5.Canvas异步绘制?那绘制时的数据怎么保证同步
6.于是有了这些条件:异步绘制,数据同步,用到画布,那不就是SurfaceView的宿命吗
不了解SurfaceView的同学请补一下SurfaceView的基础用法
有了这些基础和条件之后,就开始写代码了
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
public LoopThread loopThread;
public static final int START_X = 150;
public static final int START_Y = 500;
public MySurfaceView(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
loopThread = new LoopThread(holder, context);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
loopThread.isRunning = true;
loopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
loopThread.isRunning = false;
try {
loopThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class LoopThread extends Thread {
final SurfaceHolder holder;
Context context;
Paint paint;
//是否停止绘制
boolean isRunning = false;
float radius = 10f,
radius2 = 10f,
length = 10f,
height = 10f,
secondLength = 10f;
float maxRadius = 120,
maxRadius2 = 90,
maxLength = 150,
maxHeight = maxRadius * 2;
LoopThread(SurfaceHolder holder, Context context) {
this.holder = holder;
this.context = context;
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.FILL);
}
@Override
public void run() {
Canvas c = null;
while (isRunning) {
try {
synchronized (holder) {
c = holder.lockCanvas(null);
draw(c);
// Thread.sleep(100);
}
} finally {
holder.unlockCanvasAndPost(c);
}
}
}
private void draw(Canvas canvas) {
//clear screen
canvas.drawColor(Color.WHITE);
if (radius <= maxRadius) {//画第一个圆
canvas.translate(START_X, START_Y);
canvas.drawCircle(0, 0, radius += 10, paint);
Paint paint1 = new Paint();
paint1.setColor(Color.RED);
paint1.setStyle(Paint.Style.FILL);
paint1.setTypeface(Typeface.DEFAULT_BOLD);
paint1.setTextSize(radius > maxRadius / 2 ? maxRadius / 2 : radius);
canvas.drawText("hello", radius > maxRadius / 2 ? -maxRadius / 2 : -radius, radius > maxRadius / 2 ? maxRadius / 5 : radius, paint1);
} else if (radius > maxRadius && length < maxLength) {//画横
canvas.translate(START_X, START_Y);
canvas.drawCircle(0, 0, maxRadius, paint);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(maxRadius / 2);
canvas.drawText("hello", -maxRadius / 2, maxRadius / 5, paint);
canvas.translate(radius, 0);
Paint paint1 = new Paint();
paint1.setColor(Color.BLUE);
paint1.setStyle(Paint.Style.FILL);
paint1.setStrokeWidth(5f);
canvas.drawLine(0, 0, length += 30, 0, paint1);
} else if (height <= maxHeight) {//画竖线
canvas.translate(START_X, START_Y);
canvas.drawCircle(0, 0, radius, paint);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(maxRadius / 2);
canvas.drawText("hello", -maxRadius / 2, maxRadius / 5, paint);
canvas.translate(radius, 0);
Paint paint1 = new Paint();
paint1.setColor(Color.BLUE);
paint1.setStyle(Paint.Style.FILL);
paint1.setStrokeWidth(5f);
canvas.drawLine(0, 0, length, 0, paint1);
canvas.translate(length, 0);
canvas.drawLine(0, -height / 2, 0, height += 50, paint1);
} else if (secondLength <= maxLength) {//画3横
canvas.translate(START_X, START_Y);
canvas.drawCircle(0, 0, radius, paint);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(maxRadius / 2);
canvas.drawText("hello", -maxRadius / 2, maxRadius / 5, paint);
canvas.translate(radius, 0);
Paint paint1 = new Paint();
paint1.setColor(Color.BLUE);
paint1.setStyle(Paint.Style.FILL);
paint1.setStrokeWidth(5f);
canvas.drawLine(0, 0, length, 0, paint1);
canvas.translate(length, 0);
canvas.drawLine(0, -(START_Y + height) / 2, 0, (START_Y + height) / 2, paint1);
canvas.translate(0, -(START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength += 30, 0, paint1);
canvas.translate(0, (START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength, 0, paint1);
canvas.translate(0, (START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength, 0, paint1);
} else {//画3圆
canvas.translate(START_X, START_Y);
canvas.drawCircle(0, 0, radius, paint);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(maxRadius / 2);
canvas.drawText("hello", -maxRadius / 2, maxRadius / 5, paint);
canvas.translate(radius, 0);
Paint paint1 = new Paint();
paint1.setColor(Color.BLUE);
paint1.setStyle(Paint.Style.FILL);
paint1.setStrokeWidth(5f);
canvas.drawLine(0, 0, length, 0, paint1);
canvas.translate(length, 0);
canvas.drawLine(0, -(START_Y + height) / 2, 0, (START_Y + height) / 2, paint1);
canvas.translate(0, -(START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength, 0, paint1);
canvas.translate(0, (START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength, 0, paint1);
canvas.translate(0, (START_Y + height) / 2);
canvas.drawLine(0, 0, secondLength, 0, paint1);
//第二层第1个圆
canvas.translate(radius2 + secondLength, -(START_Y + height));
canvas.drawCircle(0, 0, radius2 += 30, this.paint);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(radius2 > maxRadius2 / 2 ? maxRadius2 / 2 : radius2);
canvas.drawText("haha", radius2 > maxRadius2 / 2 ? -maxRadius2 / 2 : -radius2, maxRadius2/5, paint);
//第二层第2个圆
canvas.translate(0, (START_Y + height) / 2);
canvas.drawCircle(0, 0, radius2, this.paint);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(radius2 > maxRadius2 / 2 ? maxRadius2 / 2 : radius2);
canvas.drawText("haha", radius2 > maxRadius2 / 2 ? -maxRadius2 / 2 : -radius2, maxRadius2/5, paint);
//第二层第3个圆
canvas.translate(0, (START_Y + height) / 2);
RectF rectF = new RectF();
rectF.left = -maxRadius2;
rectF.right = maxRadius2;
rectF.top = -maxRadius2;
rectF.bottom = maxRadius2;
canvas.drawRoundRect(rectF, 10, 10, this.paint);
paint.setStyle(Paint.Style.FILL);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(radius2 > maxRadius2 / 2 ? maxRadius2 / 2 : radius2);
canvas.drawText("haha", radius2 > maxRadius2 / 2 ? -maxRadius2 / 2 : -radius2, maxRadius2/5, paint);
if (radius2 > 90) {
isRunning = false;
}
}
}
}
}
代码都是很基本的绘制,没有什么特别的
目前该控件的缺点很多很多,以后有可能拓展的方向:可拖拽缩放大小 、动态绘制分级并画线、添加控件的点击事件