zeyuphoenix

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

          樹(選擇框)

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

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

          先看一個簡單的例子,從網上看到的,如圖:

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

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

          publicclass CheckBoxTreeNode extends DefaultMutableTreeNode {

          屬性:

              /**

               * is node check

               */

          privatebooleanisChecked = false;

          然后就是Renderer,這里實現TreeCellRenderer,并繼承了JCheckBox

          publicclass CheckBoxTreeCellRenderer extends JCheckBox implements

                 TreeCellRenderer {

          然后實現接口的方法:

              @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());

          設置選擇狀態后返回:

                 setSelected(false);

                 returnthis;

          然后是一個我們自己的JTree,它增加了鼠標監聽,實現選擇框的勾選效果:

          publicclass CheckBoxTree extends JTree {

          在構造函數里設置它的Renderer和監聽:

              setCellRenderer(new CheckBoxTreeCellRenderer());

              addCheckingListener();

          然后是處理監聽:

          addMouseListener(new MouseAdapter() {

              @Override

              publicvoid mousePressed(MouseEvent e) {

          在監聽里先取得選擇的節點:

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

                 TreePath treePath = getPathForRow(row);

                 CheckBoxTreeNode node = ((CheckBoxTreeNode) treePath

                     .getLastPathComponent());

          然后設置選擇狀態:

                 // if check , will uncheck.

                 boolean checking = !node.isChecked();

                 node.setChecked(checking);

          當然這里可以做額外處理,例如選中節點時同時選擇子節點或者父節點:

          最后刷新:

                 // repaint

                 repaint();

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

              CheckBoxTree tree = new CheckBoxTree();

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

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

          因為我們使用JCheckBox作為樹的節點,導致我們只能呈現一個選擇框和一個文本框的效果,其它復雜效果很難再實現了,簡單說就是JCheckBox很難做效果

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

          要通過鼠標監聽和Repaint才能使樹選擇效果刷新.

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

          選擇模式簡單(需要鼠標事件),驗證數據單一(關聯關系不好),封裝性不好(使用達不到完全封閉)

          這個問題就需要定義接口和數據結構了,本來想自己寫呢,后來發現一個老外寫了一個,很強大,比我寫的好多了,就用它了.

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

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

          publicclass MyTreeNode extends DefaultMutableTreeNode {

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

              /** is select or not. */

              privatebooleanisSelected = false;

              /** select model. */

          privateintselectionMode = 0;

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

              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上我們放置了兩個組件,當然也可以放置更復雜的:

              /** check box in tree node. */

              private JCheckBox checkBox = null;

              /** label text in tree node. */

              private TreeLabel labelText = null;

          其中TreeLabel是我們自己寫的:

          privateclass TreeLabel extends JLabel {

          我們為它添加了焦點狀態和選擇狀態:

              /** is select. */

              privatebooleanisSelected = false;

              /** is have focus. */

              privatebooleanhasFocus = false;

          然后復寫它的方法和方法,使它的呈現和JTree一致:

              @Override

              public Dimension getPreferredSize() {

              @Override

              publicvoid paint(Graphics g) {

          設置顏色和大小:

                 g.setColor(UIManager

                        .getColor("Tree.selectionBorderColor"));

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

                        d.height - 1);

          在類里我們實現TreeCellRenderer接口的方法:

              @Override

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

                     boolean hasFocus) {

          設置JCheckBox的狀態:

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

          設置樹節點顯示:

                 labelText.setFont(tree.getFont());

                 labelText.setText(stringValue);

                 labelText.setSelected(isSelected);

                 labelText.setFocus(hasFocus);

          然后復寫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,再設置Renderer,監聽鼠標.

                  JTree tree = new JTree();

                  tree.setCellRenderer(new MyCheckRenderer());

                 tree.addMouseListener(new NodeSelectionListener(tree));

          處理鼠標監聽:

                  @Override

                  publicvoid mouseClicked(MouseEvent e) {

          設置選擇:

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

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

                      node.setSelected(isSelected);

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

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

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

          它實現了無關的選中、父選擇子全選擇、子選擇父選擇和子單選擇父選擇四種選擇狀態.

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

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

          然后定義了一個數據模型:TreeCheckingMode,在模型里它提供了checkPathuncheckPathupdateCheckAfterChildrenInsertedupdateCheckAfterChildrenRemovedupdateCheckAfterStructureChanged的虛方法,供子類實現,這些實現就是樹的選擇狀態的表示,當一個樹的節點選擇、取消選擇、插入、刪除和更新之后,節點選擇狀態的變化.

          TreeCheckingModeSimpleTreeCheckingModePropagateTreeCheckingModePropagatePreservingCheckTreeCheckingModePropagatePreservingUncheckTreeCheckingMode四個實現類,代表了四種選擇關聯狀態,通過實現父類的虛方法,當樹選擇變化或內容變化時,選擇節點變化,這樣我們的樹就可以了四種選擇邏輯了,當然你也可以繼承TreeCheckingMode實現自己的選擇邏輯.

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

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

          使用很簡單,new出來直接使用就可以了,可以設置選擇狀態:

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

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

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

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

          導航

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

          統計

          常用鏈接

          留言簿(52)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 长沙市| 洛扎县| 蒙自县| 霍邱县| 济阳县| 金阳县| 榆树市| 新宁县| 屏东县| 龙南县| 比如县| 改则县| 曲松县| 通海县| 攀枝花市| 永善县| 石渠县| 清丰县| 怀安县| 教育| 阳东县| 淳安县| 青铜峡市| 皋兰县| 建阳市| 柏乡县| 汶川县| 武安市| 洪江市| 丽水市| 威信县| 揭东县| 宜兰市| 德江县| 偏关县| 晋城| 兰西县| 永定县| 寿光市| 苍山县| 大荔县|