Рисование непосредственно на стекле: не работает
Я занимаюсь разработкой приложения с использованием Swing. Пока пользователь взаимодействует с приложением, справочные данные могут быть доступны в закрытом разделе экрана, и я хотел бы нарисовать небольшую стрелку, указывающую на этот раздел, когда это произойдет.
Для этого я расширил JPanel и добавил его в качестве стеклянной панели приложения jframe. Имя пользовательского класса стеклянной панели - AlertGlassPane.
AlertGlassPane делает это: ожидает появления новых справочных данных. Когда это происходит и раздел справки закрывается, он находит положение раздела справки на экране, а затем рисует анимированную стрелку на его стороне.
Чтобы нарисовать стрелку, я расширил метод paintComponent на стеклянной панели.
Чтобы анимировать стрелку, я создал маленькую нить, которая зацикливается каждые 100 мс, вызывая перерисовку на стеклянной панели.
Проблема: Java игнорирует мой рисунок... если триггер запускает анимацию и останавливается, ничего не происходит. В приложении нет рисунков.
Я знаю, что цикл работает и вызывается paintComponent, но пользовательская краска не действует.
Но если я наведу указатель мыши на область, где должна отображаться стрелка, это произойдет! Но только когда мышь движется. Если остановка перемещает мышь, анимация также останавливается, и стрелка останавливается на последней позиции перед остановкой мыши (или покидает область).
Я много чего перепробовал (например, как установить область обрезки рисунка), но ничего не помогло. Единственное, что сработало, это когда я по ошибке позвонил перекрасить изнутри paintComponent.
В данный момент я ищу способ связать систему рендеринга с тем, что область области моего стекла должна быть перекрашена (перерисовка (x, y, w, h) не работала...). Может быть, перенести пользовательский рендеринг в JLabel, а затем добавить этот ярлык на стекло? Мне не нравится этот подход...
Я постараюсь очистить код перед публикацией фрагмента здесь. Это поможет?
Заранее спасибо!
snnipet:
package br.com.r4j.test;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;
/**
*
* @author
*/
public class TestGlassPaneAnimation extends JPanel
{
private static TestGlassPaneAnimation gvp = new TestGlassPaneAnimation();
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
JFrame f = new JFrame("Anitest in glass");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.setResizable(false);
f.setLayout(new GridLayout(5, 3));
f.add(new JLabel("First Name :"));
f.add(new JTextField(20));
f.add(new JLabel("Last Name :"));
f.add(new JTextField(20));
f.add(new JLabel("Phone Number :"));
f.add(new JTextField(20));
f.add(new JLabel("Email:"));
f.add(new JTextField(20));
f.add(new JLabel("Address :"));
f.add(new JTextField(20));
JButton btnStart = new JButton("Click me, please!");
f.add(btnStart);
btnStart.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
gvp.startAnimation();
}
});
f.setGlassPane(gvp);
f.pack();
f.setVisible(true);
gvp.setVisible(true);
}
});
}
private BufferedImage icon;
private boolean animate = false;
private long timeStart = 0;
private Thread thrLastActive = null;
public TestGlassPaneAnimation()
{
setLayout(null);//this is the exception to the rule case a layoutmanager might make setting Jlabel co-ords harder
setOpaque(false);
Icon icon1 = UIManager.getIcon("OptionPane.warningIcon");
int imgW = icon1.getIconWidth();
int imgH = icon1.getIconHeight();
this.icon = ImageUtilities.getBufferedImageOfIcon(icon1, imgW, imgH);
this.animate = false;
}
public void startAnimation()
{
this.animate = true;
this.timeStart = (new Date()).getTime();
if (this.thrLastActive != null)
this.thrLastActive.interrupt();
this.thrLastActive = new Thread(new Runnable()
{
public void run()
{
try
{
while (true)
{
// int x = 250, y = 250;
// int width = 60, height = 60;
Thread.currentThread().sleep(100);
// frmRoot.invalidate();
// repaint(x, y, width, height);
repaint(new Rectangle(x, y, width, height));
// repaint(new Rectangle(10, 10, 2000, 2000));
// repaint();
// paintComponent(getGraphics());
}
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
this.thrLastActive.start();
}
protected void paintComponent(Graphics g)
{
try
{
// enables anti-aliasing
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
java.awt.Composite composite = g2.getComposite();
System.err.println("b1: " + g.getClipBounds());
if (this.animate)
{
long timeSpent = (new Date()).getTime() - timeStart;
int x = 10, y = 150;
int width = 60, height = 60;
float maxAlpha = 0.8f;
x += (-100*Math.sin(5*2*Math.PI*timeSpent/10000)+50)/15;
System.err.println("painting::x: " + x + ", y: " + y + ", sin: " + (Math.sin(6*2*Math.PI*timeSpent/10000)));
// g.setClip(x-10, y-10, width, height);
System.err.println("b2: " + g.getClipBounds());
AlphaComposite alpha2 = AlphaComposite.SrcOver.derive(maxAlpha);
g2.setComposite(alpha2);
g2.drawImage(this.icon, x, y, null);
g2.setComposite(composite);
g2.setComposite(composite);
}
}
catch (Throwable e)
{
System.err.println("Errr!");
e.printStackTrace();
}
}
}
class ImageUtilities {
public static BufferedImage resize(BufferedImage image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
}
public static BufferedImage getBufferedImageOfIcon(Icon icon, int imgW, int imgH) {
BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
return img;
}
}
1 ответ
Хм, точно не знаю, что происходит с фрагментом, который вы дали, но, похоже, он взят из моего, я написал другой пример (мне пришлось изменить 1 или 2 строки кода в showWarningIcon(Component c)
а также refreshLocations()
методы но ничего особенного:
Если будет напечатано что-то, кроме Дэвида, и нажата кнопка (нажмите меня, пожалуйста), это покажет это:
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class TestGlassPaneAnimation {
private static GlassValidationPane gvp = new GlassValidationPane();
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame f = new JFrame("Anitest in glass");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//f.setResizable(false);
f.setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.HORIZONTAL;
gc.weightx = 1;
gc.weighty = 1;
gc.insets = new Insets(15, 15, 15, 15);//give some space so icon doesnt cover components when shown
gc.gridx = 0;
gc.gridy = 0;
f.add(new JLabel("First Name:"), gc);
final JTextField jtf = new JTextField(20);
gc.gridx = 1;
f.add(jtf, gc);
gc.gridx = 0;
gc.gridy = 1;
f.add(new JLabel("Surname:"), gc);
final JTextField jtf2 = new JTextField(20);
gc.gridx = 1;
f.add(jtf2, gc);
JButton btnStart = new JButton("Click me, please!");
gc.gridx = 2;
f.add(btnStart, gc);
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!jtf.getText().equalsIgnoreCase("david")) {
gvp.showWarningIcon(jtf);
}
}
});
f.addComponentListener(new ComponentAdapter() {//so wjen frame is resized icons follow
@Override
public void componentResized(ComponentEvent ce) {
super.componentResized(ce);
gvp.refreshLocations();
}
});
f.setGlassPane(gvp);
f.pack();
f.setVisible(true);
gvp.setVisible(true);
}
});
}
}
class GlassValidationPane extends JPanel {
private HashMap<Component, JLabel> warningLabels = new HashMap<>();
private ImageIcon warningIcon;
public GlassValidationPane() {
setLayout(null);//this is the exception to the rule case a layoutmanager might make setting Jlabel co-ords harder
setOpaque(false);
Icon icon = UIManager.getIcon("OptionPane.warningIcon");
int imgW = icon.getIconWidth();
int imgH = icon.getIconHeight();
BufferedImage img = ImageUtilities.getBufferedImageOfIcon(icon, imgW, imgH);
warningIcon = new ImageIcon(ImageUtilities.resize(img, 24, 24));
}
void showWarningIcon(Component c) {
if (warningLabels.containsKey(c)) {
return;
}
JLabel label = new JLabel();
label.setIcon(warningIcon);
//int x=c.getX();//this will make it insode the component
int x = c.getX() - warningIcon.getIconWidth();//this makes it appear outside/next to component if space
int y = c.getY();
label.setBounds(x, y, warningIcon.getIconWidth(), warningIcon.getIconHeight());
add(label);
revalidate();
repaint();
warningLabels.put(c, label);
}
public void removeWarningIcon(Component c) {
for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
Component component = entry.getKey();
JLabel jLabel = entry.getValue();
if (component == c) {
remove(jLabel);
revalidate();
repaint();
break;
}
}
warningLabels.remove(c);
}
public void refreshLocations() {
for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
Component c = entry.getKey();
JLabel label = entry.getValue();
//int x=c.getX();//this will make it insode the component
int x = c.getX() - label.getIcon().getIconWidth();//this makes it appear outside/next to component
int y = c.getY();
label.setBounds(x, y, label.getIcon().getIconWidth(), label.getIcon().getIconHeight());
revalidate();
repaint();
}
}
}
class ImageUtilities {
public static BufferedImage resize(BufferedImage image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
}
public static BufferedImage getBufferedImageOfIcon(Icon icon, int imgW, int imgH) {
BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
return img;
}
}
Если вы ищете более зрелую библиотеку, взгляните на JXLayer - Проверочные оверлеи и Проверочные оверлеи с использованием стеклянной панели.
Также может потребоваться прочитать здесь, который показывает много способов проверки данных текстовых полей (кроме нажатия кнопки), таких как DocumentFilter
а также InputVerifier
и т.п.