[疯狂Java]AWT:右键菜单

时间:2021-09-10 17:04:56

1. 上下文菜单——右键菜单:

    1) 顾名思义就是鼠标右键打开的菜单,它也叫上下文菜单;

    2) 之所以叫上下文菜单是因为右击的位置不一样弹出的浮动菜单显示的内容可能不同;

    3) 比如:在Word中在文本编辑区域右键弹出的菜单和左侧导航栏中右键菜单的内容是完全不同的,可以见右键菜单的具体内容是依赖所属的区域或者右击在哪个GUI组件中,右击区域不同显示内容就不同,因此它是上下文相关的,这个上下文就是GUI组件区域;


2. 右键菜单的生成步骤:

    1) 第一步必然是要鼠标右击,既然鼠标右击就必须对鼠标右击事件进行监听和处理,而且可以通过观察,右键菜单都是在右键松开时弹出的,因此必须要精确到鼠标松开这一事件;

    2) 其次就是右键菜单的上下文相关特性,必须要确定鼠标是在哪个GUI区域右击的;

    3) 结合以上两点弹出右键菜单的具体步骤是:

         i. 先调用GUI组件的addMouseListener方法注册一个可以监控鼠标事件的监听器;

         ii. 我们的监听器必须实现mouseReleased这一方法;

         iii. 在该方法中判断松开的是左键还是右键(约定俗成是右键弹出浮动菜单的);

         iv. 如果是右键则要弹出浮动菜单;

    4) 因此我们注册鼠标监听器这一环节的代码如下:

Panel p = new Panel();
p.addMouseListener(new MouseAdapter() {

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
		// super.mouseReleased(e);
		if (e.isPopupTrigger()) {
			// 弹出浮动菜单
		}
	}
	
});
!!这就是弹出浮动菜单的准备工作,即框架;

    5) 关于isPopupTrigger方法:

         i. 原型(MouseEvent的方法):public boolean MouseEvent.isPopupTrigger();

         ii. 为什么不调用Left/Right之类的方法来判断是左键还是右键呢?因为在历史上不同操作系统触发上下文菜单的方式不同,有些是右键按下时触发、有些是右键松开时触发,甚至有的是左键上的操作,因此为了真正的实现跨平台,就用该方法隐藏了操作系统的具体实现,它能保证一定能检验是否是右键菜单的触发动作;

         iii. 因此最为严谨的做法是mouseReleased和mousePressed方法里都要调用isPopupMenu来检测弹出动作,但一般还是松开鼠标键的情况更被广泛接受;


3. PopupMenu——关于上下文菜单:

    1) 即我们的主角上下文菜单(右键菜单)了;

    2) 构造器:PopupMenu(); // 没有文本标签参数,因为右键菜单是浮动菜单,不需要标签

    3) 向其中添加菜单项(还是add,和普通Menu一样):MenuItem add(MenuItem mi);

!!但是要注意的是:右键菜单的菜单项是没有快捷键的,只有菜单栏中菜单的菜单项可以添加菜单项的,即使你添加了也不会生效;

    4) 上下文菜单最重要的性质就是上下文相关,因此右键菜单一定应该属于某个窗口(容器),不同窗口(容器)拥有不同的右键菜单,因此不同窗口(容器)中打开的右键菜单内容不同,所以必须调用容器的add方法将PopupMenu加入其中,例如:Panel p = new Panel(); PopupMenu pop = new PoupMenu(); p.add(pop);

    5) 最后要显示菜单,即弹出右键菜单,即在上面的e.isPopupTrigger中的语句,只要调用PopupMenu的show方法即可:

         i. 原型:public void PopupMenu.show(Component origin, int x, int y);

         ii. x、y必然是弹出时菜单左上角的坐标了;

         iii. origin是坐标原点的意思,即(x, y)所处的坐标系,因为x和y我们可以通过MouseEvent的getX和getY方法获取,但是这两个方法得到的坐标是屏幕绝对坐标,即相对于整个屏幕左上角的坐标,但是右键菜单往往是在一个GUI容器中(窗口等)显示,因此需要将x和y转换成相对于菜单所属容器的原点的坐标,而origin传的就应该是右键菜单的所属窗口容器,这样该方法就会以该容器左上角为原点将(x, y)转换成相对于该原点的坐标,这样就满足了平台无关性的要求,即得到的都是相对坐标并且是逻辑坐标;

!!最后看一下MouseEvent的getX和getY方法:

        a. public int getX();

        b. public int getY();

!两者返回的都是相对于屏幕左上角的绝对坐标,因此需要给出所属组件origin来将其转化成相对逻辑坐标;

!因此完整的监听器实现代码如下:

Panel p = new Panel();
PopupMenu pop = new PopupMenu();
p.add(pop);
p.addMouseListener(new MouseAdapter() {

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
		// super.mouseReleased(e);
		if (e.isPopupTrigger()) {
			// 弹出浮动菜单
			pop.show(p, e.getX(), e.getY());
		}
	}
	
});


4. 示例:

public class AwtTest {

	public void init() {
		Frame f = new Frame("Menu Test");
		TextArea ta = new TextArea(4, 30); f.add(ta);
		Panel p = new Panel(); f.add(p, BorderLayout.SOUTH);
		p.setPreferredSize(new Dimension(300, 160));
			PopupMenu pop = new PopupMenu(); p.add(pop);
				CheckboxMenuItem autoWrap = new CheckboxMenuItem("Auto wrap"); pop.add(autoWrap);
				pop.addSeparator();
				MenuItem copyItem = new MenuItem("Copy"); pop.add(copyItem);
				MenuItem pasteItem = new MenuItem("Paste"); pop.add(pasteItem);
				pop.add(new MenuItem("-"));
				Menu format = new Menu("Format"); pop.add(format);
					MenuItem commentItem = new MenuItem("Comment"); format.add(commentItem);
					MenuItem cancelItem = new MenuItem("Cancel comment"); format.add(cancelItem);
			
			
		ActionListener menuListener = e -> {
			String cmd = e.getActionCommand();
			ta.append("Click '" + cmd + "' menu item.\n");
			if (cmd.equals("Exit")) {
				System.exit(0);
			}
		};
		copyItem.addActionListener(menuListener);
		pasteItem.addActionListener(menuListener);
		commentItem.addActionListener(menuListener);
		cancelItem.addActionListener(menuListener);
		f.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				// TODO Auto-generated method stub
				// super.windowClosing(e);
				System.exit(0);
			}
		});
		
		p.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseReleased(MouseEvent e) {
				// TODO Auto-generated method stub
				// super.mouseReleased(e);
				if (e.isPopupTrigger()) {
					// 弹出浮动菜单
					pop.show(p, e.getX(), e.getY());
				}
			}
				
		});
			
		f.pack();
		f.setVisible(true);
	}
	
	public static void main(String[] args) {
		new AwtTest().init();
	}

}

!!如果完全依赖AWT的自动调整,那么组件的大小都是自动设定的,当然也可以自己设定组件的大小,特别是编辑框之类的,通常会自己设定大小,因此就要使用Component类的setPreferredSize方法了:public void Component.setPreferredSize(Dimension preferredSize);

!Dimension的构造器就是:Dimension(int width, int height); // 就是一个结构体,包装了宽和高