中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java怎么實現帶復選框的樹

發布時間:2021-08-06 10:46:21 來源:億速云 閱讀:168 作者:小新 欄目:編程語言

小編給大家分享一下Java怎么實現帶復選框的樹,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

在使用Java Swing開發UI程序時,很有可能會遇到使用帶復選框的樹的需求,但是Java Swing并沒有提供這個組件,因此如果你有這個需求,你就得自己動手實現帶復選框的樹。

CheckBoxTree與JTree在兩個層面上存在差異:

1.在模型層上,CheckBoxTree的每個結點需要一個成員來保存其是否被選中,但是JTree的結點則不需要。
2.在視圖層上,CheckBoxTree的每個結點比JTree的結點多顯示一個復選框。

既然存在兩個差異,那么只要我們把這兩個差異部分通過自己的實現填補上,那么帶復選框的樹也就實現了。
現在開始解決第一個差異。為了解決第一個差異,需要定義一個新的結點類CheckBoxTreeNode,該類繼承DefaultMutableTreeNode,并增加新的成員isSelected來表示該結點是否被選中。對于一顆CheckBoxTree,如果某一個結點被選中的話,其復選框會勾選上,并且使用CheckBoxTree的動機在于可以一次性地選中一顆子樹。那么,在選中或取消一個結點時,其祖先結點和子孫結點應該做出某種變化。在此,我們應用如下遞歸規則:

1.如果某個結點被手動選中,那么它的所有子孫結點都應該被選中;如果選中該結點使其父節點的所有子結點都被選中,則選中其父結點。
2.如果某個結點被手動取消選中,那么它的所有子孫結點都應該被取消選中;如果該結點的父結點處于選中狀態,則取消選中其父結點。

注意:上面的兩條規則是遞歸規則,當某個結點發生變化,導致另外的結點發生變化時,另外的結點也會導致其他的結點發生變化。在上面兩條規則中,強調手動,是因為手動選中或者手動取消選中一個結點,會導致其他結點發生非手動的選中或者取消選中,這種非手動導致的選中或者非取消選中則不適用于上述規則。

按照上述規則實現的CheckBoxTreeNode源代碼如下:

package demo; 
 
import javax.swing.tree.DefaultMutableTreeNode; 
 
public class CheckBoxTreeNode extends DefaultMutableTreeNode 
{ 
 protected boolean isSelected; 
  
 public CheckBoxTreeNode() 
 { 
  this(null); 
 } 
  
 public CheckBoxTreeNode(Object userObject) 
 { 
  this(userObject, true, false); 
 } 
  
 public CheckBoxTreeNode(Object userObject, boolean allowsChildren, boolean isSelected) 
 { 
  super(userObject, allowsChildren); 
  this.isSelected = isSelected; 
 } 
 
 public boolean isSelected() 
 { 
  return isSelected; 
 } 
  
 public void setSelected(boolean _isSelected) 
 { 
  this.isSelected = _isSelected; 
   
  if(_isSelected) 
  { 
   // 如果選中,則將其所有的子結點都選中 
   if(children != null) 
   { 
    for(Object obj : children) 
    { 
     CheckBoxTreeNode node = (CheckBoxTreeNode)obj; 
     if(_isSelected != node.isSelected()) 
      node.setSelected(_isSelected); 
    } 
   } 
   // 向上檢查,如果父結點的所有子結點都被選中,那么將父結點也選中 
   CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
   // 開始檢查pNode的所有子節點是否都被選中 
   if(pNode != null) 
   { 
    int index = 0; 
    for(; index < pNode.children.size(); ++ index) 
    { 
     CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index); 
     if(!pChildNode.isSelected()) 
      break; 
    } 
    /* 
     * 表明pNode所有子結點都已經選中,則選中父結點, 
     * 該方法是一個遞歸方法,因此在此不需要進行迭代,因為 
     * 當選中父結點后,父結點本身會向上檢查的。 
     */ 
    if(index == pNode.children.size()) 
    { 
     if(pNode.isSelected() != _isSelected) 
      pNode.setSelected(_isSelected); 
    } 
   } 
  } 
  else 
  { 
   /* 
    * 如果是取消父結點導致子結點取消,那么此時所有的子結點都應該是選擇上的; 
    * 否則就是子結點取消導致父結點取消,然后父結點取消導致需要取消子結點,但 
    * 是這時候是不需要取消子結點的。 
    */ 
   if(children != null) 
   { 
    int index = 0; 
    for(; index < children.size(); ++ index) 
    { 
     CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index); 
     if(!childNode.isSelected()) 
      break; 
    } 
    // 從上向下取消的時候 
    if(index == children.size()) 
    { 
     for(int i = 0; i < children.size(); ++ i) 
     { 
      CheckBoxTreeNode node = (CheckBoxTreeNode)children.get(i); 
      if(node.isSelected() != _isSelected) 
       node.setSelected(_isSelected); 
     } 
    } 
   } 
    
   // 向上取消,只要存在一個子節點不是選上的,那么父節點就不應該被選上。 
   CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
   if(pNode != null && pNode.isSelected() != _isSelected) 
    pNode.setSelected(_isSelected); 
  } 
 } 
}

第一個差異通過繼承DefaultMutableTreeNode定義CheckBoxTreeNode解決了,接下來需要解決第二個差異。第二個差異是外觀上的差異,JTree的每個結點是通過TreeCellRenderer進行顯示的。為了解決第二個差異,我們定義一個新的類CheckBoxTreeCellRenderer,該類實現了TreeCellRenderer接口。CheckBoxTreeRenderer的源代碼如下:

package demo; 
 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
 
import javax.swing.JCheckBox; 
import javax.swing.JPanel; 
import javax.swing.JTree; 
import javax.swing.UIManager; 
import javax.swing.plaf.ColorUIResource; 
import javax.swing.tree.TreeCellRenderer; 
 
public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer 
{ 
 protected JCheckBox check; 
 protected CheckBoxTreeLabel label; 
  
 public CheckBoxTreeCellRenderer() 
 { 
  setLayout(null); 
  add(check = new JCheckBox()); 
  add(label = new CheckBoxTreeLabel()); 
  check.setBackground(UIManager.getColor("Tree.textBackground")); 
  label.setForeground(UIManager.getColor("Tree.textForeground")); 
 } 
  
 /** 
  * 返回的是一個<code>JPanel</code>對象,該對象中包含一個<code>JCheckBox</code>對象 
  * 和一個<code>JLabel</code>對象。并且根據每個結點是否被選中來決定<code>JCheckBox</code> 
  * 是否被選中。 
  */ 
 @Override 
 public Component getTreeCellRendererComponent(JTree tree, Object value, 
   boolean selected, boolean expanded, boolean leaf, int row, 
   boolean hasFocus) 
 { 
  String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus); 
  setEnabled(tree.isEnabled()); 
  check.setSelected(((CheckBoxTreeNode)value).isSelected()); 
  label.setFont(tree.getFont()); 
  label.setText(stringValue); 
  label.setSelected(selected); 
  label.setFocus(hasFocus); 
  if(leaf) 
   label.setIcon(UIManager.getIcon("Tree.leafIcon")); 
  else if(expanded) 
   label.setIcon(UIManager.getIcon("Tree.openIcon")); 
  else 
   label.setIcon(UIManager.getIcon("Tree.closedIcon")); 
    
  return this; 
 } 
 
 @Override 
 public Dimension getPreferredSize() 
 { 
  Dimension dCheck = check.getPreferredSize(); 
  Dimension dLabel = label.getPreferredSize(); 
  return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height); 
 } 
  
 @Override 
 public void doLayout() 
 { 
  Dimension dCheck = check.getPreferredSize(); 
  Dimension dLabel = label.getPreferredSize(); 
  int yCheck = 0; 
  int yLabel = 0; 
  if(dCheck.height < dLabel.height) 
   yCheck = (dLabel.height - dCheck.height) / 2; 
  else 
   yLabel = (dCheck.height - dLabel.height) / 2; 
  check.setLocation(0, yCheck); 
  check.setBounds(0, yCheck, dCheck.width, dCheck.height); 
  label.setLocation(dCheck.width, yLabel); 
  label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height); 
 } 
  
 @Override 
 public void setBackground(Color color) 
 { 
  if(color instanceof ColorUIResource) 
   color = null; 
  super.setBackground(color); 
 } 
}

在CheckBoxTreeCellRenderer的實現中,getTreeCellRendererComponent方法返回的是JPanel,而不是像DefaultTreeCellRenderer那樣返回JLabel,因此JPanel中的JLabel無法對選中做出反應,因此我們重新實現了一個JLabel的子類CheckBoxTreeLabel,它可以對選中做出反應,其源代碼如下:

package demo; 
 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
 
import javax.swing.Icon; 
import javax.swing.JLabel; 
import javax.swing.UIManager; 
import javax.swing.plaf.ColorUIResource; 
 
public class CheckBoxTreeLabel extends JLabel 
{ 
 private boolean isSelected; 
 private boolean hasFocus; 
  
 public CheckBoxTreeLabel() 
 { 
 } 
  
 @Override 
 public void setBackground(Color color) 
 { 
  if(color instanceof ColorUIResource) 
   color = null; 
  super.setBackground(color); 
 } 
  
 @Override 
 public void paint(Graphics g) 
 { 
  String str; 
  if((str = getText()) != null) 
  { 
   if(0 < str.length()) 
   { 
    if(isSelected) 
     g.setColor(UIManager.getColor("Tree.selectionBackground")); 
    else 
     g.setColor(UIManager.getColor("Tree.textBackground")); 
    Dimension d = getPreferredSize(); 
    int imageOffset = 0; 
    Icon currentIcon = getIcon(); 
    if(currentIcon != null) 
     imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() - 1); 
    g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height); 
    if(hasFocus) 
    { 
     g.setColor(UIManager.getColor("Tree.selectionBorderColor")); 
     g.drawRect(imageOffset, 0, d.width - 1 - imageOffset, d.height - 1); 
    } 
   } 
  } 
  super.paint(g); 
 } 
  
 @Override 
 public Dimension getPreferredSize() 
 { 
  Dimension retDimension = super.getPreferredSize(); 
  if(retDimension != null) 
   retDimension = new Dimension(retDimension.width + 3, retDimension.height); 
  return retDimension; 
 } 
  
 public void setSelected(boolean isSelected) 
 { 
  this.isSelected = isSelected; 
 } 
  
 public void setFocus(boolean hasFocus) 
 { 
  this.hasFocus = hasFocus; 
 } 
}

通過定義CheckBoxTreeNode和CheckBoxTreeCellRenderer。我們解決了CheckBoxTree和JTree的兩個根本差異,但是還有一個細節問題需要解決,就是CheckBoxTree可以響應用戶事件決定是否選中某個結點。為此,我們為CheckBoxTree添加一個響應用戶鼠標事件的監聽器CheckBoxTreeNodeSelectionListener,該類的源代碼如下:

package demo; 
 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
 
import javax.swing.JTree; 
import javax.swing.tree.TreePath; 
import javax.swing.tree.DefaultTreeModel; 
 
public class CheckBoxTreeNodeSelectionListener extends MouseAdapter 
{ 
 @Override 
 public void mouseClicked(MouseEvent event) 
 { 
  JTree tree = (JTree)event.getSource(); 
  int x = event.getX(); 
  int y = event.getY(); 
  int row = tree.getRowForLocation(x, y); 
  TreePath path = tree.getPathForRow(row); 
  if(path != null) 
  { 
   CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent(); 
   if(node != null) 
   { 
    boolean isSelected = !node.isSelected(); 
    node.setSelected(isSelected); 
    ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node); 
   } 
  } 
 } 
}

到此為止,CheckBoxTree所需要的所有組件都已經完成了,接下來就是如何使用這些組件。下面給出了使用這些組件的源代碼:

package demo; 
 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTree; 
import javax.swing.tree.DefaultTreeModel; 
 
public class DemoMain 
{ 
 public static void main(String[] args) 
 { 
  JFrame frame = new JFrame("CheckBoxTreeDemo"); 
  frame.setBounds(200, 200, 400, 400); 
  JTree tree = new JTree(); 
  CheckBoxTreeNode rootNode = new CheckBoxTreeNode("root"); 
  CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1"); 
  CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1"); 
  CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2"); 
  CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3"); 
  node1.add(node1_1); 
  node1.add(node1_2); 
  node1.add(node1_3); 
  CheckBoxTreeNode node2 = new CheckBoxTreeNode("node_2"); 
  CheckBoxTreeNode node2_1 = new CheckBoxTreeNode("node_2_1"); 
  CheckBoxTreeNode node2_2 = new CheckBoxTreeNode("node_2_2"); 
  node2.add(node2_1); 
  node2.add(node2_2); 
  rootNode.add(node1); 
  rootNode.add(node2); 
  DefaultTreeModel model = new DefaultTreeModel(rootNode); 
  tree.addMouseListener(new CheckBoxTreeNodeSelectionListener()); 
  tree.setModel(model); 
  tree.setCellRenderer(new CheckBoxTreeCellRenderer()); 
  JScrollPane scroll = new JScrollPane(tree); 
  scroll.setBounds(0, 0, 300, 320); 
  frame.getContentPane().add(scroll); 
   
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
  frame.setVisible(true); 
 } 
}

其執行結果如下圖所示:

Java怎么實現帶復選框的樹

以上是“Java怎么實現帶復選框的樹”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永春县| 临汾市| 阿合奇县| 怀化市| 兴业县| SHOW| 色达县| 体育| 班戈县| 噶尔县| 保靖县| 芦山县| 彭山县| 沭阳县| 芜湖县| 兰州市| 秀山| 澄迈县| 衡南县| 晋江市| 蒲城县| 邮箱| 天津市| 仪征市| 揭东县| 武山县| 福贡县| 丹寨县| 平昌县| 滨州市| 新干县| 高淳县| 右玉县| 澳门| 临湘市| 元阳县| 什邡市| 永丰县| 庄河市| 米泉市| 鹤峰县|