เมื่อพัฒนาโปรแกรม UI โดยใช้ Java Swing คุณมีแนวโน้มที่จะต้องเผชิญกับความจำเป็นในการใช้ต้นไม้กับกล่องกาเครื่องหมาย แต่ Java Swing ไม่ได้ให้ส่วนประกอบนี้ดังนั้นหากคุณมีข้อกำหนดนี้คุณต้องใช้ต้นไม้พร้อมกล่องกาเครื่องหมายด้วยตัวเอง
มีความแตกต่างระหว่าง CheckBoxTree และ JTree ในสองระดับ:
1. บนเลเยอร์โมเดลแต่ละโหนดของ CheckBoxTree ต้องการสมาชิกเพื่อบันทึกว่ามีการเลือกหรือไม่ แต่โหนด jtree ไม่ได้
2. บนเลเยอร์มุมมองแต่ละโหนดของ CheckBoxTree จะแสดงกล่องกาเครื่องหมายอีกหนึ่งช่องกว่าโหนดของ JTree
เนื่องจากมีความแตกต่างสองประการตราบใดที่เราเติมความแตกต่างทั้งสองนี้ด้วยการใช้งานของเราเองต้นไม้ที่มีช่องทำเครื่องหมายจะถูกนำไปใช้
ตอนนี้เริ่มแก้ไขความแตกต่างแรก ในการแก้ปัญหาความแตกต่างแรกต้องมีการกำหนดช่องรับสัญญาณโหนดใหม่ซึ่งจะต้องมีการกำหนดค่าเริ่มต้น MutableTreenode และเพิ่มสมาชิกใหม่ที่ได้รับการคัดเลือกเพื่อระบุว่าโหนดถูกเลือกหรือไม่ สำหรับ CheckBoxTree หากเลือกโหนดกล่องกาเครื่องหมายจะถูกตรวจสอบและแรงจูงใจในการใช้ CheckBoxTree คือการเลือกทรีย่อยในครั้งเดียว จากนั้นเมื่อมีการเลือกหรือยกเลิกโหนดโหนดบรรพบุรุษและโหนดลูกหลานควรทำการเปลี่ยนแปลงบางอย่าง ที่นี่เราใช้กฎการเรียกซ้ำต่อไปนี้:
1. หากเลือกโหนดด้วยตนเองควรเลือกลูกหลานทั้งหมดของมัน หากเลือกโหนดเพื่อให้ลูกทุกคนของโหนดพาเรนต์ที่เลือกนั้นจะเลือกโหนดพาเรนต์ของมัน
2. หากโหนดไม่ถูกตรวจสอบด้วยตนเองลูกหลานทั้งหมดของมันควรไม่ถูกตรวจสอบ; หากโหนดพาเรนต์ของโหนดถูกเลือกโหนดพาเรนต์จะไม่ถูกตรวจสอบ
หมายเหตุ: กฎสองข้อข้างต้นเป็นกฎแบบเรียกซ้ำ เมื่อโหนดเปลี่ยนแปลงและทำให้โหนดอื่นเปลี่ยนโหนดอื่นจะทำให้โหนดอื่นเปลี่ยน ในสองกฎข้างต้นการเลือกด้วยตนเองหรือการยกเลิกการเลือกด้วยตนเองของโหนดจะทำให้เกิดการเลือกที่ไม่ใช่ด้วยตนเองหรือไม่เลือกโหนดอื่น ๆ การเลือกที่ไม่ใช่ด้วยตนเองนี้หรือไม่รวมการเลือกที่ไม่ใช่ด้วยตนเองนี้หรือการเลือกที่ไม่ได้เลือกจะไม่นำไปใช้กับกฎข้างต้น
ซอร์สโค้ดของ CheckBoxTreenode ที่ใช้งานตามกฎข้างต้นมีดังนี้:
การสาธิตแพ็คเกจ; นำเข้า Javax.swing.tree.defaultMutableTreenode; checkboxtreenode ระดับสาธารณะขยายการเริ่มต้น DefaultMutableTreenode {Boolean ที่ได้รับการป้องกันที่ได้รับการคัดเลือก; Public CheckBoxTreenode () {this (null); } Public CheckBoxTreenode (Object UserObject, True, False); } Public CheckBoxTreenode (Object UserObject, Boolean อนุญาตให้เด็ก, Boolean isselected) {super (userObject, อนุญาตให้เด็ก); this.isselected = isselected; } บูลีนสาธารณะ isselected () {return isselected; } โมฆะสาธารณะ setSelected (บูลีน _isselected) {this.isselected = _isselected; if (_isselected) {// ถ้าเลือกให้เลือกโหนดลูกทั้งหมดถ้า (เด็ก! = null) {สำหรับ (Object OBJ: เด็ก) {checkBoxTreenode node = (checkBoxTreenode) OBJ; if (_isselected! = node.isselected ()) node.setSelected (_isselected); }} // ตรวจสอบหากโหนดลูกทั้งหมดของโหนดพาเรนต์ถูกเลือกจากนั้นเลือกโหนด parent node checkBoxTreenode pnode = (checkBoxTreenode) พาเรนต์; // เริ่มตรวจสอบว่าโหนดลูกทั้งหมดของ pnode ถูกเลือกถ้า (pnode! = null) {int index = 0; สำหรับ (; index <pnode.children.size (); ++ ดัชนี) {checkboxTreenode pchildNode = (checkBoxTreenode) pnode.children.get (ดัชนี); if (! pchildnode.isselected ()) break; } / * * ระบุว่ามีการเลือกโหนดลูกทั้งหมดของ pNode แล้วเลือกโหนดพาเรนต์ * วิธีนี้เป็นวิธีการเรียกซ้ำดังนั้นจึงไม่จำเป็นต้องวนซ้ำที่นี่เพราะ * เมื่อเลือกโหนดพาเรนต์โหนดโหนดพาเรนต์เองจะตรวจสอบขึ้น */ if (index == pnode.children.size ()) {ถ้า (pnode.isselected ()! = _isselected) pnode.setSelected (_isselected); }}} else { / * * หากการยกเลิกโหนดพาเรนต์ทำให้โหนดลูกถูกยกเลิกแล้วโหนดลูกทั้งหมดควรถูกเลือกในเวลานี้ * มิฉะนั้นการยกเลิกโหนดลูกจะทำให้โหนดพาเรนต์ถูกยกเลิกและจากนั้นการยกเลิกโหนดพาเรนต์จะทำให้โหนดลูกถูกยกเลิก แต่ * ไม่จำเป็นต้องยกเลิกโหนดลูกในเวลานี้ */ ถ้า (เด็ก! = null) {int index = 0; สำหรับ (; index <children.size (); ++ ดัชนี) {checkboxTreenode childNode = (checkboxTreenode) เด็ก. gild (ดัชนี); if (! childNode.isselected ()) break; } // เมื่อยกเลิกจากบนลงล่างถ้า (index == children.size ()) {สำหรับ (int i = 0; i <children.size (); ++ i) {checkboxTreenode node = (checkboxTreenode) เด็ก. get (i); if (node.isselected ()! = _isselected) node.setSelected (_isselected); }}} // ยกเลิกตราบใดที่มีโหนดลูกที่ไม่ได้เลือกโหนดพาเรนต์ไม่ควรเลือก checkboxTreenode pnode = (checkboxTreenode) พาเรนต์; if (pnode! = null && pnode.isselected ()! = _isselected) pnode.setSelected (_isselected); - ความแตกต่างแรกได้รับการแก้ไขโดยการสืบทอด DefaultMutableTreEnode CheckBoxTreenode และความแตกต่างที่สองจะต้องได้รับการแก้ไขต่อไป ความแตกต่างที่สองคือความแตกต่างของลักษณะที่ปรากฏและแต่ละโหนดของ jtree จะแสดงผ่าน treecellrenderer ในการแก้ไขความแตกต่างที่สองเราได้กำหนด checkboxtreecellrenderer คลาสใหม่ที่ใช้อินเตอร์เฟส treecellrenderer ซอร์สโค้ดของ CheckBoxTreerenderer มีดังนี้:
การสาธิตแพ็คเกจ; นำเข้า Java.awt.Color; นำเข้า Java.awt.Component; นำเข้า Java.awt.Dimension; นำเข้า Javax.swing.jcheckbox; นำเข้า Javax.swing.jpanel; นำเข้า Javax.swing.jtree; นำเข้า Javax.swing.uimanager; นำเข้า javax.swing.plaf.coloruiresource; นำเข้า Javax.swing.tree.treeecellrenderer; checkboxtreecellrenderer ระดับสาธารณะขยาย JPanel ใช้ treecellrenderer {ป้องกัน jcheckbox ตรวจสอบ; ฉลาก checkboxtreelabel ที่ได้รับการป้องกัน publicboxtreecellrenderer () {setlayout (null); เพิ่ม (ตรวจสอบ = new JCheckBox ()); เพิ่ม (label = new CheckBoxTreelabel ()); check.setbackground (uimanager.getColor ("tree.textbackground")); label.setforeground (uimanager.getColor ("tree.textforeground")); }/*** ส่งคืนวัตถุ <code> jPanel </code> ซึ่งมีวัตถุ <code> jcheckbox </code> วัตถุ* และ A <code> jlabel </code> วัตถุ และตัดสินใจว่า <code> jcheckbox </code> * ถูกเลือกขึ้นอยู่กับว่าแต่ละโหนดถูกเลือกหรือไม่ */ @Override ส่วนประกอบสาธารณะ GetTreecellRendererComponent (JTree Tree, ค่าวัตถุ, บูลีนที่เลือก, บูลีนขยาย, ใบบูลีน, แถว int, boolean hasfocus) {string StringValue = tree.ConvertValuetotext setEnabled (tree.isenabled ()); check.setSelected ((checkBoxTreenode) ค่า) .Seselected ()); label.setFont (tree.getFont ()); label.settext (StringValue); label.setSelected (เลือก); label.setFocus (hasfocus); ถ้า (ใบไม้) label.seticon (uimanager.geticon ("tree.leaficon")); อื่นถ้า (ขยาย) label.seticon (uimanager.geticon ("tree.openicon")); label.seticon (uimanager.geticon ("tree.closedicon")); คืนสิ่งนี้; } @Override มิติสาธารณะ getPreferredSize () {Dimension dcheck = check.getPreverredSize (); DIMESSION DLABEL = label.getPreferredSize (); ส่งคืนมิติใหม่ (dcheck.width + dlabel.width, dcheck.height <dlabel.height? dlabel.height: dcheck.height); } @Override โมฆะสาธารณะ dolayout () {Dimension dcheck = check.getPreverredSize (); DIMESSION 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 โมฆะสาธารณะ setBackground (สีสี) {ถ้า (อินสแตนซ์สีของ coloruiresource) color = null; super.setbackground (สี); - ในการใช้งาน CheckBoxTreeCellRenderer วิธีการ GetTreecellRendererComponent จะส่งคืน JPanel แทนที่จะส่งคืน JLabel เช่น DefaultTreeecellRenderer ดังนั้น JLabel ใน JPanel จึงไม่สามารถตอบสนองต่อการเลือกได้ ดังนั้นเราจะทำการปรับแต่งคลาสย่อยของ jlabel checkboxtreelabel ใหม่ซึ่งสามารถตอบสนองต่อการเลือกและซอร์สโค้ดของมันมีดังนี้:
การสาธิตแพ็คเกจ; นำเข้า Java.awt.Color; นำเข้า Java.awt.Dimension; นำเข้า java.awt.graphics; นำเข้า Javax.swing.icon; นำเข้า Javax.swing.jlabel; นำเข้า Javax.swing.uimanager; นำเข้า javax.swing.plaf.coloruiresource; checkboxtreelabel คลาสสาธารณะขยาย jlabel {private boolean isselected; บูลีนส่วนตัว Hasfocus; publicboxtreelabel () {} @Override public void setbackground (สีสี) {ถ้า (อินสแตนซ์สีของ coloruiresource) color = null; super.setbackground (สี); } @Override Public Void Paint (Graphics G) {String Str; if ((str = getText ())! = null) {if (0 <str.length ()) {ถ้า (isselected) g.setColor (uimanager.getColor ("tree.selectionBackground")); อื่น ๆ g.setColor (uimanager.getColor ("tree.textbackground")); Dimension D = getPreferredSize (); int imageOffset = 0; ไอคอนปัจจุบัน = geticon (); ถ้า (ปัจจุบัน! = null) imageOffset = presidenticon.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 มิติสาธารณะ getPreferredSize () {มิติ retdimension = super.getPreverredSize (); if (retdimension! = null) retdimension = มิติใหม่ (retdimension.width + 3, retdimension.height); ส่งคืน retdimension; } โมฆะสาธารณะ SetSelected (Boolean ISSELECTED) {this.isselected = isselected; } โมฆะสาธารณะ setFocus (บูลีน hasfocus) {this.hasfocus = hasfocus; - โดยการกำหนด checkboxtreenode และ checkboxtreecellrenderer เราแก้ไขความแตกต่างพื้นฐานสองประการระหว่าง CheckBoxTree และ JTree แต่ยังมีปัญหารายละเอียดที่ต้องแก้ไขนั่นคือ CheckBoxTree สามารถตัดสินใจได้ว่าจะเลือกโหนดบางอย่างเพื่อตอบสนองต่อเหตุการณ์ผู้ใช้หรือไม่ ในการทำเช่นนี้เราเพิ่มเครื่องฟัง CheckBoxTree ที่ตอบสนองต่อเหตุการณ์เมาส์ผู้ใช้ ซอร์สโค้ดของคลาสนี้มีดังนี้:
การสาธิตแพ็คเกจ; นำเข้า java.awt.event.mouseadapter; นำเข้า java.awt.event.mousevent; นำเข้า Javax.swing.jtree; นำเข้า javax.swing.tree.treepath; นำเข้า Javax.swing.tree.defaultTreemodel; checkboxtreenodeselectionListener ขยายชั้นเรียนขยาย Mouseadapter {@Override โมฆะสาธารณะ mouseclicked (เหตุการณ์ MouseEvent) {JTree Tree = (JTree) Event.getSource (); int x = event.getx (); int y = event.gety (); int row = tree.getRowForLocation (x, y); Treepath Path = tree.getPathForrow (แถว); if (path! = null) {checkBoxTreenode node = (checkBoxTreenode) path.getLastPathComponent (); if (node! = null) {boolean isselected =! node.isselected (); Node.SetSelected (ISSELECTED); ((defaultTreemodel) tree.getModel ()). nodestructurechanged (โหนด); - จนถึงตอนนี้ส่วนประกอบทั้งหมดที่ CheckboxTree ต้องการเสร็จสมบูรณ์และขั้นตอนต่อไปคือวิธีการใช้งาน ซอร์สโค้ดสำหรับการใช้ส่วนประกอบเหล่านี้ได้รับด้านล่าง:
การสาธิตแพ็คเกจ; นำเข้า Javax.swing.jframe; นำเข้า Javax.swing.jscrollpane; นำเข้า Javax.swing.jtree; นำเข้า Javax.swing.tree.defaultTreemodel; Demomain คลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] 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 = ใหม่ 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 (checkboxtreenodeselectionListener () ใหม่ (); tree.setModel (รุ่น); tree.setCellRenderer (CheckBoxTreeCellRenderer () ใหม่ ()); jscrollpane scroll = new jscrollpane (ต้นไม้); Scroll.SetBounds (0, 0, 300, 320); frame.getContentPane (). เพิ่ม (เลื่อน); frame.setDefaultCloseoperation (jframe.exit_on_close); frame.setVisible (จริง); -ผลการดำเนินการแสดงในรูปด้านล่าง:
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น