"weiches" Bewegen von einem JComponent

  • Themenstarter Themenstarter bananenkasper
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
B

bananenkasper

Gast
Hallo Leute,

ich programmiere gerade ein kleines Spiel, bei dem sich Textfelder über einem Hintergrund bewegen sollen.

Momentan ist es so verwirklicht, dass ich JLabels mit "setBounds()" neu positioniere, und dann "repaint()" des "Hintergrund-Component" aufrufe.

Das geht soweit auch ganz gut, nur leider ist das ganze halt recht "ruckelig".

Aber wenn ich das JLabel pixelweise neu positioniere und entsprechend oft "repaint()" aufrufe, leidet die performance gewaltig.

Gibt es da eine elegantere Lösung?


MFG
 
Du brauchst keinen "repaint", "setLocation" sollte genügen.
Versuch mal das Hintergrundbild im Bildpanel so zu malen:
Code:
   @Override
    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawImage(img, r.x, r.y, r.width + r.x, r.height + r.y,
                r.x, r.y, r.width + r.x, r.height + r.y, null);
    }
Die animierten JLabels werden zu diesem Bildpanel hinzugefügt.
"setLocation" bewirkt einen repaint, und zwar nur von dem Rechteck, das aktualisiert werden muss.
In "paintComponent" kann man dieses Rechteck mittels "getClipBounds" abfragen.
Der drawImage(..)-Aufruf, mit den angegebenen Parametern, zeichnet nur den
Teil des Bildes neu, der sich mit dem Cliprechteck überschneidet.
 
André Uhres hat gesagt.:
Du brauchst keinen "repaint", "setLocation" sollte genügen.
Versuch mal das Hintergrundbild im Bildpanel so zu malen:
Code:
   @Override
    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawImage(img, r.x, r.y, r.width + r.x, r.height + r.y,
                r.x, r.y, r.width + r.x, r.height + r.y, null);
    }
Die animierten JLabels werden zu diesem Bildpanel hinzugefügt.
"setLocation" bewirkt einen repaint, und zwar nur von dem Rechteck, das aktualisiert werden muss.
In "paintComponent" kann man dieses Rechteck mittels "getClipBounds" abfragen.
Der drawImage(..)-Aufruf, mit den angegebenen Parametern, zeichnet nur den
Teil des Bildes neu, der sich mit dem Cliprechteck überschneidet.


Clipping in Kombination mit Überschreiben von "paint()"....
Hatte ich auch schonmal in Betracht gezogen.
Erschien mir bis dato aber zu aufwending.
Aber wenn es so simple ist 😉

Vielen Dank für den Tipp!!
werde es gleich mal probieren!
 
wie würde das gehen wenn ich kein bild zeichen möchte (drawImage()) sondern ein JLabel?
 
Irgendwie hätte ich das jetzt alles komplett anders verstanden - aber André Uhres scheint seine Kristallkugel frisch geputzt zu haben :wink:

Du willst also ein JPanel, bei dem in Hintergrund ein JLabel zu sehen ist, und auf diesem JPanel sollen sich JTextFields rumbewegen?!
 
Marco13 hat gesagt.:
Du willst also ein JPanel, bei dem in Hintergrund ein JLabel zu sehen ist,
und auf diesem JPanel sollen sich JTextFields rumbewegen?!
Gemäss seinem ersten Beitrag sind die Textfelder JLabels und gemäss seinem letzten Beitrag
ist der "Hintergrund-Component" auch ein JLabel. Also JLabels bewegen sich auf einem JLabel
(es geht aber auch mit JTextFields, die sich auf einem JLabel bewegen).
Beispiel: TimerAnimation2.jar (Quellcode im jar)
 
André Uhres hat gesagt.:
Marco13 hat gesagt.:
Du willst also ein JPanel, bei dem in Hintergrund ein JLabel zu sehen ist,
und auf diesem JPanel sollen sich JTextFields rumbewegen?!
Gemäss seinem ersten Beitrag sind die Textfelder JLabels und gemäss seinem letzten Beitrag
ist der "Hintergrund-Component" auch ein JLabel. Also JLabels bewegen sich auf einem JLabel
(es geht aber auch mit JTextFields, die sich auf einem JLabel bewegen).
Beispiel: TimerAnimation2.jar (Quellcode im jar)


Es gibt ein Hintergrundbild, dass ich in ein JLabel gepackt habe.
Dann gibt es Textfelder, die auch JLabels sind und sich auf diesem Hintergrund bewegen.

genaugenommen ist der Hintergrund ein "JLayerdPane",

es bewegt sich also ein JComponent "in" bzw. auf einem anderen JComponent.

Die Textfelder erstelle ich mit "setText()", "setFont()", "setBorder()", "setBackground()", etc.
Sie stellen reinen Text dar, der allerdings auf Blöcken "JLabel.setOpaque(true)" abgebildet ist.

Was ich noch nicht verstanden habe ist, wie ich diese "Textfeld-JLabels" zeichnen kann.
Ich meine es gibt ja eben keine Funktion "Graphics.drawComonent()"...
 
Andre,

ich habe mal dein Beispielcode etwas modifiziert.
Da kommst du ja ausschliesslich mit "setLocation()" aus...

Will ich ein JLabel positionieren klappt das aber nicht.
Warum ist das so?

Code:
public class GuiTest extends JPanel {

	private class TimerAnimation extends JLabel implements ActionListener {
		// public class TimerAnimation extends JTextField implements
		// ActionListener {
		int deltaX = 2, deltaY = 3, directionX = 1, directionY = 1;

		public TimerAnimation(final int startX, final int startY,
				final int deltaX, final int deltaY, int directionX,
				int directionY, int delay) {
			this.deltaX = deltaX;
			this.deltaY = deltaY;
			this.directionX = directionX;
			this.directionY = directionY;
			// setText("O");
			try {
				setIcon(new ImageIcon(new URL(
						"http://www.java-forum.org/de/userfiles/"
								+ "user3690/redbull.gif")));
			} catch (MalformedURLException ex) {
				ex.printStackTrace();
			}
			setSize(getPreferredSize());
			setLocation(startX, startY);
			new javax.swing.Timer(delay, this).start();
		}

		public void actionPerformed(final ActionEvent e) {
			Container parent = getParent();
			// Determine next X position
			int nextX = getLocation().x + (deltaX * directionX);
			if (nextX < 0) {
				nextX = 0;
				directionX *= -1;
			}
			if (nextX + getSize().width > parent.getSize().width) {
				nextX = parent.getSize().width - getSize().width;
				directionX *= -1;
			}
			// Determine next Y position
			int nextY = getLocation().y + (deltaY * directionY);
			if (nextY < 0) {
				nextY = 0;
				directionY *= -1;
			}
			if (nextY + getSize().height > parent.getSize().height) {
				nextY = parent.getSize().height - getSize().height;
				directionY *= -1;
			}
			setLocation(nextX, nextY); // Move the label/textfield
		}

	}

	private class Picture extends JLabel {
		public Picture(final JFrame frame, final Image img) {
			setIcon(new ImageIcon(img));
			setOpaque(true);
			setBounds(75, 75, img.getWidth(frame), img.getHeight(frame));
			frame.setLayout(null);
			frame.add(this);// Picture component to the Frame:
		}
	}

	private class WordLabel extends JLabel {
		private final FontMetrics metrics;

		private final Font font = new Font("Sans", Font.BOLD, 24);

		WordLabel() {
			metrics = getFontMetrics(font);
			setFont(font);
			setForeground(Color.GREEN);
			setBackground(Color.BLUE);
			setBorder(BorderFactory.createLineBorder(Color.RED));
		}

		public int getWidth() {
			return metrics.stringWidth(getText()) + 4;
		}

		public int getHeight() {
			return metrics.getHeight() + 4;
		}
	}

	private final WordLabel word = new WordLabel();

	private final JFrame frame = new JFrame();

	private final Picture pic;

	private final Timer timer;

	private int y;

	GuiTest() {
		timer = new Timer(100, new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				y++;
				word.setLocation(0, y);

			}
		});
		timer.start();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(400, 400);
		frame.setLocationRelativeTo(null);
		URL url = null;
		Image img = null;// background image
		try {
			url = new URL("http://www.java-forum.org/de/userfiles/user3690/"
					+ "bottle-water-on-forehead.gif");
		} catch (MalformedURLException ex) {
			ex.printStackTrace();
		}
		try {
			img = ImageIO.read(url);
		} catch (IOException e) {
			e.printStackTrace();
		}
		pic = new Picture(frame, img);
		pic.add(word);
		pic.add(new TimerAnimation(10, 10, 2, 3, 1, 1, 10));
		pic.add(new TimerAnimation(300, 100, 3, 2, -1, 1, 20),
				BorderLayout.SOUTH);
		frame.setVisible(true);
		word.setText("hallo");
	}

	private static void createAndShowGui() {
		new GuiTest();
	}

	public static void main(final String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGui();
			}
		});
	}
}
 
Versuch's mal so:
Code:
private class WordLabel extends JLabel {
    private final Font font = new Font("Sans", Font.BOLD, 24);
    WordLabel() {
        setFont(font);
        setForeground(Color.GREEN);
        setBackground(Color.BLUE);
        setBorder(BorderFactory.createLineBorder(Color.RED));
    }
    public void setText(String text) {
        super.setText(text);
        setSize(getPreferredSize());
    }
}
 
André Uhres hat gesagt.:
Versuch's mal so:
Code:
private class WordLabel extends JLabel {
    private final Font font = new Font("Sans", Font.BOLD, 24);
    WordLabel() {
        setFont(font);
        setForeground(Color.GREEN);
        setBackground(Color.BLUE);
        setBorder(BorderFactory.createLineBorder(Color.RED));
    }
    public void setText(String text) {
        super.setText(text);
        setSize(getPreferredSize());
    }
}

wofür ist das gut?

EDIT:
ich habe zwar keinen Schimmer warum, aber so funktioniert es!!
Tausend Dank, Andre !!
 
bananenkasper hat gesagt.:
wofür ist das gut?
Mit "setText(text)" wird die "PreferredSize" von "WordLabel" automatisch angepasst.
Da "Picture" keinen LayoutManager benutzt, müssen wir die "Size" von "WordLabel" manuell mit "setSize(..)" setzen.
Man könnte es natürlich auch so machen:
Code:
...
   private class WordLabel extends JLabel {
      private final Font font = new Font("Sans", Font.BOLD, 24);
      WordLabel() {
         setFont(font);
         setForeground(Color.GREEN);
         setBackground(Color.BLUE);
         setBorder(BorderFactory.createLineBorder(Color.RED));
      }
   }
...
   GuiTest() {
...
      word.setText("hallo");
      word.setSize(word.getPreferredSize());
   }
...
}

Die Klasse "TimerAnimation" geht übrigens ähnlich vor. Nach "setIcon" wird "setSize(getPreferredSize())" aufgerufen:
Code:
  private class TimerAnimation extends JLabel implements ActionListener {
...
      public TimerAnimation(...) {
...
         try {
            setIcon(...);
         } catch (MalformedURLException ex) {
            ex.printStackTrace();
         }
         setSize(getPreferredSize());//<-------------------
...
      }
 
Hi,
ich lese gerade, dass bei Swing paintComponent statt paint überschrieben werden sollte. Ich hatte bislang immer paint überschrieben und keine Probleme. Was ist der Grund für paintComponent()?

Viele Grüße,
Wolfgang
 
Wolfgang Lenhard hat gesagt.:
ich lese gerade, dass bei Swing paintComponent statt paint überschrieben werden sollte. ... Was ist der Grund für paintComponent()?
"paint" ruft paintComponent, paintBorder und paintChildren auf, in dieser Reihenfolge.
Es gibt im Allgemeinen keinen Grund paintBorder() oder paintChildren() zu überschreiben.
Dies macht es den Programmen einfacher, nur den Teil des Zeichnens zu überschreiben,
den sie erweitern müssen: paintComponent.
Siehe auch: Malen in AWT und Swing: Die Malmethoden
 
André Uhres hat gesagt.:
bananenkasper hat gesagt.:
wofür ist das gut?
Mit "setText(text)" wird die "PreferredSize" von "WordLabel" automatisch angepasst.
Da "Picture" keinen LayoutManager benutzt, müssen wir die "Size" von "WordLabel" manuell mit "setSize(..)" setzen.

Ok das ist soweit klar...
Aber warum wird das Label nach einem "setLocation()" nicht neu gezeichnet, wenn ich nicht voher setSize() aufrufe?
 
bananenkasper hat gesagt.:
Aber warum wird das Label nach einem "setLocation()" nicht neu gezeichnet, wenn ich nicht voher setSize() aufrufe?
Es wird so gross gezeichnet wie sein Size ist. Wenn niemand den Size setzt,
dann ist er (0,0) und man sieht dann natürlich nix :wink:
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben