Jtextfields on Top of Active Drawing on Jpanel, Threading Problems

JTextFields on top of active drawing on JPanel, threading problems

NewTest extends JPanel; but because you're not painting every pixel on each call to paintComponent(), you need to invoke the super-class's method and erase the old drawing:

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = this.getWidth();
int height = this.getHeight();
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
...
}

Addendum: As you note, setting the background color in the constructor precludes the need to fill the panel in paintComponent(), while super.paintComponent() allows the text field(s) to function correctly. As you observe, the proposed workaround is fragile. Instead, simplify the code and optimize as warranted. For example, you may not need the complication of insets, extra buffers and a component listener.

Addendum 2: Note that super.paintComponent() calls the UI delegate's update() method, "which fills the specified component with its background color (if its opaque property is true)." You can use setOpaque(false) to preclude this.

Animation Test

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;

/** @see http://stackoverflow.com/questions/3256941 */
public class AnimationTest extends JPanel implements ActionListener {

private static final int WIDE = 640;
private static final int HIGH = 480;
private static final int RADIUS = 25;
private static final int FRAMES = 24;
private final Timer timer = new Timer(20, this);
private final Rectangle rect = new Rectangle();
private BufferedImage background;
private int index;
private long totalTime;
private long averageTime;
private int frameCount;

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
new AnimationTest().create();
}
});
}

private void create() {
JFrame f = new JFrame("AnimationTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.start();
}

public AnimationTest() {
super(true);
this.setOpaque(false);
this.setPreferredSize(new Dimension(WIDE, HIGH));
this.addMouseListener(new MouseHandler());
this.addComponentListener(new ComponentHandler());
}

@Override
protected void paintComponent(Graphics g) {
long start = System.nanoTime();
super.paintComponent(g);
int w = this.getWidth();
int h = this.getHeight();
g.drawImage(background, 0, 0, this);
double theta = 2 * Math.PI * index++ / 64;
g.setColor(Color.blue);
rect.setRect(
(int) (Math.sin(theta) * w / 3 + w / 2 - RADIUS),
(int) (Math.cos(theta) * h / 3 + h / 2 - RADIUS),
2 * RADIUS, 2 * RADIUS);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
g.setColor(Color.white);
if (frameCount == FRAMES) {
averageTime = totalTime / FRAMES;
totalTime = 0; frameCount = 0;
} else {
totalTime += System.nanoTime() - start;
frameCount++;
}
String s = String.format("%1$5.3f", averageTime / 1000000d);
g.drawString(s, 5, 16);
}

@Override
public void actionPerformed(ActionEvent e) {
this.repaint();
}

private class MouseHandler extends MouseAdapter {

@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
JTextField field = new JTextField("test");
Dimension d = field.getPreferredSize();
field.setBounds(e.getX(), e.getY(), d.width, d.height);
add(field);
}
}

private class ComponentHandler extends ComponentAdapter {

private final GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
private final GraphicsConfiguration gc =
ge.getDefaultScreenDevice().getDefaultConfiguration();
private final Random r = new Random();

@Override
public void componentResized(ComponentEvent e) {
super.componentResized(e);
int w = getWidth();
int h = getHeight();
background = gc.createCompatibleImage(w, h, Transparency.OPAQUE);
Graphics2D g = background.createGraphics();
g.clearRect(0, 0, w, h);
g.setColor(Color.green.darker());
for (int i = 0; i < 128; i++) {
g.drawLine(w / 2, h / 2, r.nextInt(w), r.nextInt(h));
}
g.dispose();
System.out.println("Resized to " + w + " x " + h);
}
}
}

Adding JTextfield to JPanel over paint layer

Click anywhere on this AnimationTest to add a text field. Note the use of invokeLater() and the call to super.paintComponent(g). Resize the frame to see how the default layout works.

Drawing a rectangle in a JPanel, snapped to the nearest x pixels

Well, I assume you have two Point objects (start, end), so you can create a little method to adjust the values.

Here is a simple example to get your started:

import java.awt.*;
public class Main
{
public static void main(String[] args) throws Exception
{
Point start = new Point(103, 105);
Point end = new Point(203, 205);

adjust(start, 4, true);
adjust(end, 4, false);

System.out.println(start);
System.out.println(end);
}

public static void adjust(Point p, int snap, boolean snapLower)
{
int modX = p.x % snap;
p.x = snapLower ? p.x - modX : p.x + snap - modX;

int modY = p.y % snap;
p.y = snapLower ? p.y - modY : p.y + snap - modY;
}
}

Java Swing: paint under components of a JPanel?

I think you want to override paintComponent() not paint().

paint() usually doesn't do any painting but delegates to paintComponent(), paintBorder() and paintChildren().

see javax.swing.JComponent.paint() for more info

Example:

  @Override
public void paintComponent(Graphics g) {
/* your draw code here */
}

You could call super.paintComponent(g); to draw the component as usual but can be omitted.

Swing active rendering efficiency or how to combine active rendering with gui widgets

I have a few observations:

  1. Your improved repaintDrawTime() is very readable, but it is a micro-benchmark and subject to the vagaries of the host OS. I can't help wondering if the XP results are an artifact of that system's limited clock resolution. I see very different results on Windows 7 and Ubuntu 10.

  2. If you don't use a null layout, you won't need the extra panel; the default layout for JPanel is FlowLayout, and f.add(this) simply adds it to the center of the frame's default BorderLayout.

  3. Repeated constructor invocations can be time consuming.

    Consider replacing

    g.setColor(new Color(0, 128, 0, 100));

    with

    private static final Color color = new Color(0, 128, 0, 100);
    ...
    g.setColor(color);

    Alternatively, a simple color lookup table, may be useful, e.g.

    private final Queue<Color> clut = new LinkedList<Color>();

Prevent JPanel from redrawing background on repaint() calls

I'd recommend to build your code upon the already existent code provided by the API instead of messing with it. Just store the image as BufferedImage. This allows you to display it using an ImageIcon, so it's an additional simplification. This allows you to update single pixels without any hassles with the API. If you absolutely insist on excluding the JPanel from the repaint-routine, this question might help.

In general:

Follow the conventional use of the API. If you want to permanently store data of an image, us a BufferedImage. JComponents are supposed to be entirely overridden every time the frame is updated.



Related Topics



Leave a reply



Submit