عند تطوير برامج واجهة المستخدم باستخدام Java Swing ، من المحتمل أن تواجه الحاجة إلى استخدام شجرة مع خانات الاختيار ، ولكن Java Swing لا يوفر هذا المكون ، لذلك إذا كان لديك هذا المتطلبات ، فيجب عليك تنفيذ شجرة مع مربعات الاختيار بنفسك.
هناك اختلافات بين checkstree و Jtree على مستويين:
1. على طبقة النموذج ، تحتاج كل عقدة من checkstree إلى عضو لحفظ ما إذا كان قد تم تحديده ، لكن عقد Jtree لا تفعل ذلك.
2. على طبقة العرض ، تعرض كل عقدة من checkstree خانة اختيار واحدة أكثر من عقدة Jtree.
نظرًا لوجود اختلافان ، طالما أننا نملأ هذين الاختلافين بتنفيذنا الخاص ، سيتم تنفيذ الشجرة مع خانات الاختيار.
ابدأ الآن في حل الفرق الأول. لحل الاختلاف الأول ، يجب تحديد مربعات الاختيار من فئة العقدة الجديدة ، والتي ترث DefaultMutableTreenode وتضيف عضوًا جديدًا تم تحديده للإشارة إلى ما إذا كانت العقدة قد تم تحديدها. بالنسبة إلى مربع الاختيار ، إذا تم تحديد عقدة ، فسيتم التحقق من خانة الاختيار الخاصة بها ، والتحفيز لاستخدام CheckStree هو تحديد الشجرة الفرعية مرة واحدة. بعد ذلك ، عند تحديد عقدة أو إلغاؤها ، يجب أن تقوم عقدة أجدادها وعقدة سليلها ببعض التغييرات. هنا ، نطبق القواعد العودية التالية:
1. إذا تم اختيار العقدة يدويًا ، فيجب اختيار جميع نسلها ؛ إذا تم تحديد العقدة لجعل جميع الأطفال من العقدة الأم الخاصة بها محددة ، فسيتم تحديد العقدة الأم الخاصة بها.
2. إذا لم يتم فحص العقدة يدويًا ، فيجب عدم رادع جميع أحفادها ؛ إذا تم تحديد عقدة الأصل للعقدة ، فلا يتم فحص العقدة الأم.
ملاحظة: القواعد المذكورة أعلاه هي القواعد العودية. عندما تتغير العقدة وتتسبب في تغيير عقدة أخرى ، فإن عقدة أخرى ستتسبب أيضًا في تغيير العقد الأخرى. في قاعدتين أعلاه ، فإن الاختيار اليدوي أو إلغاء تحديد اليدوي للعقدة سيؤدي إلى اختيار غير يدوي أو إلغاء تحديد العقد الأخرى. لن ينطبق هذا الاختيار غير اليدوي أو عدم تحديد هذا الاختيار غير اليدوي أو عدم تحديده على القواعد المذكورة أعلاه.
رمز المصدر لـ checkstreeNode الذي تم تنفيذه وفقًا للقواعد المذكورة أعلاه هو كما يلي:
حزمة العرض التوضيحي ؛ استيراد javax.swing.tree.defaultMutableTreenode ؛ الطبقة العامة chectboxtreeNode يمتد DefaultMutableTreenode {محمية Boolean Isselected ؛ public checkstreeNode () {this (null) ؛ } public checkstreeNode (Object userObject ، true ، false) ؛ } chectBoxTreeNode (Object userObject ، Boolean يتيح للأطفال ، isselected المنطقي) {super (userObject ، السماح للأطفال) ؛ this.isselected = isselected ؛ ) } public void setSelected (boolean _isselected) {this.isselected = _isselected ؛ if (_isselected) {// إذا تم تحديدها ، حدد جميع العقد الفرعية إذا (الأطفال! = null) {for (object obj: children) {checkstreeNode node = (checkstreeNode) obj ؛ if (_isselected! = node.isselected ()) node.setselected (_isselected) ؛ }} // تحقق من ، إذا تم تحديد جميع العقد الفرعية للعقدة الأصل ، فحدد الأصل chicktreeNode pnode = (chectBoxTreeNode) parent ؛ // ابدأ في التحقق مما إذا كانت جميع العقد الفرعية في pNode يتم تحديدها إذا (pnode! = null) {int index = 0 ؛ لـ (؛ index <pnode.children.size () ؛ ++ index) {chectBoxTreeNode pChildNode = (checkstreenode) pnode.children.get (index) ؛ إذا (! pchildnode.isselected ()) استراحة ؛ } / * * يشير إلى أنه تم تحديد جميع العقد الفرعية في PNODE ، يتم تحديد العقدة الأصل. * هذه الطريقة هي طريقة متكررة ، لذلك ليست هناك حاجة للتكرار هنا ، لأنه * عند تحديد العقدة الأصل ، ستتحقق العقدة الأصل نفسها لأعلى. */ if (index == pnode.children.size ()) {if (pnode.isselected ()! = _isselected) pnode.setselected (_isselected) ؛ }}} آخر { / * * إذا كان إلغاء العقدة الأصل يؤدي إلى إلغاء عقدة الطفل ، فيجب تحديد جميع العقد الفرعية في هذا الوقت ؛ * خلاف ذلك ، فإن إلغاء العقدة الطفل سيؤدي إلى إلغاء العقدة الأصل ، ثم سيؤدي إلغاء العقدة الأصل إلى إلغاء عقدة الطفل ، ولكن * ليس ضروريًا لإلغاء العقدة الفرعية في هذا الوقت. */ if (الأطفال! = null) {int index = 0 ؛ لـ (؛ index <children.size () ؛ ++ index) {chectBoxTreeNode childnode = (checkboxtreeNode) childres.get (index) ؛ if (! childnode.isselected ()) استراحة ؛ } // عند الإلغاء من الأعلى إلى الأسفل إذا (index == children.size ()) {for (int i = 0 ؛ i <Kids.size () ؛ ++ i) {chectBoxTreeNode node = (chectBoxTreeNode) childrs.get (i) ؛ if (node.isselected ()! = _isselected) node.setselected (_isselected) ؛ }}} // إلغاء ، طالما أن هناك عقدة طفل غير محددة ، يجب عدم تحديد العقدة الأصل. CheckStreeNode pNode = (checkstreeNode) Parent ؛ if (pnode! = null && pnode.isselected ()! = _isselected) pnode.setselected (_isselected) ؛ }}} يتم حل الفرق الأول عن طريق وراثة DefaultMutAbleTreenode تعريف checkstreeNode ، ويجب حل الفرق الثاني بعد ذلك. الفرق الثاني هو الفرق في المظهر ، ويتم عرض كل عقدة من Jtree من خلال TreecellRenderer. لحل الاختلاف الثاني ، نحدد مجموعة جديدة من checktreecellrenderer التي تنفذ واجهة treecellrenderer. رمز المصدر لـ CheckStreERENDERER هو كما يلي:
حزمة العرض التوضيحي ؛ استيراد 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.treecellrenderer ؛ الطبقة العامة chectboxtreecellrenderer يمتد jpanel الأدوات treecellrenderer {محمية jcheckbox check ؛ تسمية chectBoxTreelabel المحمية ؛ public checkstreecellRenderer () {setLayout (null) ؛ إضافة (check = new jCheckbox ()) ؛ إضافة (label = new chectboxtreelabel ()) ؛ 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 ، قيمة الكائن ، المحدد المنطقي ، الموسع المنطقي ، الأوراق المنطقية ، الصف int ، boolean hasfocus) {StringValue = tree.convertvaluetOteText (القيمة ، تم اختياره ، موسع ، ورقة ، صف ، hasfocus) ؛ setenabled (tree.isenabled ()) ؛ check.SetSelected (((chectBoxTreeNode) value) .isselected ()) ؛ label.setfont (tree.getfont ()) ؛ label.settext (StringValue) ؛ label.SetSelected (محدد) ؛ label.setFocus (hasfocus) ؛ if (Leaf) label.seticon (uimanager.geticon ("tree.leaficon")) ؛ وإلا else label.seticon (uimanager.geticon ("tree.closedicon")) ؛ إرجاع هذا ؛ } Override الأبعاد العامة getPreferRedSize () {dimension dcheck = check.getPreferRedSize () ؛ Dimension dlabel = label.getPreferRedSize () ؛ إرجاع البعد الجديد (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 (لون اللون) {if (مثيل اللون من coloruiresource) color = null ؛ Super.SetBackground (اللون) ؛ }} في تنفيذ CheckStreecellRenderer ، تُرجع طريقة getTreecellRendererComponent jpanel ، بدلاً من إرجاع JLabel مثل DefaultTreecellRenderer. لذلك ، لا يمكن أن يتفاعل JLabel في JPanel مع الاختيار. لذلك ، قمنا بإعداد فئة فرعية من JLabel CheckSteLabel ، والتي يمكن أن تتفاعل مع التحديد ، كما يلي الرمز المصدر الخاص به:
حزمة العرض التوضيحي ؛ استيراد java.awt.color ؛ استيراد java.awt.dimension ؛ استيراد java.awt.graphics ؛ استيراد javax.swing.icon ؛ استيراد javax.swing.jlabel ؛ استيراد javax.swing.uimanager ؛ استيراد javax.swing.plaf.ColorUiresource ؛ الطبقة العامة chectboxtreelabel يمتد Jlabel {private boolean isselected ؛ hecfocus المنطقية الخاصة ؛ public checkstrielabel () {} Override public void setbackground (color color) {if (color exature of coloruiresource) color = null ؛ Super.SetBackground (اللون) ؛ } Override public void paint (Graphics g) {String str ؛ if ((str = getText ())! = null) {if (0 <str.length ()) {if (isselected) g.setColor (uimanager.getColor ("tree.selectebackground")) ؛ else g.setColor (uimanager.getColor ("tree.textbackground")) ؛ البعد D = getPreferredSize () ؛ int imageoffset = 0 ؛ أيقونة 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 البعد العام getPreferRedSize () {dimension retDimension = super.getPreferRedSize () ؛ إذا (retDimension! = null) retDimension = البعد الجديد (retDimension.width + 3 ، retdimension.hight) ؛ إرجاع Retdimension ؛ } public void setSelected (boolean isselected) {this.isselected = isselected ؛ } public void setfocus (boolean hasfocus) {this.hasfocus = hasfocus ؛ }} عن طريق تحديد checkstreeNode و CheckStreecellRenderer. لقد حلنا الاختلافين الأساسيين بين CheckStree و Jtree ، ولكن لا تزال هناك مشكلة مفصلة تحتاج إلى حل ، أي أن CheckStree يمكن أن يقرر ما إذا كان سيتم اختيار عقدة معينة استجابةً لأحداث المستخدم. للقيام بذلك ، نضيف مستمع CheckStree الذي يستجيب لأحداث ماوس المستخدم. الكود المصدري لهذه الفئة هو كما يلي:
حزمة العرض التوضيحي ؛ استيراد java.awt.event.mouseadapter ؛ استيراد java.awt.event.mouseevent ؛ استيراد javax.swing.jtree ؛ استيراد javax.swing.tree.treepath ؛ استيراد javax.swing.tree.defaulttreemodel ؛ الطبقة العامة chectBoxTreeNodesElectionListener تمدد mouseadapter {Override public void 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 (row) ؛ if (path! = null) {chectBoxTreeNode node = (checkboxtreeNode) path.getLastPathComponent () ؛ if (node! = null) {boolean isselected =! node.isselected () ؛ node.setsected (isselected) ؛ ((DefaultTreemodel) tree.getModel ()). nodestructurechanged (العقدة) ؛ }}}}} حتى الآن ، تم الانتهاء من جميع المكونات المطلوبة من قبل CheckStree ، والخطوة التالية هي كيفية استخدامها. فيما يلي رمز المصدر لاستخدام هذه المكونات:
حزمة العرض التوضيحي ؛ استيراد javax.swing.jframe ؛ استيراد javax.swing.jscrollpane ؛ استيراد javax.swing.jtree ؛ استيراد javax.swing.tree.defaulttreemodel ؛ الفئة العامة demomain {public static void main (string [] args) {jframe frame = new JFrame ("checkboxtreedemo") ؛ Frame.SetBounds (200 ، 200 ، 400 ، 400) ؛ Jtree Tree = New Jtree () ؛ chectBoxTreeNode rootnode = جديد checkstreeNode ("root") ؛ chectBoxTreeNode Node1 = جديد checkstreeNode ("node_1") ؛ checkstreeNode node1_1 = جديد chectBoxTreeNode ("node_1_1") ؛ chectBoxTreeNode node1_2 = new chectBoxTreeNode ("node_1_2") ؛ checkstreeNode node1_3 = جديد chectBoxTreeNode ("node_1_3") ؛ node1.add (node1_1) ؛ node1.add (node1_2) ؛ node1.add (node1_3) ؛ chectBoxTreeNode Node2 = جديد chectBoxTreeNode ("node_2") ؛ chectBoxTreeNode node2_1 = جديد chectBoxTreeNode ("node_2_1") ؛ chectBoxTreeNode node2_2 = جديد chectBoxTreeNode ("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 ChectBoxTreeNodesElectionListener ()) ؛ tree.setModel (نموذج) ؛ Tree.setCellRenderer (جديد chectBoxTreecellRenderer ()) ؛ jscrollpane scroll = new jscrollpane (tree) ؛ Scroll.setBounds (0 ، 0 ، 300 ، 320) ؛ frame.getContentPane (). add (scroll) ؛ frame.setDefaultCloseOperation (jframe.exit_on_close) ؛ frame.setVisible (صحيح) ؛ }}تظهر نتائج التنفيذ في الشكل أدناه:
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.