Android實(shí)現(xiàn)思維導(dǎo)圖

最近,小弟在實(shí)現(xiàn)一個(gè)思維導(dǎo)圖的開源控件藐石。下面我簡單介紹一下如下打造一個(gè)類似思維導(dǎo)圖軟件的ViewGroup滞诺。

基本結(jié)構(gòu).png

建立模型

主要模型結(jié)構(gòu)相對簡單:TreeModel,NoteModel婉烟,NoteView,TreeView暇屋。

核心實(shí)現(xiàn)分布如下:

  • TreeModel:樹形結(jié)構(gòu)的存儲(chǔ)似袁,樹形結(jié)構(gòu)的遍歷,添加咐刨、刪除節(jié)點(diǎn);
  • NoteModel:節(jié)點(diǎn)關(guān)聯(lián)的指向,和Parent的指向;
  • TreeView :繪制樹形結(jié)構(gòu)昙衅,對樹形結(jié)構(gòu)位置的糾正,實(shí)現(xiàn)View層的添加定鸟,刪除而涉,note關(guān)聯(lián)繪制;
  • NoteView:顯示text;

編寫位置計(jì)算核心代碼

在核心代碼中,我想和大家分享的是TreeView如何對多種Style(樹形形狀)進(jìn)行適配的問題联予。因?yàn)槲覀兊臉湫谓Y(jié)構(gòu)的表達(dá)多種的啼县,有的是一個(gè)半樹形圖,有點(diǎn)是圓形展開的等沸久。對于這個(gè)問題季眷,作為程序員如何進(jìn)行解耦能,采用Interface進(jìn)行解構(gòu)適配卷胯,統(tǒng)一行為子刮。所以在這里我寫了一個(gè)TreeLayoutManager進(jìn)行管理樹形的位置表達(dá)。這里我實(shí)現(xiàn)了一個(gè)RightTreeLayoutManager窑睁。代碼概況如下:

接口

public interface TreeLayoutManager {
    /**
     * 進(jìn)行樹形結(jié)構(gòu)的位置計(jì)算
     */
    void onTreeLayout(TreeView treeView);

    /**
     * 位置分布好后的回調(diào),用于確認(rèn)ViewGroup的大小
     */
    ViewBox onTreeLayoutCallBack();

    /**
     * 修正位置
     *
     * @param treeView
     * @param next
     */
    void correctLayout(TreeView treeView, NodeView next);

}

實(shí)現(xiàn)


public class RightTreeLayoutManager implements TreeLayoutManager{

    final int msg_standard_layout = 1;
    final int msg_correct_layout = 2;
    final int msg_box_call_back = 3;

    private ViewBox mViewBox;
    private int mDy;
    private int mDx;
    private int mHeight;

    public RightTreeLayoutManager(int dx, int dy, int height) {
        mViewBox = new ViewBox();

        this.mDx = dx;
        this.mDy = dy;
        this.mHeight = height;
    }

    @Override
    public void onTreeLayout(final TreeView treeView) {

        final TreeModel<String> mTreeModel = treeView.getTreeModel();
        if (mTreeModel != null) {

            View rootView = treeView.findNodeViewFromNodeModel(mTreeModel.getRootNode());
            if (rootView != null) {
                rootTreeViewLayout((NodeView) rootView);
            }

            mTreeModel.addForTreeItem(new ForTreeItem<NodeModel<String>>() {
                @Override
                public void next(int msg, NodeModel<String> next) {
                    doNext(msg, next, treeView);
                }
            });

            //基本布局
            mTreeModel.ergodicTreeInWith(msg_standard_layout);

            //糾正
            mTreeModel.ergodicTreeInWith(msg_correct_layout);

            mViewBox.clear();
            mTreeModel.ergodicTreeInDeep(msg_box_call_back);

        }
    }

    @Override
    public ViewBox onTreeLayoutCallBack() {
        if (mViewBox != null) {
            return mViewBox;
        } else {
            return null;
        }
    }

    /**
     * 布局糾正
     *
     * @param treeView
     * @param next
     */
    public void correctLayout(TreeView treeView, NodeView next) {
              //主要是糾正對于標(biāo)準(zhǔn)布局出現(xiàn)的錯(cuò)誤话告,譬如,在圖片糾正中的那種情況
             //糾正需要對同層的Note進(jìn)行拉伸
    }

    /**
     * 標(biāo)準(zhǔn)分布
     *
     * @param treeView
     * @param rootView
     */
    private void standardLayout(TreeView treeView, NodeView rootView) {
                //標(biāo)準(zhǔn)分布主要是在基于root節(jié)點(diǎn)進(jìn)行排開
                //對于奇數(shù)和偶數(shù)不同的情況進(jìn)行排開
                //中間向外計(jì)算位置
    }

    /**
     * 移動(dòng)
     *
     * @param rootView
     * @param dy
     */
    private void moveNodeLayout(TreeView superTreeView, NodeView rootView, int dy) {
                //如果一個(gè)note節(jié)點(diǎn)進(jìn)行了移動(dòng)卵慰,那么它
                //會(huì)影響到它的子節(jié)點(diǎn)的位置沙郭。
                //所以要進(jìn)行重新計(jì)算,把它的所有的Note位置進(jìn)行位移        
    }


    /**
     * root節(jié)點(diǎn)的定位
     *
     * @param rootView
     */
    private void rootTreeViewLayout(NodeView rootView) {
        int lr = mDy;
        int tr = mHeight / 2 - rootView.getMeasuredHeight() / 2;
        int rr = lr + rootView.getMeasuredWidth();
        int br = tr + rootView.getMeasuredHeight();
        rootView.layout(lr, tr, rr, br);
    }
}

View的連線

要實(shí)現(xiàn)對View和View的連線裳朋,只要在View的位置定了之后病线,就進(jìn)行畫線即可。用Sketch畫個(gè)演示如下:


Paste_Image.png

其中線為一個(gè)貝塞爾曲線鲤嫡。代碼如下:

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mTreeModel != null) {
            drawTreeLine(canvas, mTreeModel.getRootNode());
        }
        super.dispatchDraw(canvas);
    }

    /**
     * 繪制樹形的連線
     *
     * @param canvas
     * @param root
     */
    private void drawTreeLine(Canvas canvas, NodeModel<String> root) {
        NodeView fatherView = (NodeView) findNodeViewFromNodeModel(root);
        if (fatherView != null) {
            LinkedList<NodeModel<String>> childNodes = root.getChildNodes();
            for (NodeModel<String> node : childNodes) {

                //連線
                drawLineToView(canvas, fatherView, findNodeViewFromNodeModel(node));

                //遞歸
                drawTreeLine(canvas, node);
            }
        }
    }
    /**
     * 繪制兩個(gè)View直接的連線
     *
     * @param canvas
     * @param from
     * @param to
     */
    private void drawLineToView(Canvas canvas, View from, View to) {

        if (to.getVisibility() == GONE) {
            return;
        }

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);

        float width = 2f;

        paint.setStrokeWidth(dp2px(mContext, width));
        paint.setColor(mContext.getResources().getColor(R.color.chelsea_cucumber));

        int top = from.getTop();
        int formY = top + from.getMeasuredHeight() / 2;
        int formX = from.getRight();

        int top1 = to.getTop();
        int toY = top1 + to.getMeasuredHeight() / 2;
        int toX = to.getLeft();

        Path path = new Path();
        path.moveTo(formX, formY);
        path.quadTo(toX - dp2px(mContext, 15), toY, toX, toY);

        canvas.drawPath(path, paint);
    }

位置的糾正流程

位置糾正的問題送挑;在對于我之前的位置的算法探索流程如下圖,關(guān)鍵是寫好已知的代碼暖眼,之后糾正惕耕。

糾正.png
標(biāo)準(zhǔn)位置錯(cuò)誤.png
標(biāo)準(zhǔn)位置錯(cuò)誤的處理.png

觀察發(fā)現(xiàn),所有的移動(dòng)都是基于被操作的點(diǎn)開始的上下之分的上下移動(dòng)诫肠。

最后

記得在GitHub上給我一個(gè)星吧司澎。https://github.com/owant/ThinkMap
百度應(yīng)用市場:http://shouji.baidu.com/software/11238419.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欺缘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挤安,更是在濱河造成了極大的恐慌谚殊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤铜,死亡現(xiàn)場離奇詭異嫩絮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)围肥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門剿干,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人穆刻,你說我怎么就攤上這事怨愤。” “怎么了蛹批?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長篮愉。 經(jīng)常有香客問我腐芍,道長,這世上最難降的妖魔是什么试躏? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任猪勇,我火速辦了婚禮,結(jié)果婚禮上颠蕴,老公的妹妹穿的比我還像新娘泣刹。我一直安慰自己,他們只是感情好犀被,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布椅您。 她就那樣靜靜地躺著,像睡著了一般寡键。 火紅的嫁衣襯著肌膚如雪掀泳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天西轩,我揣著相機(jī)與錄音员舵,去河邊找鬼。 笑死藕畔,一個(gè)胖子當(dāng)著我的面吹牛马僻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播注服,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼韭邓,長吁一口氣:“原來是場噩夢啊……” “哼措近!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仍秤,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤熄诡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后诗力,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凰浮,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年苇本,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袜茧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓣窄,死狀恐怖笛厦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情俺夕,我是刑警寧澤裳凸,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站劝贸,受9級(jí)特大地震影響姨谷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜映九,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一梦湘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧件甥,春花似錦捌议、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至譬正,卻和暖如春弄捕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背导帝。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工守谓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人您单。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓斋荞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虐秦。 傳聞我的和親對象是個(gè)殘疾皇子平酿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容