zeyuphoenix

          愿我愛的人快樂,愿愛我的人快樂,為了這些,我愿意不快樂.

          樹(選擇框)

          先是春游然后又開始忙了,都沒時間寫了,不爽.

          JTree的選擇框其實也是Renderer的一種表現(xiàn),單純實現(xiàn)效果的話很簡單,只需要設(shè)置Renderer就可以了,但是如果你想實現(xiàn)一個好的JTree選擇框就比較難了,因為這里有選擇問題、監(jiān)聽問題、選中后的父子關(guān)系等,這里主要是參考別人的實現(xiàn)寫的.

          先看一個簡單的例子,從網(wǎng)上看到的,如圖:

          它只是單純的實現(xiàn)了樹的選擇框效果,寫的很簡單.

          首先是TreeNode,我們擴(kuò)展JavaTreeNode,添加了我們自己的選擇屬性:

          publicclass CheckBoxTreeNode extends DefaultMutableTreeNode {

          屬性:

              /**

               * is node check

               */

          privatebooleanisChecked = false;

          然后就是Renderer,這里實現(xiàn)TreeCellRenderer,并繼承了JCheckBox

          publicclass CheckBoxTreeCellRenderer extends JCheckBox implements

                 TreeCellRenderer {

          然后實現(xiàn)接口的方法:

              @Override

             public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,

                     boolean hasFocus) {

          取得TreeNode

                 // get tree node

                 CheckBoxTreeNode node = ((CheckBoxTreeNode) value);

                 // set check box text

                 setText(node.toString());

          設(shè)置選擇狀態(tài)后返回:

                 setSelected(false);

                 returnthis;

          然后是一個我們自己的JTree,它增加了鼠標(biāo)監(jiān)聽,實現(xiàn)選擇框的勾選效果:

          publicclass CheckBoxTree extends JTree {

          在構(gòu)造函數(shù)里設(shè)置它的Renderer和監(jiān)聽:

              setCellRenderer(new CheckBoxTreeCellRenderer());

              addCheckingListener();

          然后是處理監(jiān)聽:

          addMouseListener(new MouseAdapter() {

              @Override

              publicvoid mousePressed(MouseEvent e) {

          在監(jiān)聽里先取得選擇的節(jié)點:

                  int row = getRowForLocation(e.getX(), e.getY());

                 TreePath treePath = getPathForRow(row);

                 CheckBoxTreeNode node = ((CheckBoxTreeNode) treePath

                     .getLastPathComponent());

          然后設(shè)置選擇狀態(tài):

                 // if check , will uncheck.

                 boolean checking = !node.isChecked();

                 node.setChecked(checking);

          當(dāng)然這里可以做額外處理,例如選中節(jié)點時同時選擇子節(jié)點或者父節(jié)點:

          最后刷新:

                 // repaint

                 repaint();

          然后就是使用了,和一般的JTree基本一致,只是節(jié)點是我們自己定義的Node.

              CheckBoxTree tree = new CheckBoxTree();

          之后就和一個普通的JTree一樣了.

          到這里,簡單的選擇框樹就完成了,它基本可以用,但是還是有一些問題的.

          因為我們使用JCheckBox作為樹的節(jié)點,導(dǎo)致我們只能呈現(xiàn)一個選擇框和一個文本框的效果,其它復(fù)雜效果很難再實現(xiàn)了,簡單說就是JCheckBox很難做效果

          解決辦法就是我們在做Renderer,使用JPanel繼承,這樣就可以實現(xiàn)更復(fù)雜的Node.

          要通過鼠標(biāo)監(jiān)聽和Repaint才能使樹選擇效果刷新.

          通過解決問題一,我們可以在Renderer設(shè)置JCheckBox,這樣就避免了刷新;同時我們可以額外實現(xiàn)一個單選效果.

          選擇模式簡單(需要鼠標(biāo)事件),驗證數(shù)據(jù)單一(關(guān)聯(lián)關(guān)系不好),封裝性不好(使用達(dá)不到完全封閉)

          這個問題就需要定義接口和數(shù)據(jù)結(jié)構(gòu)了,本來想自己寫呢,后來發(fā)現(xiàn)一個老外寫了一個,很強(qiáng)大,比我寫的好多了,就用它了.

          先看我們簡單解決12的例子,如圖:

          TreeNode和前一個例子差不多,我們額外添加了一個選擇模式的屬性:

          publicclass MyTreeNode extends DefaultMutableTreeNode {

          兩個屬性,表示選擇和選擇模式:

              /** is select or not. */

              privatebooleanisSelected = false;

              /** select model. */

          privateintselectionMode = 0;

          在設(shè)置選擇時,如果只允許單選的選擇模式,我們設(shè)置其它不選擇:

              publicvoid setSelected(boolean isSelected) {

                  this.isSelected = isSelected;

                  if ((selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)

                       && (children != null)) {

                      Enumeration<?> enumTemp = children.elements();

                      while (enumTemp.hasMoreElements()) {

                          MyTreeNode node = (MyTreeNode) enumTemp.nextElement();

                          node.setSelected(isSelected);

                      }

                  }

              }

          然后就是Renderer,這里我們不繼承JCheckBox,繼承JPanel:

          publicclass MyCheckRenderer extends JPanel implements TreeCellRenderer {

          JPanel上我們放置了兩個組件,當(dāng)然也可以放置更復(fù)雜的:

              /** check box in tree node. */

              private JCheckBox checkBox = null;

              /** label text in tree node. */

              private TreeLabel labelText = null;

          其中TreeLabel是我們自己寫的:

          privateclass TreeLabel extends JLabel {

          我們?yōu)樗砑恿私裹c狀態(tài)和選擇狀態(tài):

              /** is select. */

              privatebooleanisSelected = false;

              /** is have focus. */

              privatebooleanhasFocus = false;

          然后復(fù)寫它的方法和方法,使它的呈現(xiàn)和JTree一致:

              @Override

              public Dimension getPreferredSize() {

              @Override

              publicvoid paint(Graphics g) {

          設(shè)置顏色和大小:

                 g.setColor(UIManager

                        .getColor("Tree.selectionBorderColor"));

                 g.drawRect(imageOffset, 0, d.width - 1 - imageOffset,

                        d.height - 1);

          在類里我們實現(xiàn)TreeCellRenderer接口的方法:

              @Override

             public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row,

                     boolean hasFocus) {

          設(shè)置JCheckBox的狀態(tài):

                 checkBox.setSelected(((MyTreeNode) value).isSelected());

          設(shè)置樹節(jié)點顯示:

                 labelText.setFont(tree.getFont());

                 labelText.setText(stringValue);

                 labelText.setSelected(isSelected);

                 labelText.setFocus(hasFocus);

          然后復(fù)寫JPanelgetPreferredSize方法和doLayout方法,使顯示合理:

              /**

               * set select node's prefer size.

               */

              @Override

              public Dimension getPreferredSize() {

                 Dimension d_check = checkBox.getPreferredSize();

                 Dimension d_label = labelText.getPreferredSize();

                 returnnew Dimension(d_check.width + d_label.width,

                        (d_check.height < d_label.height ? d_label.height

                               : d_check.height));

              }

              /**

               * set tree select node layout.

               */

              @Override

              publicvoid doLayout() {

                 Dimension d_check = checkBox.getPreferredSize();

                 Dimension d_label = labelText.getPreferredSize();

                 int y_check = 0;

                 int y_label = 0;

                 if (d_check.height < d_label.height) {

                     y_check = (d_label.height - d_check.height) / 2;

                 } else {

                     y_label = (d_check.height - d_label.height) / 2;

                 }

                 checkBox.setLocation(0, y_check);

                  checkBox.setBounds(0, y_check, d_check.width, d_check.height);

                 labelText.setLocation(d_check.width, y_label);

                 labelText.setBounds(d_check.width, y_label, d_label.width,

                        d_label.height);

              }

          最后是使用,和前面得差不多,先取得JTree,再設(shè)置Renderer,監(jiān)聽鼠標(biāo).

                  JTree tree = new JTree();

                  tree.setCellRenderer(new MyCheckRenderer());

                 tree.addMouseListener(new NodeSelectionListener(tree));

          處理鼠標(biāo)監(jiān)聽:

                  @Override

                  publicvoid mouseClicked(MouseEvent e) {

          設(shè)置選擇:

                      MyTreeNode node = (MyTreeNode) path.getLastPathComponent();

                      boolean isSelected = !(node.isSelected());

                      node.setSelected(isSelected);

                      ((DefaultTreeModel) tree.getModel()).nodeChanged(node);

          然后和普通的JTree一樣使用了.

          最后是問題三的解決,這個是一個老外寫的,很不錯,但是很復(fù)雜,如圖:

          它實現(xiàn)了無關(guān)的選中、父選擇子全選擇、子選擇父選擇和子單選擇父選擇四種選擇狀態(tài).

          代碼別人寫的就不寫了,寫下它的大概思路:

          首先它定義了一個事件: TreeCheckingEvent,這個事件是描繪選擇關(guān)系和選擇路徑的,這樣就簡化了鼠標(biāo)事件處理;然后是事件的監(jiān)聽器: TreeCheckingListener,它提供監(jiān)聽.

          然后定義了一個數(shù)據(jù)模型:TreeCheckingMode,在模型里它提供了checkPathuncheckPathupdateCheckAfterChildrenInsertedupdateCheckAfterChildrenRemovedupdateCheckAfterStructureChanged的虛方法,供子類實現(xiàn),這些實現(xiàn)就是樹的選擇狀態(tài)的表示,當(dāng)一個樹的節(jié)點選擇、取消選擇、插入、刪除和更新之后,節(jié)點選擇狀態(tài)的變化.

          TreeCheckingModeSimpleTreeCheckingModePropagateTreeCheckingModePropagatePreservingCheckTreeCheckingModePropagatePreservingUncheckTreeCheckingMode四個實現(xiàn)類,代表了四種選擇關(guān)聯(lián)狀態(tài),通過實現(xiàn)父類的虛方法,當(dāng)樹選擇變化或內(nèi)容變化時,選擇節(jié)點變化,這樣我們的樹就可以了四種選擇邏輯了,當(dāng)然你也可以繼承TreeCheckingMode實現(xiàn)自己的選擇邏輯.

          然后還有一個RendererDefaultCheckboxTreeCellRenderer,繼承JPanel,實現(xiàn)TreeCellRenderer,來渲染樹的節(jié)點,這個和我上面寫的基本一致.所以大家可以看到,UI最后還是做邏輯,UI呈現(xiàn)也就那么多,還是邏輯多而復(fù)雜.

          然后是樹的CheckModelTreeCheckingModelDefaultTreeCheckingModel,主要是處理樹的數(shù)據(jù)變化和增刪改;以及監(jiān)聽和選擇狀態(tài)的記錄(樹的Model我們還用,在它的基礎(chǔ)上添加了新的選擇Model,這樣就分離了數(shù)據(jù)和選擇狀態(tài)).
          最后是CheckboxTree,它繼承JTree,設(shè)置ModelTreeModel,設(shè)置CheckModelTreeCheckingModel,設(shè)置ModeTreeCheckingMode,增加TreeCheckingListener監(jiān)聽,并提供了展開樹等事件.

          使用很簡單,new出來直接使用就可以了,可以設(shè)置選擇狀態(tài):

              tree.getCheckingModel().setCheckingMode(CheckingMode.PROPAGATE);

          總之,這個樹寫的還是不錯了,它自己實現(xiàn)了事件和數(shù)據(jù)模型,這樣可以很自由的進(jìn)行樹數(shù)據(jù)的處理,建議如果大家做大項目的時候,比較常用的組件還是自己實現(xiàn)寫,使用自己的事件處理和數(shù)據(jù)模型甚至UI,這樣雖然麻煩,但可以做出更利于自己的效果(當(dāng)然是很大很復(fù)雜的項目,小項目還不夠費時間呢).

          到此為止,關(guān)于樹的就寫完了,除了一個DND拖拽應(yīng)該沒有漏什么東西了,樹的組件并不復(fù)雜, 方法不算太多,UI可以重寫的也很少,Mdoel因為數(shù)據(jù)集簡單也不復(fù)雜,數(shù)據(jù)處理最多也就是個遞歸,監(jiān)聽也就是鼠標(biāo)和樹選擇、展開事件,因此一般樹的UI和事件處理不是我們的重點,我在這里寫的可能大多數(shù)項目都不會用到,(至少我很少用).因此對樹的處理大多還是邏輯,主要是生成樹、更新和刪除節(jié)點.

          posted on 2010-04-27 21:54 zeyuphoenix 閱讀(2738) 評論(0)  編輯  收藏 所屬分類: JTree的使用

          導(dǎo)航

          <2010年4月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          統(tǒng)計

          常用鏈接

          留言簿(52)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 科技| 永和县| 楚雄市| 莎车县| 新竹市| 县级市| 吉安县| 精河县| 安塞县| 武功县| 额尔古纳市| 巫溪县| 永宁县| 西安市| 河东区| 沙雅县| 察雅县| 铜川市| 高要市| 嘉兴市| 彩票| 沙坪坝区| 临沂市| 尚义县| 浦北县| 宜春市| 留坝县| 孙吴县| 华蓥市| 界首市| 涪陵区| 竹北市| 讷河市| 博白县| 武功县| 山东| 苗栗县| 翁牛特旗| 凤庆县| 渭南市| 巴塘县|