Migotanie rysowanych figur w awt

0

Piszę sobie taki program:

import java.awt.*;
import java.awt.event.*;


public class Kropa extends Frame{
	int liczX=0, liczY=0;
	int[] iksy = new int[20];
	int[] igreki = new int[20];
	
	int mouseX, mouseY, pointX, pointY;

	Kropa(String name){
		super(name);
		setBounds(200, 100, 800, 600);
		setMinimumSize(new Dimension(500, 400));
		Zdarzenia obsZd = new Zdarzenia();
		addMouseMotionListener(obsZd);
		addMouseListener(obsZd);
		addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent we){
                System.exit(0);
            }
        });
	}
	
	private boolean isInBelt(int x, int y){
		return mouseX<x+6 && mouseX>x-6 && mouseY>y-6 && mouseY<y+6;
	}
	
	public void paint(Graphics g){
		g.setColor(Color.RED);
		for(int i=0; i<liczX; i++){ 
			if(isInBelt(iksy[i], igreki[i])){ 
				g.fillOval(iksy[i]-4, igreki[i]-4, 9, 9);
				g.drawString(iksy[i]+" "+igreki[i], iksy[i]+10, igreki[i]-10);
			}else g.fillOval(iksy[i]-2, igreki[i]-2, 5, 5);
		}
		if(pointX!=-1&&pointY!=-1){
			g.fillOval(pointX-4, pointY-4, 9, 9);
			g.drawString(pointX+" "+pointY, pointX+10, pointY-10);
		}
		g.drawPolyline(iksy, igreki, liczX);
	}
		
	public static void main(String[] args){
		Kropa okno = new Kropa("moje okno");
		okno.setVisible(true);
	}

	class Zdarzenia extends MouseAdapter implements MouseMotionListener{
		
		public void mouseDragged(MouseEvent arg0){
			mouseX=pointX=arg0.getX();
			mouseY=pointY=arg0.getY();
			repaint();
		}

		public void mouseMoved(MouseEvent me){
			mouseX=me.getX();
			mouseY=me.getY();
			repaint();
		}
		
		public void mouseReleased(MouseEvent arg0){
			iksy[liczX++]=mouseX;
			igreki[liczY++]=mouseY;
			pointY=pointX=-1;
			repaint();
		}	
	}
}

Rysuje on łamaną linię wg. kliknięć użytkownika. Punkty załamania stanowią kropki, które stają się większe i wyświetlają swoje współrzędne po najechaniu na nie kursorem. Program działa jak należy, ale jego mankamentem jest migotanie figur przy ruchu kursora. Domyślam się w czym rzecz: metoda paint() jest wywoływana w trybie ciągłym przez metody mouseMoved() i mouseDragged(). O ile z mouseMoved mogę sobie poradzić(umieszczając w niej warunek odświeżenia tylko jeżeli kursor znajduje się w pobliżu którejś kropki) to pozostaje jeszcze kwestia mouseDragged która musi pracować w trybie ciągłym, bo właśnie rysowana kropka jest na bieżąco prezentowana na ekranie, a to wymaga nieustannej aktualizacji. Co zmienić, żeby rysowało obraz bez zająknięcia?

0

We wszystkich zdarzeniach dotyczących akcji myszy przemalowujesz całość, więc się nie dziw, że migocze; Skoro zaktualizować musisz jedynie tę kropkę spod kursora, to kasuj tylko ją, a nie całą zawartość.

0
furious programming napisał(a):

Skoro zaktualizować musisz jedynie tę kropkę spod kursora, to kasuj tylko ją, a nie całą zawartość.

W jaki sposób to zrobić?

0

Dwa tryby rysowanie zależne od pola logicznego, np drawAll. Metoda mouseReleased ustawia drawAll na true, a metoda mouseDragged na false. Metoda paint, w zależności od wartości drawAll, rysuje wszystko lub tylko nowy obiekt.

0
bogdans napisał(a):

Dwa tryby rysowanie zależne od pola logicznego, np drawAll. Metoda mouseReleased ustawia drawAll na true, a metoda mouseDragged na false. Metoda paint, w zależności od wartości drawAll, rysuje wszystko lub tylko nowy obiekt.

Ja chyba czegoś nie rozumiem. To co widzimy na ekranie pochodzi tylko i wyłącznie z ostatniego wywołania metody paint, zgadza się? W jaki więc sposób mogę zaprojektować wersję metody która uwzględnia rysowanie tylko jednej kropki a innych nie? Te nieuwzględnione zostaną przecież zamalowane przez kolor tła. Ja chcę żeby wszystkie kropki były widoczne cały czas: te już istniejące i ta obecnie rysowana zależna od pozycji kursora.

0

To co widzimy na ekranie pochodzi tylko i wyłącznie z ostatniego wywołania metody paint, zgadza się?

Gdyby tak miało być, to czemu miały by służyć wywołania repaint() we wszystkich trzech metodach dotyczących akcji myszy..?

Ja chcę żeby wszystkie kropki były widoczne cały czas: te już istniejące i ta obecnie rysowana zależna od pozycji kursora.

Obecnie rysowana, czy przyklejona do kursora przed zwolnieniem przycisku myszy?

Jeśli chodzi o tryby rysowania to już wyjaśnił Ci @bogdans - deklarujesz sobie zmienną logiczną, której wartość ustawiana jest w zdarzeniach myszy; W zdarzeniu mouseReleased ustawiasz wartość zmiennej na true i wywołujesz repaint, a w zdarzeniu mouseDragged wpisujesz do niej false i też wywołujesz repaint; Natomiast w zdarzeniu paint sprawdzasz stan tej zmiennej, i jeśli zawiera false to aktualizujesz odpowiedni fragment, a jeśli true - cały obraz.

0
furious programming napisał(a):

Gdyby tak miało być, to czemu miały by służyć wywołania repaint() we wszystkich trzech metodach dotyczących akcji myszy..?

W opisie kodu zaznaczyłem, że chodzi mi tylko o mouseDragged, z resztą wiem jak sobie poradzić. Zresztą co w tym dziwnego, że metoda repaint jest we wszystkich trzech metodach? One nie działają jednocześnie, a każda wprowadza jakieś zmiany w danych.

Obecnie rysowana, czy przyklejona do kursora przed zwolnieniem przycisku myszy?

Obecnie rysowana = przyklejona do kursora aż do chwili puszczenia przycisku. Jeżeli wam się chce to kod możecie wrzucić do kompilatora, jest w pełni działający, zobaczycie o co chodzi.

Jeśli chodzi o tryby rysowania to już wyjaśnił Ci @bogdans - deklarujesz sobie zmienną logiczną, której wartość ustawiana jest w zdarzeniach myszy; W zdarzeniu mouseReleased ustawiasz wartość zmiennej na true i wywołujesz repaint, a w zdarzeniu mouseDragged wpisujesz do niej false i też wywołujesz repaint; Natomiast w zdarzeniu paint sprawdzasz stan tej zmiennej, i jeśli zawiera false to aktualizujesz odpowiedni fragment, a jeśli true - cały obraz.

Ja w pełni rozumiem jak zaprojektować zależne od flagi bloki kodu w metodzie paint, tylko nie wiem co się ma w nich znaleźć. Jeżeli zrobię coś takiego:

 boolean flaga;
	paint(Graphics g){
		if(flaga){                                                 //podczas mouseMoved
			rysuj punkty z tablica[];
		}else{                                                    //podczas mouseDragged
			rysuj kolejny punkt i dodaj do tablica[];
		}
	}

będzie rysowało albo punkty istniejące, albo obecnie trzymany przez kursor(do chwili puszczenia). Jak można zaktualizować tylko fragment obrazu?

2

Źle sobie wyobrażasz działanie metody paint, jeśli nie ma w niej wywołania super.paint(g) ani samodzielnie nie "sprzątasz", to metoda paint dorysowuje do tego co już jest narysowane. Gdyby okno programu było cały czas na wierzchu i nie zmieniało rozmiaru, to w ogóle nie byłaby potrzebna zmiana stanu rysowania, program mógłby cały czas być w stanie rysowania tylko nowych rzeczy. Wypróbuj taki program (tylko nowe rzeczy są rysowane), zminimalizuj okno, a potem przywróć.

0

Napisałem taki program testowy:

boolean flag=true;

public void paint(Graphics g){
		//super.paint(g);
		if(flag){
			g.setColor(Color.RED);
			g.fillRect(120, 140, 50, 50);
		}else{
			g.setColor(Color.BLUE);
			g.fillOval(100, 100, 60, 60);
		}
	}

public void mouseMoved(MouseEvent me){
			if(me.getX()>400){
				flag=false;
				repaint();
			}else{
				flag=true;
				repaint();
			}
		}

I niestety nie działa tak jakby to wynikało z twojego tekstu. Za każdym razem rysuje tylko czerwony kwadrat albo tylko niebieskie koło i to niezależne czy jest wywołanie super.paint(g) czy nie. Odkryłem natomiast, że faktycznie można wywołać repaint() z parametrami wymiarów i współrzędnych kwadratowego wycinka który ma być ponownie wyrysowany, ale na razie jest mi to nieprzydatne.
Co jest nie tak z powyższym kodem? Co daje wywołanie super.paint() w AWT (tylko proszę nie pisać: "przekazuje kontekst graficzny do przesłoniętej metody", bo to wiem, ale jakie są tego skutki).

0

Łżesz :), rysuje jedno i drugie.

import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
import java.awt.*;

public class Test extends JFrame
{
    private CustomPanel previous = null;
    private Color color;
    public static void main(String[] args)
    {
        new Test();
    }
    public Test()
    {
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        add(new CustomPanel());
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }
 
    class CustomPanel extends JPanel implements MouseMotionListener
    {
        private boolean flag = false;
        public CustomPanel()
        {
            addMouseMotionListener(this);
            setPreferredSize(new Dimension(400,400));
        }
        public void paint(Graphics g)
        {
        //super.paint(g);
        if(flag){
            g.setColor(Color.RED);
            g.fillRect(120, 140, 50, 50);
        }
        else
        {
            g.setColor(Color.BLUE);
            g.fillOval(100, 100, 60, 60);
        }
    }        
        //-------------------- 
        public void mouseMoved(MouseEvent me)
        {
            if(me.getX()>200)
            {
                flag = false;
                repaint();
            }
            else
            {
                flag = true;
                repaint();
            }        
        }
        //--------------------
        public void mouseDragged(MouseEvent e)
        { 
        }
    }
}

super.paint(g), to wywołanie metody paint z klasy bazowej (w powyższym przykładzie z klasy JPanel). W klasie JPanel metoda paint(g) wypełnia cały obszar kolorem tła, zatem zamazuje co było.

0

To wykonaj sobie mocium panie to :D :

import java.awt.*;
import java.awt.event.*;

public class Test extends Frame{

	boolean flag = true;
	
	Test(String name){
		super(name);
		setBounds(200, 100, 800, 600);
		setMinimumSize(new Dimension(500, 400));
		Zdarzenia obsZd = new Zdarzenia();
		addMouseMotionListener(obsZd);
		addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent we){
                System.exit(0);
            }
        });
	}
	

	public void paint(Graphics g){
		super.paint(g);
		if(flag){
			g.setColor(Color.RED);
			g.fillRect(120, 140, 50, 50);
		}else{
			g.setColor(Color.BLUE);
			g.fillOval(100, 100, 60, 60);
		}
	}
		
	public static void main(String[] args){
		Test okno = new Test("moje okno");
		okno.setVisible(true);
	}

	class Zdarzenia extends MouseMotionAdapter{
				
		public void mouseMoved(MouseEvent me){
			if(me.getX()>400){
				flag=false;
				repaint();
			}else{
				flag=true;
				repaint();
			}
		}		
	}
}

Chciałbym zauważyć że ty rozszerzyłeś JFrame, czyli zrobiłeś to w Swingu. A ja rysowałem jedynie na Frame AWT i do tego bez panela. Czyli co, wychodzi na to, że w AWT bez wspomagania Swinga zawsze rysuje całość, chyba, że wywołasz repaint z parametrem?

0

Nie wiem, z czystego AWT już wieki nie korzystam, i nie mam zamiaru wracać.

0

Jeszcze jedno pytanie, żeby nie zakładać nowego wątku: jaka jest różnica między wywołaniem metody paint() a paintComponent() w Swingu?

0

Przeczytaj w dokumentacji http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html opis metody paint.

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