preface
Oddly enough, when I was coding in high school, I spent all day working on noIP topics, delving into algorithms such as recursion, divide-and-conquer, and dynamic programming. When I actually work, I find that I rarely need to use the algorithm until this page. In fact, I wrote this page the year before last, but I have been lazy and do not want to organize and publish it. Last year, I published some on CSDN, but I did not write it carefully. Today, I will tell you about it carefully over the weekend, hoping to remind you of the algorithm. The project requirement is a mind map, the number of nodes, and the data returned by the server, which requires each click to calculate the location and draw the layout.
The effect
Train of thought
1. The layout: So this layout is a diagram, it can be really big, and you can drag it up, down, left, and right, so I’m thinking of an HVScrollView, and I’m just gonna put a RelativeLayout inside it, and I’m gonna set it up to 500DP, and then I’m gonna have a new node, Something like addView within a RelativeLayout makes the layout larger and supports various kinds of scrolling. When a node needs to be removed, call RemoveView to remove the layout, reduce the width and height, and save memory. 2. Node: For the time being, each node is regarded as a button. The position to be drawn is calculated according to the number, where x position is the previous node + a fixed value, and Y position is the previous node Y – the current number of nodes * the height of each node / 2
X is equal to the previous x plus a// A is the node spacing.Y is equal to the previous y minus n times b divided by2 //n is the number of nodes and b is the placeholder height of each node.Copy the code
Lines are fourth-order Bezier curves with four nodes as shown in the figure below.
implementation
A few points finished, the following step by step to achieve, mainly or more pull ideas. 1. There is an animation at the beginning of the node, and the animation code is as follows:
ScaleAnimation animation = new ScaleAnimation(0.0f,1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new BounceInterpolator());
animation.setStartOffset(tree_current == 1 ? 1050 : 50);// The number of animation seconds.
animation.setFillAfter(true);
animation.setDuration(700);Copy the code
2. Define node entity classes based on actual requirements
public class nodechild {
private String id;
private String name;
private String buteid;
private String butetype;
private String nodetype;
private String ispass;
public String getNodetype() {
return nodetype;
}
public void setNodetype(String nodetype) {
this.nodetype = nodetype;
}
public nodechild(String id, String name, String buteid, String butetype, String nodetype) {
super(a);this.id = id;
this.name = name;
this.buteid = buteid;
this.butetype = butetype;
this.nodetype = nodetype;
}
public nodechild(String id, String name) {
super(a);this.id = id;
this.name = name;
}
public nodechild(String id, String name, String ispass) {
super(a);this.id = id;
this.name = name;
this.ispass = ispass;
}
public String getIspass() {
return ispass;
}
public void setIspass(String ispass) {
this.ispass = ispass;
}
public String getButeid() {
return buteid;
}
public void setButeid(String buteid) {
this.buteid = buteid;
}
public String getButetype() {
return butetype;
}
public void setButetype(String butetype) {
this.butetype = butetype;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name; }}Copy the code
Draw a button method
public void drawbutton(int button_y, int button_x, int line_x, final int tree_h, final nodechild[] nc,String nodeid) {}Copy the code
Button_x is the x coordinate of the current node. Button_y is the y coordinate of the current node. Line_x is the x coordinate of the line. The detailed code is as follows:
public void drawbutton(int button_y, int button_x, int line_x, final int tree_current, final nodechild[] nc, String nodeid) {
// The starting y coordinate of the storage line
int line_y = button_y;
// The button width is 300 for even tiers and 200 for even tiers
button_x = tree_current % 2= =1 ? button_x : button_x - 100;
// Get the number of drawings for the next level
int num = 1;
if(tree_current ! =1) num = nc.length;// The number of layers below
// Get the y coordinate of the first button at the next level
button_y = button_y - (num - 1) * bt_width / 2;
if (button_y < tree_xnum[tree_current]) {
button_y = tree_xnum[tree_current] + 100;
}
// Move the current layout to the center of the page
if (tree_current > 2) hv.scrollTo(button_x - 400, button_y - 100);
if (tree_xnum[tree_current] < button_y + 200 + (num - 1) * bt_width)
tree_xnum[tree_current] = button_y + 200 + (num - 1) * bt_width;
// Store the first button coordinates of the next level
final int button_y_f = button_y;
final int button_x_f = button_x;
for (int i = 0; i < num; i++) {
final int bt_paly_y = bt_width;
int bt_w = tree_current % 2= =0 ? bt_width : 200;
int bt_h = 200;
// Define and set button properties
bt[i] = new Button(NodeActivity.this);
if (tree_current % 2! =0) {
bt[i].setBackgroundResource(R.drawable.allokbutton);
} else {
bt[i].setBackgroundResource(R.drawable.button33);
}
bt[i].setTextColor(Color.WHITE);
bt[i].setTextSize(15 - (int) Math.sqrt(nc[i].getName().length() - 1));
bt[i].setText(nc[i].getName());
// Define and set the exit animation
final String nc_id = nc[i].getId();
ScaleAnimation animation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new BounceInterpolator());
animation.setStartOffset(tree_current == 1 ? 1050 : 50);// The number of animation seconds.
animation.setFillAfter(true);
animation.setDuration(700);
bt[i].startAnimation(animation);
final int i1 = i;
// Set the listener
bt[i].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// In erase mode, erase other nodes and lines of the same level
if (model) mstack.pop(tree_current);
// Prevent multiple clicks, lazy solution
if(((Button)v).getHint() ! =null) {
Toast.makeText(getApplicationContext(), ((Button)v).getText(), Toast.LENGTH_LONG).show();
return;
}
((Button)v).setHint("1");
insertLayout.setEnabled(false);
int w = button_y_f + i1 * bt_paly_y;
int h = button_x_f + bt_paly_y / 2 * 3;
getRemoteInfo(w, h, button_y_f + i1 * bt_paly_y, button_x_f, tree_current + 1, nc_id, nc[i1].getButeid()); }});// Add button to the page by layout
layoutParams[i] = new RelativeLayout.LayoutParams(bt_w, bt_h);
layoutParams[i].topMargin = button_y + i * bt_paly_y;
layoutParams[i].leftMargin = button_x;
insertLayout.addView(bt[i], layoutParams[i]);
// Draw lines into the page
if(tree_current ! =1) {
if (button_y + 100 + i * 300 - (line_y + 100) > =0) {// In order to optimize memory, also drunk
view = new DrawGeometryView(this.50.50, button_x + 100 - (line_x + bt_paly_y) + 50 + (tree_current % 2= =0 ? 100 : 0), button_y + 100 + i * 300
- (line_y + 100) + 50, nc[i].getButetype());
layoutParams1[i] = new RelativeLayout.LayoutParams(Math.abs(line_x - button_x) + 500.100 + button_y + i * 300 - line_y);
view.invalidate();
layoutParams1[i].topMargin = (line_y + 100) - 50;// line_y-600; //Math.min(line_y+100,button_y+100
layoutParams1[i].leftMargin = (line_x + bt_paly_y) - 50;// line_x+300;
if (tree_current % 2= =0) layoutParams1[i].leftMargin -= 100;
insertLayout.addView(view, layoutParams1[i]);
} else {
view = new DrawGeometryView(this.50, -(button_y + 100 + i * 300 - (line_y + 100)) + 50, button_x - line_x - 150 + (tree_current % 2= =0 ? 100 : 0), 50,
nc[i].getButetype());
layoutParams1[i] = new RelativeLayout.LayoutParams(Math.abs(line_x - button_x) + 500.100 + Math.abs(button_y + i * 300
- line_y));
view.invalidate();
layoutParams1[i].topMargin = (button_y + 100 + i * 300) - 50;// line_y-600; //Math.min(line_y+100,button_y+100
layoutParams1[i].leftMargin = (line_x + bt_paly_y) - 50;// line_x+300;
if (tree_current % 2= =0) layoutParams1[i].leftMargin -= 100;
insertLayout.addView(view, layoutParams1[i]);
}
/ / the line into the stack
mstack.push(view, tree_current);
}
/ / button into the stackmstack.push(bt[i], tree_current); }}Copy the code
Notes written very full, some values have not been extracted, a bit messy, but does not affect the reading. 4. Marking method
public class DrawGeometryView extends View {
private int beginx=0;
private int beginy=0;
private int stopx=100;
private int stopy=100;
private int offset=0;
private String word="dd";
/** * * @param context * @param attrs */
public DrawGeometryView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/** * * @param context */
public DrawGeometryView(Context context,int beginx,int beginy,int stopx,int stopy,String word) {
super(context);
this.beginx=beginx;
this.beginy=beginy;
this.stopx=stopx;
this.stopy=stopy;
if (word==null) word="";
this.word=word;
}
public int Dp2Px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint redPaint = new Paint(); // Red brush
redPaint.setAntiAlias(true); // Anti-aliasing effect, smooth drawing
redPaint.setColor(Color.WHITE); // Set the brush color
redPaint.setStrokeWidth(5.0f);// Set the stroke width
redPaint.setStyle(Style.STROKE);// Set the brush fill type (full fill)
redPaint.setTextSize(50);/ / font
Path mPath=new Path();
mPath.reset();
/ / starting point
mPath.moveTo(beginx, beginy);
// Bezier curve
mPath.cubicTo(beginx+80, beginy, beginx+80, stopy,stopx- 100., stopy);
/ / draw the pathcanvas.drawPath(mPath, redPaint); }}Copy the code
This method also has some text drawing in the project, and I have deleted some of the code. 5. The stack
public class Mystack {
View[] v = new View[1500];
int[] treehigh = new int[1500];
int size = 0;
public void push(View view, int treecurrent) {
size++;
v[size] = view;
treehigh[size] = treecurrent;
}
public void pop(int treecurrent) {
while (treehigh[size] > treecurrent && size > 0) {
if (size > 0) insertLayout.removeView(v[size]);
size--;
}
for (int j = 49; j > treecurrent; j--) {// Tree hd 0
tree_xnum[j] = 0;
}
for (int x = size; x > 0; x--) {
if (treehigh[x] > treecurrent) {
insertLayout.removeView(v[x]);
}// Fix the bug that the top element of the stack is occupied by the previous tree element, but it will waste a small amount of memory.
if (treehigh[x] == treecurrent) {
try {
((Button) v[x]).setHint(null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}Copy the code
This code is mainly to use an array to store the view, in fact, I should have used SparseArray, at that time I wrote a normal array, but I didn’t bother to change. Push stores the view into the array, and pop iterates through the higher-level view and removes elements. 5. As for the code to switch mode, it is simple, it is a non operation
murp_nodemodel_title.setOnClickListener(new OnClickListener() {
@Override
public voidonClick(View v) { Toast.makeText(getApplicationContext(), ! model ?"Switched to erasing mode. Clicking on nodes erases subsequent nodes. Try it." : "Switched to normal mode, all nodes on the same graph, try it.", Toast.LENGTH_LONG).show();
model = !model;
}
});Copy the code
conclusion
On the whole, mind map drawing is realized, but there are still many places worth optimizing, such as the width and height of nodes are not extracted; The stack also needs to be optimized; Compute node occupying height is not rigorous enough; If you have time, you can play around with it. Source address github.com/qq273681448… If you feel good, remember to pay attention to me!