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

Mind mapping





Mind mapping


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!