项目历程—右键菜单(问题,解决,拓展(非教学向,因为乱))

java 复制代码
public class UI {
    UIListener ul=new UIListener();

    public void showUI(){ 
      

        JFrame jf=new JFrame();
        jf.setLocation(400,400);
        jf.setSize(400,500);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setTitle("右键菜单");

        JPopupMenu jpm=new JPopupMenu();
        JMenuItem jpm1=new JMenuItem("删除");
        JMenuItem jpm2=new JMenuItem("添加");
        JMenuItem jpm3=new JMenuItem("打开");
        jpm.add(jpm1);
        jpm.add(jpm2);
        jpm.add(jpm3);
        JPanel panel=new JPanel();
        panel.setComponentPopupMenu(jpm);
        jf.add(panel);
 
       
        jpm1.addActionListener(ul);
        jf.addMouseListener(ul);
       
       

        jf.setVisible(true);
        



    }

    public static void main(String[] args) {
        UI ui=new UI();
        ui.showUI();
    }
}

问题:

明明在鼠标监听器的左键加入了输出"删除被点击"的代码,可在点击"打开"时没有附带着输出鼠标监听器的代码

答案:

JMenuItem在鼠标点击操作会侧重于动作监听器,因此对于鼠标监听器中的代码可能会有执行不完整的情况

拓展:

(1)在GUI编程里,鼠标事件是按照一定路径传播的,从最内层到最外层,例如从JMenuItem一直到JFrame。动作事件依赖鼠标事件的完成,因此需要先完成鼠标事件,再完成动作事件,并且动作事件不会随着鼠标事件一级一级向上传播,只在JMenuItem这一级被处理

(2)当在选择菜单项时,会先在JMenuItem这一层触发鼠标事件,然后向上传播至JFrame,并被鼠标监听器监听并执行代码,然后才被JMenuItem这一层监听并执行动作监听器中的代码。

java 复制代码
JButton btn=new JButton();
btn.getName();
btn.getText();

getName()和getText()方法的区别:getName方法是获取该组件的名称,例如会得到JButton或者JComponent等,但不是获取按钮的文本内容,getText才是获取按钮的文本内容


自己实操代码出现问题,如果新建的文件夹名与动作监听器的名字一致,例如public class ActionListener implements ActionListener ,就会报出Cyclic Inheritence involving(类名)这样的编译错误,因此文件类名不能与接口名一致

目的:在窗口中加上一个右键菜单,无论是按钮还是标签还是面板本身,都能够得到其对应的菜单,并且点击对应的的菜单选项时,都能得到与其相连的组件的名称

实操:

(1):创建一个窗口,先加入面板,再往面板中添加按钮和标签,因为后续如果想将菜单和组件进行绑定,不能并且无法在JFrame中进行绑定,因此先往窗口中填充面板。填充完面板,按钮和标签后,才分别创建各自的菜单,与之绑定。为了实现点击菜单后有反应,再创建一个动作监听器,把每个菜单都与动作监听器绑定,这样一来,三部分的菜单所绑定的都是一个监听器。

java 复制代码
package OwnFileManager;

import NewFileManager.PMActionListener;

import javax.swing.*;

public class UI {
    PIListener pil=new PIListener();
    public void showUI(){
        JFrame jf=new JFrame();
        JPanel jPanel=new JPanel();

        jf.setTitle("右键菜单");
        jf.setLocation(600,200);
        jf.setSize(400,400);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton btn=new JButton("photos");
        JLabel jla=new JLabel("img");
        jPanel.add(btn);
        jPanel.add(jla);
        jf.add(jPanel);

        JPopupMenu jpm1=new JPopupMenu();
        JMenuItem jpm11=new JMenuItem("刷新");
        JMenuItem jpm12=new JMenuItem("新建文件夹");
        JMenuItem jpm13=new JMenuItem("新建文件");
        jpm11.addActionListener(pil);
        jpm12.addActionListener(pil);
        jpm13.addActionListener(pil);
        jpm1.add(jpm11);
        jpm1.add(jpm12);
        jpm1.add(jpm13);

        jPanel.setComponentPopupMenu(jpm1);

        JPopupMenu jpm2=new JPopupMenu();
        JMenuItem jpm21=new JMenuItem("打开");
        JMenuItem jpm22=new JMenuItem("删除");
        JMenuItem jpm23=new JMenuItem("润色");
        jpm21.addActionListener(pil);
        jpm22.addActionListener(pil);
        jpm23.addActionListener(pil);
        jpm2.add(jpm21);
        jpm2.add(jpm22);
        jpm2.add(jpm23);

        btn.setComponentPopupMenu(jpm2);

        JPopupMenu jpm3=new JPopupMenu();
        JMenuItem jpm31=new JMenuItem("删除标签");
        JMenuItem jpm32=new JMenuItem("新建标签");
        JMenuItem jpm33=new JMenuItem("修改标签");
        jpm31.addActionListener(pil);
        jpm32.addActionListener(pil);
        jpm33.addActionListener(pil);
        jpm3.add(jpm31);
        jpm3.add(jpm32);
        jpm3.add(jpm33);
        jla.setComponentPopupMenu(jpm3);




        jf.setVisible(true);
    }

    public static void main(String[] args) {
        UI ui=new UI();
        ui.showUI();;
    }
}
java 复制代码
package OwnFileManager;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class PIListener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e) {
        String a=e.getActionCommand();
        System.out.println(a+"被点击");
    }
}

至此,一个简单的右键菜单的创建监听就完成了,下面想要实现:在每一次点击菜单中某个选项时,也能获取到所属组件的名称,因此就像宇哥说的那样,每一次点击时都能知道所属组件是什么。所以说我们就不能仅仅设一个动作监听器了,我们需要在点击按钮/标签/面板的时候就需要知道事件源是什么。那么我们就需要设置一个鼠标监听器,并根据右键的事件源的不同创建处不同的菜单,但是鼠标监听器本身无法区别左键右键,而我们是希望通过右键的时候得到菜单,那么我们需要得到鼠标点击的类型是左键还是右键,并且在右键的条件中,创设一个事件源,强制转化为JComPonent类型,并根据事件源的不同创建菜单

java 复制代码
package OwnFileManager;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Type;

public class PMListener implements MouseListener {
    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {
        int TypeKey= e.getButton();
        if(TypeKey == MouseEvent.BUTTON1){

        }else if(TypeKey==MouseEvent.BUTTON3){
            JComponent source=(JComponent) e.getSource();
            if(source instanceof  JButton btn){
                JPopupMenu jpm2=new JPopupMenu();
                JMenuItem jpm21=new JMenuItem("打开");
                JMenuItem jpm22=new JMenuItem("删除");
                JMenuItem jpm23=new JMenuItem("润色");

//                jpm21.addActionListener(pil);
//                jpm22.addActionListener(pil);
//                jpm23.addActionListener(pil);
                jpm2.add(jpm21);
                jpm2.add(jpm22);
                jpm2.add(jpm23);

                btn.setComponentPopupMenu(jpm2);
            }else if(source instanceof  JLabel jla){
                JPopupMenu jpm3=new JPopupMenu();
                JMenuItem jpm31=new JMenuItem("删除标签");
                JMenuItem jpm32=new JMenuItem("新建标签");
                JMenuItem jpm33=new JMenuItem("修改标签");
//                jpm31.addActionListener(pil);
//                jpm32.addActionListener(pil);
//                jpm33.addActionListener(pil);
                jpm3.add(jpm31);
                jpm3.add(jpm32);
                jpm3.add(jpm33);
                jla.setComponentPopupMenu(jpm3);

            }

        }

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}
java 复制代码
package OwnFileManager;

import NewFileManager.PMActionListener;

import javax.swing.*;

public class UI {
    PIListener pil=new PIListener();
    public void showUI(){
        JFrame jf=new JFrame();
        JPanel jPanel=new JPanel();

        jf.setTitle("右键菜单");
        jf.setLocation(600,200);
        jf.setSize(400,400);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton btn=new JButton("photos");
        JLabel jla=new JLabel("img");
        jPanel.add(btn);
        jPanel.add(jla);
        jf.add(jPanel);

        JPopupMenu jpm1=new JPopupMenu();
        JMenuItem jpm11=new JMenuItem("刷新");
        JMenuItem jpm12=new JMenuItem("新建文件夹");
        JMenuItem jpm13=new JMenuItem("新建文件");
        jpm11.addActionListener(pil);
        jpm12.addActionListener(pil);
        jpm13.addActionListener(pil);
        jpm1.add(jpm11);
        jpm1.add(jpm12);
        jpm1.add(jpm13);

        jPanel.setComponentPopupMenu(jpm1);








        jf.setVisible(true);
    }

    public static void main(String[] args) {
        UI ui=new UI();
        ui.showUI();;
    }
}

然后我们此时会发现,右键按钮时得不到菜单,一开始我还奇怪为什么同样是setComponent,JPanel就可以,JButoon和JLabel就不行,还在想是不是鼠标监听器的方法写的有问题,后来发现忘给btn和jla加监听器了,尽管我在监听器中写了右击按钮和标签之后会发生什么,但如果不给按钮或者标签添加监听器,右击按钮,监听器是听不到的。

添加完监听器后,可以得到对应菜单,此时产生了新的需求,点击菜单项后,我们想要得到所绑定的组件的文本名称,接下来我们有一个思路,那就是在点击按钮/标签/面板时,就能得到对应类型的监听器去专门监听菜单,例如点击了按钮,就让按钮监听器监听他的菜单,点击标签就让标签监听器监听它的菜单。我们固然无法改变动作监听器的种类,但我们可以通过传入参数的不同来确定到底该监听那个。传入参数后,发现参数部分报错,于是我们就要在动作监听器中添加构造方法,传入的是组件的类型,并将该参数赋给本类的成员变量,于是就需要在外面再设一个成员变量。至此得到了组件的种类,然后我们就可以在下方的动作监听器中添加判断,如果组件类型为jpanel,就。。。。,如果组件类型为JButton,就。。。。

注意:

(1)可以在动作监听器中将JButton,JLabel和JPanel进行类型判断,但不能在鼠标监听器中进行JButton,JLabel和JPanel进行类型判断,因为那样会导致事件源的判断有误,从而出现类型转换异常问题。

(2)进行组件和菜单的绑定时,首选show(组件变量名,e.getX(),e.getY())和setComponentMenu()方法,不推荐setLocation(组件变量名.getLocationopOnScreen().x+e.getX(),组件变量名.getLocationOnScreen().Y+e.getY()

)

set.Visible(true);方法,因为这个方法点击菜单项后不仅不能关闭,重复点击组件后之前的菜单还不会消失

完整代码如下:

java 复制代码
package OwnFileManager;

import NewFileManager.PMActionListener;

import javax.swing.*;

public class UI {
    PMListener pml=new PMListener();

    public void showUI(){
        JFrame jf=new JFrame();
        JPanel jPanel=new JPanel();

        jf.setTitle("右键菜单");
        jf.setLocation(600,200);
        jf.setSize(400,400);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton btn=new JButton("photos");
        JLabel jla=new JLabel("img");
        btn.addMouseListener(pml);
        jla.addMouseListener(pml);
        jPanel.add(btn);
        jPanel.add(jla);
        jf.add(jPanel);

        //接下来实现点击菜单可以得到组件的名称,以箭头指向组件名,例如"删除->photos":
        //为了实现这点,我们可以有这样的思路,当我右键按钮或者标签时,就把监听菜单的监听器类型改成对应组件的类型,例如右键按钮时就知道是在监听按钮菜单,右键标签就知道是在监听标签菜单。因此当获取事件源后,就应在各自的鼠标监听器中创建一个动作监听器,得到其类型。
        //所以需要根据传入参数的不同来区分监听器。

        JPopupMenu jpm1=new JPopupMenu();
        JMenuItem jpm11=new JMenuItem("刷新");
        JMenuItem jpm12=new JMenuItem("新建文件夹");
        JMenuItem jpm13=new JMenuItem("新建文件");
        PIListener pil=new PIListener(jPanel);
        jpm11.addActionListener(pil);
        jpm12.addActionListener(pil);
        jpm13.addActionListener(pil);
        jpm1.add(jpm11);
        jpm1.add(jpm12);
        jpm1.add(jpm13);

        jPanel.setComponentPopupMenu(jpm1);


        jf.setVisible(true);
    }

    public static void main(String[] args) {
        UI ui=new UI();
        ui.showUI();;
    }
}
java 复制代码
package OwnFileManager;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Type;

public class PMListener implements MouseListener {
    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {



        int TypeKey= e.getButton();
        if(TypeKey == MouseEvent.BUTTON1){

        }else if(TypeKey==MouseEvent.BUTTON3){
            JComponent source=(JComponent) e.getSource();

            if(source instanceof  JButton btn){
                PIListener pil=new PIListener(btn);

                JPopupMenu jpm2=new JPopupMenu();
                JMenuItem jpm21=new JMenuItem("打开");
                JMenuItem jpm22=new JMenuItem("删除");
                JMenuItem jpm23=new JMenuItem("润色");

                jpm21.addActionListener(pil);
                jpm22.addActionListener(pil);
                jpm23.addActionListener(pil);
                jpm2.add(jpm21);
                jpm2.add(jpm22);
                jpm2.add(jpm23);

              // btn.setComponentPopupMenu(jpm2);
                jpm2.show(btn,e.getX(),e.getY());
              //上面两种方法都可以点击菜单项后关闭菜单,并且再度点击按钮时之前的菜单会隐去

               //jpm2.setLocation(btn.getLocationOnScreen().x+e.getX(),btn.getLocationOnScreen().y+e.getY());
                //jpm2.setVisible(true);//这个方法的缺点就是,打开菜单后点击菜单项关不掉。并且重复点击按钮后原先的菜单关不掉



            }else if(source instanceof  JLabel jla){
                PIListener pil=new PIListener(jla);

                JPopupMenu jpm3=new JPopupMenu();
                JMenuItem jpm31=new JMenuItem("删除标签");
                JMenuItem jpm32=new JMenuItem("新建标签");
                JMenuItem jpm33=new JMenuItem("修改标签");
                jpm31.addActionListener(pil);
                jpm32.addActionListener(pil);
                jpm33.addActionListener(pil);
                jpm3.add(jpm31);
                jpm3.add(jpm32);
                jpm3.add(jpm33);
                jla.setComponentPopupMenu(jpm3);

            }

        }

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}
java 复制代码
package OwnFileManager;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class PIListener implements ActionListener{
    JComponent jComponent;
    public PIListener (JComponent jComponent){
        this.jComponent=jComponent;
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        String a=e.getActionCommand();
        //然后我们就可以对动作监听器传进来的参数类型进行识别区分
        if(jComponent instanceof  JButton btn){
            //btn就是按钮菜单对应的按钮
            String b=btn.getText();
            System.out.println(a+"->"+b );
        } else if(jComponent instanceof  JLabel jla){
            //btn就是按钮菜单对应的按钮
            String b=jla.getText();
            System.out.println(a+"->"+b );
        }else if(jComponent instanceof JPanel jPanel){
            System.out.println("面板被"+a);
        }

        /*
        String a=e.getActionCommand();
        System.out.println(a+"被点击");
         */

    }
}

再实操一遍:

java 复制代码
package OwnFileManager;

import javax.swing.*;

public class UI {
    //创建一个窗口面板,在面板中添加一个右键菜单,点击菜单项可以输出菜单文本内容
    //窗口中添加一个按钮和标签,给各自附加上对应菜单,并且点击各自菜单的时候能够得到"菜单项文本名称->组件文本名称"

    PMListener pml=new PMListener();
    public void showUI(){
        JFrame jf=new JFrame();
        JPanel jPanel=new JPanel();

        jf.setTitle("右键菜单");
        jf.setSize(400,400);
        jf.setLocation(400,350);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


        JButton btn=new JButton("photos");
        JLabel jla=new JLabel("img");

        PIListener pil=new PIListener(jPanel);
        JPopupMenu jpm1=new  JPopupMenu();
        JMenuItem jpm11=new JMenuItem("刷新");
        JMenuItem jpm12=new JMenuItem("新建文件夹");
        JMenuItem jpm13=new JMenuItem("新建文件");
        jpm11.addActionListener(pil);
        jpm12.addActionListener(pil);
        jpm13.addActionListener(pil);
        jpm1.add(jpm11);
        jpm1.add(jpm12);
        jpm1.add(jpm13);
        jPanel.setComponentPopupMenu(jpm1);







        btn.addMouseListener(pml);
        jla.addMouseListener(pml);

        jPanel.add(btn);
        jPanel.add(jla);


        jf.add(jPanel);


        jf.setVisible(true);










    }

    public static void main(String[] args) {
        UI ui=new UI();
        ui.showUI();
    }
}
java 复制代码
package OwnFileManager;

import preKnowledgeReview.A;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class PIListener implements ActionListener {
        JComponent jComponent;
        public PIListener (JComponent jComponent){
            this.jComponent=jComponent;
        }
    @Override
    public void actionPerformed(ActionEvent e) {
        if(jComponent instanceof  JButton btn){
            String a=e.getActionCommand();
            String str=btn.getText();
            System.out.println(a+"->"+str);
        }else if(jComponent instanceof  JLabel jla){
            String a=e.getActionCommand();
            String str=jla.getText();
            System.out.println(a+"->"+str);
        }else if(jComponent instanceof  JPanel jPanel){
            String a=e.getActionCommand();
            System.out.println(a);
        }




    }
}
java 复制代码
package OwnFileManager;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class PMListener implements MouseListener {
    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {
        int TypeButton=e.getButton();
        if(TypeButton==MouseEvent.BUTTON3){
            JComponent source=(JComponent)  e.getSource();
            if(source instanceof  JButton btn){
                 /*原本想借着在点击鼠标的时候就把按钮的文本内容得到,确实也得到了,但问题是传不到别的监听器中,因为一个文件里面只让有一个公共类
                并且在创建自定义类的时候注意不要和原本的java自带的类起冲突,例如string不要写成String
                class string {
                    public String str (){
                        return ((JButton) source).getText();
                    }
                }

                  */
                //PIL监听器的设置不仅是为了监听菜单和确定其监听类型,更是为了获取事件源的文本内容,btn的传入等于说是把该事件源也传进动作监听器中了,实现了另一种形式的动作监听器对事件源的"监听",
                //因此不对事件源添加动作监听器却依然能够在动作监听器中处理该事件源的方法就出现了,那就是把事件源作为参数传入动作监听器中
                PIListener pil=new PIListener(btn);

                JPopupMenu jpm2=new  JPopupMenu();
                JMenuItem jpm21=new JMenuItem("删除");
                JMenuItem jpm22=new JMenuItem("打开");
                JMenuItem jpm23=new JMenuItem("润色");

                jpm21.addActionListener(pil);
                jpm22.addActionListener(pil);
                jpm23.addActionListener(pil);
                jpm2.add(jpm21);
                jpm2.add(jpm22);
                jpm2.add(jpm23);
                btn.setComponentPopupMenu(jpm2);
            }
            else if(source instanceof  JLabel jla){
                PIListener pil=new PIListener(jla);
                JPopupMenu jpm3=new  JPopupMenu();
                JMenuItem jpm31=new JMenuItem("删除");
                JMenuItem jpm32=new JMenuItem("新建");
                JMenuItem jpm33=new JMenuItem("复制");
                jpm31.addActionListener(pil);
                jpm32.addActionListener(pil);
                jpm33.addActionListener(pil);
                jpm3.add(jpm31);
                jpm3.add(jpm32);
                jpm3.add(jpm33);
                jla.setComponentPopupMenu(jpm3);
            }


        }



    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}

这一遍又遇到了新的问题:

(1)如果已经向一个组件中绑定了一个菜单,接下来如果在向另一个菜单中传入第一个菜单里的某个菜单项,那么第一个菜单整个就会被隐去

(2)笔者想在右击按钮的时候就把按钮的文本内容得到,倒是想创建个公共类来着,问题是一个文件里面只让有一个公共类,即使得到了也传不到别的类当中去。所以得到按钮的文本内容只能发生在动作监听器中,那么这时候就可以在创建动作监听器监听菜单的同时传入本事件源,然后在动作监听器中对事件源进行获取文本内容操作,因此就需要在动作监听器中创建成员变量和构造方法。然后就能把右键按钮在鼠标监听器中得到的事件源传入到动作监听器中,这是右键菜单中最需要思维量的一部分。

至此,我能独立实现右键菜单的操作过程,按照需求进行一步步的实现和修改调整,并且对日后的项目实现有了新的感悟:不必在一开始就写出与原先正确答案一摸一样的代码,而是根据自己的需求一步步实现,然后遇到了新的需求再对原代码进行改正和调整。当在这一处,按照需求的代码和正确答案中在这一处的代码冲突时,先按照需求写,完后再改正。

至此右键菜单项目完成,接下来实现可视化文件系统和右键菜单的结合,得以在点出右键菜单后能够真正实现菜单中的项目。