最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java帶復(fù)選框的樹(Java CheckBox Tree)實(shí)現(xiàn)和應(yīng)用

 更新時(shí)間:2017年11月23日 11:28:41   作者:女武神的騎行  
這篇文章主要為大家詳細(xì)介紹了Java帶復(fù)選框的樹實(shí)現(xiàn)和應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

在使用Java Swing開發(fā)UI程序時(shí),很有可能會(huì)遇到使用帶復(fù)選框的樹的需求,但是Java Swing并沒有提供這個(gè)組件,因此如果你有這個(gè)需求,你就得自己動(dòng)手實(shí)現(xiàn)帶復(fù)選框的樹。

CheckBoxTree與JTree在兩個(gè)層面上存在差異:

1.在模型層上,CheckBoxTree的每個(gè)結(jié)點(diǎn)需要一個(gè)成員來(lái)保存其是否被選中,但是JTree的結(jié)點(diǎn)則不需要。
2.在視圖層上,CheckBoxTree的每個(gè)結(jié)點(diǎn)比JTree的結(jié)點(diǎn)多顯示一個(gè)復(fù)選框。

既然存在兩個(gè)差異,那么只要我們把這兩個(gè)差異部分通過(guò)自己的實(shí)現(xiàn)填補(bǔ)上,那么帶復(fù)選框的樹也就實(shí)現(xiàn)了。
現(xiàn)在開始解決第一個(gè)差異。為了解決第一個(gè)差異,需要定義一個(gè)新的結(jié)點(diǎn)類CheckBoxTreeNode,該類繼承DefaultMutableTreeNode,并增加新的成員isSelected來(lái)表示該結(jié)點(diǎn)是否被選中。對(duì)于一顆CheckBoxTree,如果某一個(gè)結(jié)點(diǎn)被選中的話,其復(fù)選框會(huì)勾選上,并且使用CheckBoxTree的動(dòng)機(jī)在于可以一次性地選中一顆子樹。那么,在選中或取消一個(gè)結(jié)點(diǎn)時(shí),其祖先結(jié)點(diǎn)和子孫結(jié)點(diǎn)應(yīng)該做出某種變化。在此,我們應(yīng)用如下遞歸規(guī)則:

1.如果某個(gè)結(jié)點(diǎn)被手動(dòng)選中,那么它的所有子孫結(jié)點(diǎn)都應(yīng)該被選中;如果選中該結(jié)點(diǎn)使其父節(jié)點(diǎn)的所有子結(jié)點(diǎn)都被選中,則選中其父結(jié)點(diǎn)。
2.如果某個(gè)結(jié)點(diǎn)被手動(dòng)取消選中,那么它的所有子孫結(jié)點(diǎn)都應(yīng)該被取消選中;如果該結(jié)點(diǎn)的父結(jié)點(diǎn)處于選中狀態(tài),則取消選中其父結(jié)點(diǎn)。

注意:上面的兩條規(guī)則是遞歸規(guī)則,當(dāng)某個(gè)結(jié)點(diǎn)發(fā)生變化,導(dǎo)致另外的結(jié)點(diǎn)發(fā)生變化時(shí),另外的結(jié)點(diǎn)也會(huì)導(dǎo)致其他的結(jié)點(diǎn)發(fā)生變化。在上面兩條規(guī)則中,強(qiáng)調(diào)手動(dòng),是因?yàn)槭謩?dòng)選中或者手動(dòng)取消選中一個(gè)結(jié)點(diǎn),會(huì)導(dǎo)致其他結(jié)點(diǎn)發(fā)生非手動(dòng)的選中或者取消選中,這種非手動(dòng)導(dǎo)致的選中或者非取消選中則不適用于上述規(guī)則。

按照上述規(guī)則實(shí)現(xiàn)的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) 
  { 
   // 如果選中,則將其所有的子結(jié)點(diǎn)都選中 
   if(children != null) 
   { 
    for(Object obj : children) 
    { 
     CheckBoxTreeNode node = (CheckBoxTreeNode)obj; 
     if(_isSelected != node.isSelected()) 
      node.setSelected(_isSelected); 
    } 
   } 
   // 向上檢查,如果父結(jié)點(diǎn)的所有子結(jié)點(diǎn)都被選中,那么將父結(jié)點(diǎn)也選中 
   CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
   // 開始檢查pNode的所有子節(jié)點(diǎn)是否都被選中 
   if(pNode != null) 
   { 
    int index = 0; 
    for(; index < pNode.children.size(); ++ index) 
    { 
     CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index); 
     if(!pChildNode.isSelected()) 
      break; 
    } 
    /* 
     * 表明pNode所有子結(jié)點(diǎn)都已經(jīng)選中,則選中父結(jié)點(diǎn), 
     * 該方法是一個(gè)遞歸方法,因此在此不需要進(jìn)行迭代,因?yàn)?
     * 當(dāng)選中父結(jié)點(diǎn)后,父結(jié)點(diǎn)本身會(huì)向上檢查的。 
     */ 
    if(index == pNode.children.size()) 
    { 
     if(pNode.isSelected() != _isSelected) 
      pNode.setSelected(_isSelected); 
    } 
   } 
  } 
  else 
  { 
   /* 
    * 如果是取消父結(jié)點(diǎn)導(dǎo)致子結(jié)點(diǎn)取消,那么此時(shí)所有的子結(jié)點(diǎn)都應(yīng)該是選擇上的; 
    * 否則就是子結(jié)點(diǎn)取消導(dǎo)致父結(jié)點(diǎn)取消,然后父結(jié)點(diǎn)取消導(dǎo)致需要取消子結(jié)點(diǎn),但 
    * 是這時(shí)候是不需要取消子結(jié)點(diǎn)的。 
    */ 
   if(children != null) 
   { 
    int index = 0; 
    for(; index < children.size(); ++ index) 
    { 
     CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index); 
     if(!childNode.isSelected()) 
      break; 
    } 
    // 從上向下取消的時(shí)候 
    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); 
     } 
    } 
   } 
    
   // 向上取消,只要存在一個(gè)子節(jié)點(diǎn)不是選上的,那么父節(jié)點(diǎn)就不應(yīng)該被選上。 
   CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
   if(pNode != null && pNode.isSelected() != _isSelected) 
    pNode.setSelected(_isSelected); 
  } 
 } 
} 

第一個(gè)差異通過(guò)繼承DefaultMutableTreeNode定義CheckBoxTreeNode解決了,接下來(lái)需要解決第二個(gè)差異。第二個(gè)差異是外觀上的差異,JTree的每個(gè)結(jié)點(diǎn)是通過(guò)TreeCellRenderer進(jìn)行顯示的。為了解決第二個(gè)差異,我們定義一個(gè)新的類CheckBoxTreeCellRenderer,該類實(shí)現(xiàn)了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")); 
 } 
  
 /** 
  * 返回的是一個(gè)<code>JPanel</code>對(duì)象,該對(duì)象中包含一個(gè)<code>JCheckBox</code>對(duì)象 
  * 和一個(gè)<code>JLabel</code>對(duì)象。并且根據(jù)每個(gè)結(jié)點(diǎn)是否被選中來(lái)決定<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的實(shí)現(xiàn)中,getTreeCellRendererComponent方法返回的是JPanel,而不是像DefaultTreeCellRenderer那樣返回JLabel,因此JPanel中的JLabel無(wú)法對(duì)選中做出反應(yīng),因此我們重新實(shí)現(xiàn)了一個(gè)JLabel的子類CheckBoxTreeLabel,它可以對(duì)選中做出反應(yīng),其源代碼如下:

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; 
 } 
} 

通過(guò)定義CheckBoxTreeNode和CheckBoxTreeCellRenderer。我們解決了CheckBoxTree和JTree的兩個(gè)根本差異,但是還有一個(gè)細(xì)節(jié)問(wèn)題需要解決,就是CheckBoxTree可以響應(yīng)用戶事件決定是否選中某個(gè)結(jié)點(diǎn)。為此,我們?yōu)镃heckBoxTree添加一個(gè)響應(yīng)用戶鼠標(biāo)事件的監(jiān)聽器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所需要的所有組件都已經(jīng)完成了,接下來(lái)就是如何使用這些組件。下面給出了使用這些組件的源代碼:

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); 
 } 
} 

其執(zhí)行結(jié)果如下圖所示:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中TCP通信的實(shí)現(xiàn)方法詳解

    Java中TCP通信的實(shí)現(xiàn)方法詳解

    這篇文章主要給大家介紹了關(guān)于Java中TCP通信的實(shí)現(xiàn)方法,TCP通信能實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的數(shù)據(jù)交互,通信的兩端,要嚴(yán)格區(qū)分為客戶端(Client)與服務(wù)端(Server),需要的朋友可以參考下
    2023-09-09
  • 基于Spring監(jiān)聽Binlog日志的方法詳解

    基于Spring監(jiān)聽Binlog日志的方法詳解

    MySQL 的二進(jìn)制日志(binlog)有三種不同的格式,通常被稱為 binlog 模式,這三種模式分別是 Statement 模式、Row 模式和Mixed 模式,本文將給大家介紹如何基于Spring監(jiān)聽Binlog日志,需要的朋友可以參考下
    2024-09-09
  • Java泛型類型擦除

    Java泛型類型擦除

    這篇文章主要為大家詳細(xì)介紹了Java泛型類型擦除,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • Java橋梁設(shè)計(jì)模式優(yōu)雅地將抽象與實(shí)現(xiàn)分離

    Java橋梁設(shè)計(jì)模式優(yōu)雅地將抽象與實(shí)現(xiàn)分離

    Java橋接設(shè)計(jì)模式通過(guò)將抽象和實(shí)現(xiàn)分離,使得它們可以獨(dú)立地變化,從而實(shí)現(xiàn)更靈活的代碼結(jié)構(gòu)。它是一種優(yōu)雅的設(shè)計(jì)模式,適用于需要處理多個(gè)變化因素的復(fù)雜應(yīng)用程序
    2023-04-04
  • Java單例模式的知識(shí)點(diǎn)詳解

    Java單例模式的知識(shí)點(diǎn)詳解

    在本篇文章里小編給大家整理的是關(guān)于Java單例模式的知識(shí)點(diǎn)詳解,有興趣的朋友們可以學(xué)習(xí)參考下。
    2020-02-02
  • 封裝了一個(gè)Java數(shù)據(jù)庫(kù)訪問(wèn)管理類

    封裝了一個(gè)Java數(shù)據(jù)庫(kù)訪問(wèn)管理類

    剛剛試著用JDBC,仿著原來(lái)C#的寫法寫了這段代碼,自己覺得還是挺粗糙的,還煩請(qǐng)路過(guò)的朋友推薦一個(gè)寫得較好較完整的相關(guān)例程以便學(xué)習(xí)。謝謝!
    2009-02-02
  • Springboot?No?bean?named?'XXXXX'?available?問(wèn)題解決方法

    Springboot?No?bean?named?'XXXXX'?available?問(wèn)

    這篇文章主要介紹了Springboot?No?bean?named?'XXXXX'?available?問(wèn)題解決方法,解決方法也很簡(jiǎn)單,盡量規(guī)范類的命名,注解中指定bean名稱,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • 解決springboot responseentity<string>亂碼問(wèn)題

    解決springboot responseentity<string>亂碼問(wèn)題

    這篇文章主要介紹了解決springboot responseentity<string>亂碼問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java動(dòng)態(tài)顯示文件上傳進(jìn)度實(shí)現(xiàn)代碼

    Java動(dòng)態(tài)顯示文件上傳進(jìn)度實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了Java動(dòng)態(tài)顯示文件上傳進(jìn)度實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Spark JDBC操作MySQL方式詳細(xì)講解

    Spark JDBC操作MySQL方式詳細(xì)講解

    這篇文章主要介紹了Spark JDBC操作MySQL方式,Spark SQL可以通過(guò)JDBC從傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)中讀寫數(shù)據(jù),讀取數(shù)據(jù)后直接生成的是DataFrame,然后再加上借助于Spark SQL豐富的API來(lái)進(jìn)行各種操作
    2023-02-02

最新評(píng)論

饶阳县| 叙永县| 赣榆县| 瑞昌市| 祁连县| 黎城县| 宁南县| 辽源市| 扎赉特旗| 都安| 岐山县| 澄迈县| 武宣县| 开鲁县| 宁强县| 工布江达县| 宁夏| 文成县| 三明市| 永兴县| 上饶市| 法库县| 马公市| 灵武市| 永济市| 荆州市| 镇康县| 札达县| 德兴市| 邛崃市| 武宣县| 阿克| 阿拉善左旗| 东至县| 彭州市| 满城县| 佳木斯市| 彭山县| 凤凰县| 莒南县| 姜堰市|