JMenu in Popup mit Navigation links und rechts.

Romsl

Erfahrenes Mitglied
Hi,

ich habe schonmal was versucht, aber allerdings funktioniert das nicht ganz. Es sollte wie das JMenu (incl. JMenuItem) werden, das einen Pfeil auf der rechten Seite bekommt wenn dieses KindElemente enthält und bei Mouse Entered die neuen Menu Elemente öffnet. Das selbe brauche ich jetzt auch noch auf der rechten Seite (also beide Seiten).

Hier mal mein Code:

Java:
package de.tutorials.component;

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

/**
 * TODO: document me
 * <p/>
 * <code>BrowseMenuItemExample</code>.
 * <p/>
 * User: Romsl
 * Date: 04.10.2006
 * Time: 12:27:15
 *
 * @author Roman R&auml;dle
 * @version $Revision$
 */
public class BrowseMenuItemExample {

    public static void main(String[] args) {
        final JFrame frame = new JFrame("BrowseMenuItemExample");

        frame.setSize(new Dimension(400, 300));

        final JPopupMenu menu = new JPopupMenu("MenuItem");
        menu.add(new BrowseMenuItem());

        //frame.addMouseListener();
        //frame.add(menu);
        frame.addMouseListener(new MouseAdapter() {
            /**
             * Invoked when the mouse has been clicked on a component.
             */
            public void mouseClicked(MouseEvent e) {

                if (SwingUtilities.isRightMouseButton(e)) {
                    menu.show(frame, e.getX(), e.getY());
                }
            }
        });

        frame.setVisible(true);
    }

    public enum Orientation {
        LEFT, RIGHT
    }

    static class BrowseMenuItem extends JComponent {

    private JCheckBox checkBox = new JCheckBox("Test");

    /**
     * Creates a <code>JMenuItem</code> with no set text or icon.
     */
    public BrowseMenuItem() {
        System.out.println("getLayout() = " + getLayout());

        setLayout(new BorderLayout());

        add(new Arrow(Orientation.LEFT), BorderLayout.WEST);
        add(checkBox, BorderLayout.CENTER);
        add(new Arrow(Orientation.RIGHT), BorderLayout.EAST);
    }

    class Arrow extends JComponent {

        private Polygon triangle;

        public Arrow(Orientation orientation) {

            switch (orientation) {
                case LEFT:
                    triangle = new Polygon(new int[]{0, 10, 10}, new int[]{5, 0, 10}, 3);
                    break;
                case RIGHT:
                    triangle = new Polygon(new int[]{10, 0, 0}, new int[]{5, 0, 10}, 3);
                    break;
            }

            addMouseListener(new MouseAdapter() {
                /**
                 * Invoked when the mouse enters a component.
                 */
                public void mouseEntered(MouseEvent e) {
                    JPopupMenu menu = new JPopupMenu();
                    menu.add(new JCheckBox("Test"));

                    menu.show(Arrow.this, e.getX(), e.getY());
                }
            });
        }

        /**
         * Calls the UI delegate's paint method, if the UI delegate
         * is non-<code>null</code>.  We pass the delegate a copy of the
         * <code>Graphics</code> object to protect the rest of the
         * paint code from irrevocable changes
         * (for example, <code>Graphics.translate</code>).
         * <p/>
         * If you override this in a subclass you should not make permanent
         * changes to the passed in <code>Graphics</code>. For example, you
         * should not alter the clip <code>Rectangle</code> or modify the
         * transform. If you need to do these operations you may find it
         * easier to create a new <code>Graphics</code> from the passed in
         * <code>Graphics</code> and manipulate it. Further, if you do not
         * invoker super's implementation you must honor the opaque property,
         * that is
         * if this component is opaque, you must completely fill in the background
         * in a non-opaque color. If you do not honor the opaque property you
         * will likely see visual artifacts.
         * <p/>
         * The passed in <code>Graphics</code> object might
         * have a transform other than the identify transform
         * installed on it.  In this case, you might get
         * unexpected results if you cumulatively apply
         * another transform.
         *
         * @param g the <code>Graphics</code> object to protect
         * @see #paint
         * @see javax.swing.plaf.ComponentUI
         */
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.fillPolygon(triangle);
        }

        /**
         * Returns the current width of this component.
         * This method is preferable to writing
         * <code>component.getBounds().width</code>, or
         * <code>component.getSize().width</code> because it doesn't cause any
         * heap allocations.
         *
         * @return the current width of this component
         */
        public int getWidth() {
            return 10;
        }

        /**
         * Returns the current height of this component.
         * This method is preferable to writing
         * <code>component.getBounds().height</code>, or
         * <code>component.getSize().height</code> because it doesn't cause any
         * heap allocations.
         *
         * @return the current height of this component
         */
        public int getHeight() {
            return checkBox.getHeight();
        }

        /**
         * Stores the width/height of this component into "return value"
         * <code>rv</code> and returns <code>rv</code>.
         * If <code>rv</code> is <code>null</code> a new <code>Dimension</code>
         * object is allocated.  This version of <code>getSize</code>
         * is useful if the caller wants to avoid allocating a new
         * <code>Dimension</code> object on the heap.
         *
         * @param rv the return value, modified to the component's size
         * @return <code>rv</code>
         */
        public Dimension getSize(Dimension rv) {
            return new Dimension(20, checkBox.getHeight());
        }

        /**
         * If the <code>preferredSize</code> has been set to a
         * non-<code>null</code> value just returns it.
         * If the UI delegate's <code>getPreferredSize</code>
         * method returns a non <code>null</code> value then return that;
         * otherwise defer to the component's layout manager.
         *
         * @return the value of the <code>preferredSize</code> property
         * @see #setPreferredSize
         * @see javax.swing.plaf.ComponentUI
         */
        public Dimension getPreferredSize() {
            return new Dimension(20, checkBox.getHeight());
        }
    }
}
}

Gruß

Romsl
 
Hallo!

Kleiner Tipp am Rande:

anstatt den Typ/Methoden Doclet-Kommentar zu kopieren solltest du besser die Anweisung:
Code:
/**
* {@inheritdoc }
*/
verwenden. Dadurch wird javadoc angewiesen den Kommentar aus dem in der Verbungshierachie direkt vorgehenden Element zu ziehen.

Gruß Tom
 
Hallo Romsl,

was genau funktioniert nicht? Du hast je ein Pfeil auf beiden Seiten wie gewollt. Beide reagieren. Sollen unterschiedliche Untermenüs geöffnet werden wenn links oder rechts aktiviert wird?


Vg Erdal
 
Hi,

ja es sollen unterschiedliche Menüs sein. Das ist aber nicht das Problem, sondern das bereits geöffnete Menü soll auf offen bleiben. So wie man das aus den üblichen Kontextmenüs kennt.

Gruß
-- Romsl
 
Hallo Romsl,

ich habe leider keine vollständige Lösung gefunden. Was ich aber rausgefunden habe ist, das das alte Menu verschwindet weil du kein JMenu sondern ein JComponent einfügst.

Ich habe mich etwas daran versucht, aber nicht ganz geschafft. Man müsste irgendwie das JMenu gescheit überschreiben. Jedoch ist die Klasse JMenu mit seinen 1200 Zeilen Code mit meinen Swing Kenntnissen nicht überschaubar.

Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;

public class JSuperPopupMenuUser extends JFrame {

	private JSuperPopupMenu spm = new JSuperPopupMenu("SuperPopupMenu");

	public JSuperPopupMenuUser() {
		this.setTitle(this.getClass().getCanonicalName());
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setAlwaysOnTop(true);
		this.setLocationByPlatform(true);
		this.setSize(320, 480);

		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e)) {
					spm.show(JSuperPopupMenuUser.this, e.getX(), e.getY());
				}
			}
		});

		this.setVisible(true);
	}

	public static void main(String[] args) {
		new JSuperPopupMenuUser();
	}

	class JSuperPopupMenu extends JPopupMenu {

		private JMenu menuA = new JMenu("A");

		private JExtendedMenu menuB = new JExtendedMenu("B", "C");

		private JMenuItem mItem1 = new JMenuItem("1");

		private JMenuItem mItem2 = new JMenuItem("2");

		private JMenuItem mItem3 = new JMenuItem("3");

		private JMenuItem mItem4 = new JMenuItem("4");

		private JMenuItem mItem5 = new JMenuItem("5");

		private JMenuItem mItem6 = new JMenuItem("6");

		public JSuperPopupMenu(String label) {
			super(label);

			this.add(mItem1);
			this.add(menuA);
			this.add(menuB);
			this.add(mItem6);

			menuA.add(mItem2);
			menuA.add(mItem3);

			menuB.addLeft(mItem4);
			menuB.addRight(mItem5);
		}
	}

	class JExtendedMenu extends JMenu {

		private Dimension d;

		private JMenu menuL;

		private JMenu menuR;

		private JLabel label = new JLabel("|");

		public JExtendedMenu(String l, String r) {
			menuL = new JMenu(l);
			menuR = new JMenu(r);
			menuL.setOpaque(true);
			menuR.setOpaque(true);

			this.setLayout(new BorderLayout());
			this.add(menuL, BorderLayout.WEST);
			this.add(label, BorderLayout.CENTER);
			this.add(menuR, BorderLayout.EAST);
			this.doLayout();

			d = new Dimension(80, 20);

			this.setPreferredSize(d);
			this.setMinimumSize(d);
		}

		public void addLeft(JMenuItem mItem) {
			menuL.add(mItem);
		}

		public void addRight(JMenuItem mItem) {
			menuR.add(mItem);
		}
	}
}


Vg Erdal
 
Hi flashray.

meiner Meinung nach hat das Schließen nichts mit der JComponent zu tun. Eher liegt es daran, dass der MouseListener ein neues JPopupMenu öffnet und das Alte (wahrscheinlich irgendwo registriert) automatisch geschlossen wird.

Gruß
-- Romsl
 
Hallo Romsl,

eben das ist ja meine Idee. Ein JMenu bzw. ein JMenuItem wird automatisch registriert, und gehört zum ursprünglichen JPopupMenu. Wenn du aber eine gänzlich neue Komponente abgeleitet von JComponent schreibst, musst du das registrieren manuell selbst machen. Ich wüsste aber nicht wie und wo. Wenn du aber eine Klasse schreibst die von JMenu erbt wird die Registrierung überflüssig, da sie automatisch geschieht.

Wie dem auch sei, ist eine komplizierte Angelegenheit, wenn man nicht weiß wie man rangehen soll.

Du könntest ja mal jemanden anschreiben, der professionell mit Swing zu tun hat. So Leute wie die erweiterte Komponenten zu Swing schreiben oder Look and Feels für Swing implementieren.


Vg Erdal
 
Hallo Romsl,

ich habe etwas gebastelt, was nicht genau dem enstpricht was du möchtest, aber schon etwas näher kommt - denke ich.

Java:
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;

public class JExtendedMenuUser extends JFrame {

	private JPopupMenu jpm = new JPopupMenu();

	public JExtendedMenuUser() {
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(480, 640);
		this.setAlwaysOnTop(true);
		this.setLocationByPlatform(true);

		JMenu A = new JMenu("SWT");
		A.add("Dialog");
		A.add("Widget");

		JExtendedMenu B = new JExtendedMenu("Swing", "AWT");

		JMenu D = new JMenu("JTextComponent");
		B.addLeft(new JMenuItem("JFrame"));
		B.addLeft(D);
		B.addLeft(new JMenuItem("JDialog"));
		D.add(new JMenuItem("JTextPane"));
		D.add(new JMenuItem("JTextArea"));
		D.add(new JMenuItem("JTextField"));

		JMenu E = new JMenu("TextComponent");
		B.addRight(new JMenuItem("Frame"));
		B.addRight(new JMenuItem("Dialog"));
		B.addRight(E);
		E.add(new JMenuItem("TextArea"));
		E.add(new JMenuItem("TextField"));

		jpm.add(A);
		jpm.add(B);
		jpm.add(new JMenuItem("SwingWT"));

		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e)) {
					jpm.show(JExtendedMenuUser.this, e.getX(), e.getY());
				}
			}
		});

		this.setVisible(true);
	}

	public static void main(String[] args) {
		new JExtendedMenuUser();
	}

	class JExtendedMenu extends JMenu {

		private JMenu menuL;

		private JMenu menuR;

		public JExtendedMenu(String l, String r) {
			super(l + "|" + r);

			menuL = new JMenu(" " + l + " ");
			menuR = new JMenu(" " + r + " ");

			JPopupMenu pm = getPopupMenu();
			pm.setLayout(new BoxLayout(pm, BoxLayout.X_AXIS));
			setMinimumSize(getPreferredSize());
			pm.add(menuL);
			pm.add(new JLabel("|"));
			pm.add(menuR);

			menuL.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
		}

		public void setPopupMenuVisible(boolean b) {
			boolean isVisible = isPopupMenuVisible();
			if (b != isVisible) {
				if ((b == true) && isShowing()) {
					int x = 0;
					int y = 0;
					Container parent = getParent();
					if (parent instanceof JPopupMenu) {
						x = -getWidth() / 2;
						y = 0;
					} else {
						x = - getWidth() / 2;
						y = 0;
					}
					getPopupMenu().show(this, x, y);
				} else {
					getPopupMenu().setVisible(false);
				}
			}
		}

		public void addLeft(JMenuItem mItem) {
			menuL.add(mItem);
		}

		public void addRight(JMenuItem mItem) {
			menuR.add(mItem);
		}

		public void removeLeft(JMenuItem mItem) {
			menuL.remove(mItem);
		}

		public void removeRight(JMenuItem mItem) {
			menuR.remove(mItem);
		}
	}
}


Vg Erdal
 
Zuletzt bearbeitet:
Hallo Romsl,

hier nochmal ein anderer Ansatz der aber wieder leider unvollständig ist. Ich glaube ich weiss jetzt warum das alte Menu in deinem Beispiel schließt wenn das linke oder rechte Untermenü geöffnet wird. Weil das neue linke oder rechte eben kein Untermenü ist, sondern einem eigenen JPopupMenu gehört den du im MouseListener extra erzeugst. Somit hat dieser keinen Bezug zum vorherigen. Wenn ein neues anderes JPopupMenu geöffnet wird, dann wird wohl Swing das alte schließen.


Java:
import java.awt.ComponentOrientation;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

public class JDoubleMenuUser extends JFrame {

	private JPopupMenu jpm = new JPopupMenu();

	public JDoubleMenuUser() {
		this.setTitle(this.getClass().getCanonicalName());
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(480, 640);
		this.setAlwaysOnTop(true);
		this.setLocationByPlatform(true);

		JMenu A = new JMenu("SWT");
		A.add("Dialog");
		A.add("Widget");

		JDoubleMenu B = new JDoubleMenu(jpm, "Swing", "AWT");

		JMenu D = new JMenu("JTextComponent");
		B.addLeft(new JMenuItem("JFrame"));
		B.addLeft(D);
		B.addLeft(new JMenuItem("JDialog"));
		D.add(new JMenuItem("JTextPane"));
		D.add(new JMenuItem("JTextArea"));
		D.add(new JMenuItem("JTextField"));

		JMenu E = new JMenu("TextComponent");
		B.addRight(new JMenuItem("Frame"));
		B.addRight(new JMenuItem("Dialog"));
		B.addRight(E);
		E.add(new JMenuItem("TextArea"));
		E.add(new JMenuItem("TextField"));

		jpm.add(A);
		jpm.add(B);
		jpm.add(new JMenuItem("SwingWT"));

		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e)) {
					jpm.show(JDoubleMenuUser.this, e.getX(), e.getY());
				}
			}
		});

		this.setVisible(true);
	}

	public static void main(String[] args) {
		new JDoubleMenuUser();
	}

	class JDoubleMenu extends JMenu {

		private JMenu jdm = this;

		private List<JMenuItem> left = new ArrayList<JMenuItem>();

		private List<JMenuItem> right = new ArrayList<JMenuItem>();

		private boolean isLeft = true;

		private MouseListener ml;

		public JDoubleMenu(JPopupMenu jpm, String l, String r) {
			super("\u25C4 " + l + "  |  " + r + " \u25BA");

			ml = new MouseAdapter() {
				public void mouseEntered(MouseEvent e) {
					createMenu();
				}
			};
			this.addMouseListener(ml);
		}

		private void createLeftMenu() {
			jdm.removeAll();
			jdm.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
			for (JMenuItem jmi : left) {
				jdm.add(jmi);
			}
		}

		private void createRightMenu() {
			jdm.removeAll();
			jdm.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
			for (JMenuItem jmi : right) {
				jdm.add(jmi);
			}
		}

		private void createMenu() {
			isLeft = isLeft();
			if (isLeft)
				createLeftMenu();
			else
				createRightMenu();
		}

		private boolean isLeft() {
			if (this.getMousePosition().x < this.getWidth() / 2)
				return true;
			else
				return false;
		}

		public void addLeft(JMenuItem mItem) {
			left.add(mItem);
		}

		public void addRight(JMenuItem mItem) {
			right.add(mItem);
		}

		public void removeLeft(JMenuItem mItem) {
			left.remove(mItem);
		}

		public void removeRight(JMenuItem mItem) {
			right.remove(mItem);
		}

		public void addLeft(JMenu menu) {
			left.add(menu);
		}

		public void addRight(JMenu menu) {
			right.add(menu);
		}

		public void removeLeft(JMenu menu) {
			left.remove(menu);
		}

		public void removeRight(JMenu menu) {
			right.remove(menu);
		}
	}
}


Vg Erdal
 
Hi,

Danke erstmal für die vielen Beispiele. Aber Du hast das vielleicht nicht richtig verstanden. Es soll nur ein Eintrag pro PopupMenuItem sein. Also nicht eines links und eines rechts. Ich möchte dann, dass dieses eine links und rechts Pfeile hat wobei jedesmal ein Menu erweitert wird (so wie jetzt auch). Es sind aber nicht identische Menüs die geöffnet werden sollen.

Gruß
-- Romsl
 

Anhänge

  • 26411attachment.png
    26411attachment.png
    14,9 KB · Aufrufe: 16
Zurück