Chciałbym stworzyć popover w stylu Google Chrome ze strzałkami, zaokrąglonymi narożnikami i jeśli mam czas na efekt cienia. W Java Swing. Jakie jest najlepsze podejście? SplashScreen? A może zwykły AWT Window? Inne pomysły? Dzięki!Swing: popover ze strzałką
Odpowiedz
Istnieje kilka opcji, a każdy z nich ma swoje wady i zalety ...
Tworzenie okna niestandardowy kształt - z takim podejściem niektóre systemy będą w stanie stworzyć dodatkowy odcień tył w kształcie okna, działa to również na większość systemów (powinno działać nawet na JDK-a Linuksa). Złą rzeczą w tym podejściu (które w rzeczywistości czyni go bezużytecznym) jest nieregularna linia graniczna kształtu - jeśli utworzysz okno w kształcie elipsy, jego boki będą wyglądać na szorstkie.
Utwórz nieprzezroczyste, nierozwinięte okno z narysowanym kształtem - to podejście rozwiąże główny problem podejścia (1). Możesz kształtować alias, rysując na całkowicie przezroczystym oknie. Złe jest to, że działa tylko na systemach Win i Mac. Na (przeważnie) dowolnym systemie linux dostaniesz prostokąt wynikowy okna i mnóstwo błędów o nieobsługiwanych operacjach.
Utwórz niestandardowe wyskakujące okienko wewnątrz okna java i umieść je na szybach z warstwami lub szybami. Pozwoli to w pełni uniknąć problemów ze zgodnością i uzyskać korzyści z (2) podejścia. Jest jednak coś złego w tym podejściu - możesz wyświetlać tylko takie wyskakujące okienka w oknach okna głównego. W większości przypadków jest to o wiele lepsze niż dwa inne sposoby, ponieważ używa mniej zasobów, nie tworzy dodatkowych okien i można kontrolować każdą część popup.
O 3 podejścia - można sprawdzić TooltipManager stworzyłem w moim projekcie WebLookAndFeel - używa szybę okna do wyświetlania niestandardowych kształtach półprzezroczystych podpowiedzi z cienia efektu. Wkrótce dodam okno PopupManager, które pozwoli na szybkie tworzenie "wewnętrznych" okien popupów.
Oto kilka przykładów rozwiązań:
Trochę kodu użytego do przodu we wszystkich przykładach
Metoda, aby utworzyć kształt:
private static Area createShape()
{
Area shape = new Area (new RoundRectangle2D.Double (0, 20, 500, 200, 20, 20));
GeneralPath gp = new GeneralPath (GeneralPath.WIND_EVEN_ODD);
gp.moveTo (230, 20);
gp.lineTo (250, 0);
gp.lineTo (270, 20);
gp.closePath();
shape.add (new Area (gp));
return shape;
}
adapter myszy, która umożliwia Aby przenieść okno, przeciągając komponent:
public static class WindowMoveAdapter extends MouseAdapter
{
private boolean dragging = false;
private int prevX = -1;
private int prevY = -1;
public WindowMoveAdapter()
{
super();
}
public void mousePressed (MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton (e))
{
dragging = true;
}
prevX = e.getXOnScreen();
prevY = e.getYOnScreen();
}
public void mouseDragged (MouseEvent e)
{
if (prevX != -1 && prevY != -1 && dragging)
{
Window w = SwingUtilities.getWindowAncestor (e.getComponent());
if (w != null && w.isShowing())
{
Rectangle rect = w.getBounds();
w.setBounds (rect.x + (e.getXOnScreen() - prevX),
rect.y + (e.getYOnScreen() - prevY), rect.width, rect.height);
}
}
prevX = e.getXOnScreen();
prevY = e.getYOnScreen();
}
public void mouseReleased (MouseEvent e)
{
dragging = false;
}
}
1-ty przykład podejście:
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setUndecorated (true);
JPanel panel = new JPanel();
panel.setBackground (Color.BLACK);
WindowMoveAdapter wma = new WindowMoveAdapter();
panel.addMouseListener (wma);
panel.addMouseMotionListener (wma);
frame.getContentPane().add (panel);
Area shape = createShape();
AWTUtilities.setWindowShape (frame, shape);
frame.setSize (shape.getBounds().getSize());
frame.setLocationRelativeTo (null);
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
}
Jak widać - narożniki zaokrąglonym kształcie są dość szorstkie i nie wyglądające
2-ty podejście:
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setUndecorated (true);
final Area shape = createShape();
JPanel panel = new JPanel()
{
protected void paintComponent (Graphics g)
{
super.paintComponent (g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint (Color.BLACK);
g2d.fill (shape);
}
};
panel.setOpaque (false);
WindowMoveAdapter wma = new WindowMoveAdapter();
panel.addMouseListener (wma);
panel.addMouseMotionListener (wma);
frame.getContentPane().add (panel);
AWTUtilities.setWindowOpaque (frame, false);
frame.setSize (shape.getBounds().getSize());
frame.setLocationRelativeTo (null);
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
}
teraz powinien wyglądać perfekcyjnie - jedyny problem, który będzie poprawnie działał tylko na Windowsie i Macu (co najmniej w 1.6.x JDK). Co najmniej około miesiąca temu, kiedy ostatnio sprawdzałem to na różnych systemach operacyjnych.
3-ty podejście
public static void main (String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel (new BorderLayout());
panel.setOpaque (false);
WindowMoveAdapter wma = new WindowMoveAdapter();
panel.addMouseListener (wma);
panel.addMouseMotionListener (wma);
frame.getContentPane().add (panel);
panel.add (new JButton ("Test"));
final Area shape = createShape();
JPanel glassPane = new JPanel (null)
{
public boolean contains (int x, int y)
{
// This is to avoid cursor and mouse-events troubles
return shape.contains (x, y);
}
};
glassPane.setOpaque (false);
frame.setGlassPane (glassPane);
glassPane.setVisible (true);
JComponent popup = new JComponent()
{
protected void paintComponent (Graphics g)
{
super.paintComponent (g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint (Color.BLACK);
g2d.fill (shape);
}
};
popup.addMouseListener (new MouseAdapter()
{
// To block events on the popup
});
glassPane.add (popup);
popup.setBounds (shape.getBounds());
popup.setVisible (true);
frame.setSize (800, 500);
frame.setLocationRelativeTo (null);
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
}
Jest to prosty przykład okienko umieszczony w szklanym szyby. Jak widać, istnieje tylko wewnątrz ramki JFrame, ale ma stronę z aliasami i działa poprawnie na każdym systemie operacyjnym.
bardzo dobra odpowiedź +1 – mKorbel
Dodałem próbki kodu do wszystkich podejść –
Już zauważyłem problem i zmieniłem tekst, ale dzięki za podpowiedź :) –