Problem z metodą paintComponent()

0

W ramach nauki javy chciałem stworzyć aplikację, która rysowałaby kreski w odpowiednie strony, wraz z naciśnięciem konkretnego przycisku. Niestety wynik moich starań nie jest zadowalający, w momencie naciśnięcia przycisku tworzy się kolejna ramka i przycisk umieszczony na dole ramki pojawia się również na górze (nie wiem jak to możliwe), a poza tym rysowany jest jedynie ostatni etap animacji czyli kropka zamiast całej linii. Proszę Was o jakieś podpowiedzi związane z błędami popełnionymi przeze mnie w poniższym (skróconym) kodzie.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Animacja3
{
	JFrame ramka = new JFrame();
	int x = 400;
	int y = 200;
	Color losowyKolor;
	MojPanelRysunkowy MPR = new MojPanelRysunkowy();
	public static void main ( String[] args)
	{
		Animacja3 Animka = new Animacja3();
		Animka.postawieniePrzyciskow();
	}
	public void postawieniePrzyciskow()
	{
		
		ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JButton przyciskPD = new JButton("Prawo-Dol");
		przyciskPD.addActionListener(new PrzyciskPDListener());
		
		ramka.getContentPane().add(BorderLayout.SOUTH, przyciskPD);
		
		
		ramka.getContentPane().add(MPR);
		ramka.setSize(800,600);
		ramka.setVisible(true);
	}
	public void przesuwPD()
	{
		losowyKolor = rndKolor();
		for (int i = 0; i < 5; i++)
		{
			x++;
			y++;
			
			MPR.repaint();
			
			try
			{
				Thread.sleep(50);
			}
			catch (Exception ex){}
		}
	}
	class MojPanelRysunkowy extends JPanel
	{
		public void paintComponent(Graphics g)
		{
			Graphics2D g2d = (Graphics2D) g;
			g2d.setColor(losowyKolor);
			g2d.fillOval(x,y,10,10);
		}
	}
	public Color rndKolor()
	{
		int r = (int) (Math.random() * 256);
		int g = (int) (Math.random() * 256);
		int b = (int) (Math.random() * 256);
		Color kolor = new Color(r,g,b);
		return kolor;
	}
	class PrzyciskPDListener implements ActionListener
	{
		public void actionPerformed(ActionEvent zdarzenie)
		{
			przesuwPD();
		}
	}
}
1

paintComponent rysuje cały twój komponent- od początku do końca. Jeśli chcesz, żeby było więcej kropek narysowanych - to wszystkie one musza być narysowane w paintComponent. (I to za każdym razem jak mtoda jst wywołana).
Czyli: musisz pętle for przenieśc do paintComponent.
Btw. to normalne - wszystkie standardowe środowiska UI tak działają.

0

Dziękuję bardzo za pomoc, za chwilę poprawię kod, nie wiem czy to wpłynie na ponownie wyskakujące przyciski w innym miejscu niż planowałem (niewciskalne), ale z pewnością mi pomoże.
Czy w takim wypadku powinienem wstawić paintComponent() do klasy wewnętrznej przycisku?

0

Po poprawkach program działa o tyle lepiej, że dla jednego przycisku rysuje się linia, jednak nie mam pomysłu jak dodać inne metody paintComponent() w innych przyciskach i czemu wciąż przy pierwszym kliknięciu tworzy się jakby drugi panel.
Proszę o wszelkie rady i pomysły.

0

Za pomocą pętli warunkowych if (wiem, że lepiej byłoby użyć switcha, ale to był tylko test), udało mi się wyłonić z paintComponent(), elementy potrzebne dla każdego przycisku, teraz pozostaje mi tylko problem ramki JPanel tworzącej się od nowa przy każdym kliknięciu, oraz faktu, że dodatkowo rysują mi się nowe przyciski (które nie działają). Czy ktoś z Was zauważył jakieś błędy w kodzie?

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Animacja3
{
	int a;
    JFrame ramka = new JFrame();
    int x = 400;
    int y = 200;
    Color losowyKolor;
    MojPanelRysunkowy MPR = new MojPanelRysunkowy();
    public static void main ( String[] args)
    {
        Animacja3 Animka = new Animacja3();
        Animka.postawieniePrzyciskow();
    }
    public void postawieniePrzyciskow()
    {
        ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton przyciskPD = new JButton("Prawo-Dol");
        przyciskPD.addActionListener(new PrzyciskPDListener());
		
		JButton przyciskPG = new JButton("Prawo-Gora");
		przyciskPG.addActionListener(new PrzyciskPGListener());
		
		JButton przyciskLD = new JButton("Lewo-Dol");
		przyciskLD.addActionListener(new PrzyciskLDListener());
		
		JButton przyciskLG = new JButton("Lewo-Gora");
		przyciskLG.addActionListener(new PrzyciskLGListener());
		
		ramka.getContentPane().add(BorderLayout.NORTH, przyciskPG);
        ramka.getContentPane().add(BorderLayout.SOUTH, przyciskPD);
		ramka.getContentPane().add(BorderLayout.WEST, przyciskLD);
		ramka.getContentPane().add(BorderLayout.EAST, przyciskLG);
 
        ramka.getContentPane().add(MPR);
        ramka.setSize(800,600);
        ramka.setVisible(true);
    }
      class MojPanelRysunkowy extends JPanel
    {
        public void paintComponent(Graphics g)
        {
            Graphics2D g2d = (Graphics2D) g;
            losowyKolor = rndKolor();
			g2d.setColor(losowyKolor);
            
			if (a == 1)
			{
				for (int i = 0; i < 5; i++)
				{
					g2d.fillOval(x,y,10,10);
					x++;
					y++;
		
					try
					{
						Thread.sleep(50);
					}
					catch (Exception ex){}
				}
			}
			if (a == 2)
			{
				for (int i = 0; i < 5; i++)
				{
					g2d.fillOval(x,y,10,10);
					x++;
					y--;
		
					try
					{
						Thread.sleep(50);
					}
					catch (Exception ex){}
				}
			}
			if (a == 3)
			{
				for (int i = 0; i < 5; i++)
				{
					g2d.fillOval(x,y,10,10);
					x--;
					y++;
		
					try
					{
						Thread.sleep(50);
					}
					catch (Exception ex){}
				}
			}
			if (a == 4)
			{
				for (int i = 0; i < 5; i++)
				{
					g2d.fillOval(x,y,10,10);
					x--;
					y--;
		
					try
					{
						Thread.sleep(50);
					}
					catch (Exception ex){}
				}
			}
        }
    }
    public Color rndKolor()
    {
        int r = (int) (Math.random() * 256);
        int g = (int) (Math.random() * 256);
        int b = (int) (Math.random() * 256);
        Color kolor = new Color(r,g,b);
        return kolor;
    }
    class PrzyciskPDListener implements ActionListener
    {
        public void actionPerformed(ActionEvent zdarzenie)
        {
			a = 1;
            MPR.repaint();
        }
    }
	class PrzyciskPGListener implements ActionListener
	{
		public void actionPerformed(ActionEvent zdarzenie)
		{
			a = 2;
			MPR.repaint();
		}
	}
	class PrzyciskLDListener implements ActionListener
	{
		public void actionPerformed(ActionEvent zdarzenie)
		{
			a = 3;
			MPR.repaint();
		}
	}
	class PrzyciskLGListener implements ActionListener
	{
		public void actionPerformed(ActionEvent zdarzenie)
		{
			a = 4;
			MPR.repaint();
		}
	}
}
1

Kilka uwag:

  1. W paintComponent dokumentacji jest takie coś:
    ` 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.`
      i to Ci się dzieje.
      Proste rozwiązanie to albo wołanie g2d.fillRect(0,0, width, height)
      albo super.paintComponent(g); na poczatku paintComponent
  2. A teraz problem te całe:

 try
                    {
                        Thread.sleep(50);
                    }
                    catch (Exception ex){}

w paintComponent nie mają sensu - to jest techniczna masakra. Metoda paint ma odmalowac komponent i sie skończyć. Czekanie/animacja musi być poza nią.
Czyli raczej powinieneś robić :

 for (int i = 0; i < klatka; i++)
                {
                    g2d.fillOval(x,y,10,10);
                    x++;
                    y++;


                }

I gdzieś niezależnie wywoływać klatka++ i repaint.

Tu masz link jak się używa timera do takiej zmiany:
https://stackoverflow.com/questions/27833940/running-a-task-repeatedly-with-a-variable-interval

A ogólnie musisz ogarnąc jak działa paint w swingu. I niestety nie wiem co Ci polecić.
Ale tu masz przykład "animacji"
https://codereview.stackexchange.com/questions/29630/simple-java-animation-with-swing

0

Proponuję ten filmik. Facet świetnie pokazuje jak używać Swinga oraz jak sprawić żeby wszystkie nasze elementy pozostały zapisane a następnie wyświetlone.

0

Zdaję sobie sprawę, że w takiej apce cała animacja jest bez sensu, to pozostałość z nauki i rzeczywiście wymaga usunięcia. Jutro postaram się wdrożyć Twoje pomysły i przejrzę podane przez Was linki, dopiero zaczynam z graficznymi komponentami mimo, że przewaliłem ponad 400 stron książki o javie więc jakby się pojawiły problemy to dam Panom jeszcze znać. Dziękuję bardzo za porady.

0

Wywołanie super metody paintComponent() rzeczywiście poprawiło działanie programu, jednak ramka ulega całkowitemu wyczyszczeniu przy kliknięciu przez co można tworzyć tylko jedną linię, która dodatkowo "rośnie" dzięki inkrementowaniu zmiennej klatka. Jestem co raz bliżej rozwiązania problemu, ale podane linki nie są zbyt przydatne bo nie zawierają wykorzystania kilku przycisków w połączeniu z tworzeniem animacji/grafiki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1