Java - KeyListener

0

Dlaczego poniższy program nie działa poprawnie - nie wyłącza się po naciśnięciu klawisza "M"...?

import java.awt.BorderLayout;


public class Program extends JFrame implements KeyListener{

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		new Program().setVisible(true);
	}

	/**
	 * Create the frame.
	 */
	public Program() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
	}
	public void init(){
		this.addKeyListener(this); //dodanie KeyListener'a
	}

	@Override
	public void keyPressed(KeyEvent arg0) {
		if(arg0.getKeyCode() != KeyEvent.VK_M){ //przykładowa reakcja na wciśniecie klawisza M
			dispose(); ///zakończenie programu
		}
		
	}

	@Override
	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

}

1

Nie znam się na Javie, ale: gdzie jest wywoływany ten init?

2

OMG. Czy to jest tak trudno niektórym zrozumieć że NIE wpychamy wszystkiego do jednej klasy? Masz klasę która jest okienkiem JFrame, super, ale nie rób z niej dodatkowo KeyListenera, tylko napisz nową klasę! A problem dobrze zauważył @Karolaq - nigdzie nie wywołujesz swojego init() toteż nigdzie faktycznie tego key listenera nie dodajesz.

0
Karolaq napisał(a)

Nie znam się na Javie, ale: gdzie jest wywoływany ten init?

No tak.. gamoń ze mnie... Dzięki za spostrzegawczość :)

Shalom napisał(a)

OMG. Czy to jest tak trudno niektórym zrozumieć że NIE wpychamy wszystkiego do jednej klasy?

Spokojnie, proszę się nie denerwować :) Ja tak tylko tutaj pisząc przykładowo ten programik nie chciałem się bawić w kilka klas więc dlatego zrobiłem to w 1 klasie. Normalnie jeśli coś bardziej rozbudowanego to tworzę kilka klas oczywiście.

Dziękuję za pomoc i pozdrawiam!

0

Albo jeszcze jedno... Tylko od razu mówię nie krzyczcie na mnie, bo dopiero się uczę i nie moja wina że pewnych rzeczy nie ogarniam... Tak więc chodzi mi o to jak wygląda kwestia KeyListener'a, kiedy znajduje się on w osobnej klasie. Np. mam taki kod Apletu:

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JApplet;


public class Program extends JApplet implements Runnable{
	int x,y;
	Czytacz czytacz;
	public void init(){
		Thread thread = new Thread(this);
		thread.start();
		x=0;
		y=0;
		czytacz = new Czytacz();
		addKeyListener(czytacz);
	}
	public void run(){
		if(czytacz.klawisz == 'a'){
			x++;
			y++;
			repaint();
		}
	}
	public void paint(Graphics g){
		g.clearRect(0, 0, getSize().width, getSize().height);
		g.fillRect(x, y, 20, 20);
	}
}
class Czytacz implements KeyListener {

	char klawisz;
	public void keyPressed(KeyEvent e) {
		klawisz = e.getKeyChar();
	}
	public void keyReleased(KeyEvent e) {}
	public void keyTyped(KeyEvent e) {}
	
}

Chciałbym, zeby po nacisnieciu klawisza 'a', kwadracik się przesuwał na ukos... Jak widać utworzyłem w klasie Czytacz pole o nazwie 'klawisz', typu char. W metodzie keyPressed jest mu nadawana wartość. No i w głównej klasie odwołałem się do tego pola (jeśli = 'a' to się kwadrat przesuwa). Ale czy tak można się odwoływać do tego obiektu? Sądząc po działaniu programu (a raczej jego nie działaniu) wnioskuję że nie można... Jak to powinno być, żeby było dobrze...?
P.S.
I zdaję sobie sprawę, że wątek Thread lepiej by było zrobić w osobnej klasie, ale to jest tylko taki przykładowy kod żeby pokazać o co mi chodzi.

1

Źle, źle i jeszcze raz źle. Wszystko źle ;]

  1. Program "nie działa" = nic się nie wyświetla? Bo nie ustawiłeś nigdzie rozmiaru appletu ani setVisible(true)...
  2. Po co ci ten wątek tutaj?
  3. Wiesz na czym polega programowanie zdarzeniowe? Na tym właśnie żebyś nie musiał robić takiego chorego poolingu który ukradnie ci 100% czasu procesora...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JApplet;

public class Program extends JApplet {
    private static final long serialVersionUID = 1L;
    private int x = 0;
    private int y = 0;

    @Override
    public void init() {
        x = 0;
        y = 0;
        addKeyListener(new Czytacz(this));
        this.setPreferredSize(new Dimension(200, 200));
        this.setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
        g.clearRect(0, 0, getSize().width, getSize().height);
        g.fillRect(x, y, 20, 20);
    }

    public void incXY() {
        x++;
        y++;
        repaint();
    }
}

class Czytacz implements KeyListener {

    private final Program program;

    public Czytacz(Program p) {
        program = p;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyChar() == 'a') {
            program.incXY();
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

}
0
  1. Źle to sprecyzowałem. Aplet działał (tzn. wyświetlał kwadrat), ale nie reagował na naciśnięcie klawisza.
  2. Myślałem, że aby KeyListener "czytał klawiaturę" na bieżąco, to musi być to zrobione w wątku.
  3. Nie wiem na czym polega programowanie zdarzeniowe, ale szukam już lektury związanej z tym tematem :)
    i 4. Twój programik nie działa (przynajmniej nie tak jak chciałem), gdyż nie reaguje na naciśnięcie klawisza a (kwadrat się nie przesuwa).
0

Guzik prawda. Przyduś sobie 'a' i zapewniam cię że będzie się przesuwał (oczywiście o ile jest fokus na tym oknie). Nie zwykłem wstawiać kompletnych kodów które nie działają.

0
Shalom napisał(a)

Guzik prawda. Przyduś sobie 'a' i zapewniam cię że będzie się przesuwał (oczywiście o ile jest fokus na tym oknie). Nie zwykłem wstawiać kompletnych kodów które nie działają.

W takim razie zwracam honor, widocznie ja coś muszę robić źle przy wyświetlaniu. Wyświetlam go w Eclipse za pomocą Applet Viewer'a. Nie bardzo wiem o co chodzi z tym "fokusem".

0

I ty się chcesz brać za programowanie? Aż m słabo... Weź z łaski swojej kliknij w środek tego okienka jak juz się pojawi (wtedy złapie fokusa) i wtedy powciskaj 'a'.

0
Shalom napisał(a)

I ty się chcesz brać za programowanie? Aż m słabo... Weź z łaski swojej kliknij w środek tego okienka jak juz się pojawi (wtedy złapie fokusa) i wtedy powciskaj 'a'.

Programować może przecież każdy... co prawda nie każdy powinien ale to już inna kwestia.
Wróćmy jednak do tematu... Skoro już wiem czym jest ten "fokus" to chciałem powiedzieć, że zdaję sobie sprawę z tego, że aby aplet reagował na klawisze musi być moimi słowami mówiąc "kliknięty". Po prostu nie znałem tej terminologii (fokus) do określenia tego :)
Ale dobra, koniec filozofii. Tak więc nie wiem czy ze mną jest coś nie tak, czy z Eclipse, czy w najgorszym wypadku (o zdrozo!) z Twoją aplikacją, ale no naprawdę nie działa ten program poprawnie. Nie reaguje na naciśnięcie klawisza "a". (żeby nie było.. na pewno wciskam klawisz "a"... :) ). Próbowałem go również "przydusić", ale to też nie nie daje. Klikałem także miliard razy na aplet, po czym znowu próbowałem wciskać "a" i kicha... więc już naprawdę nie wiem co jest....

0
  1. Skopiowałeś ten kod czy może próbowałeś przerobić swój na wzór tego? Jeśli przerabiałeś to zapewne przerobiłeś źle.
  2. Jesteś pewien ze uruchamiasz to co chciałeś? Bo eclipse zapamiętuje co uruchamiałeś i kliknięcie na pałę "run" nie koniecznie uruchomi ci to myślisz ze uruchomi.
  3. Nie będę ci nagrywał filmiku prezentującego że działa bo nie mam na to czasu ;] Może znajdzie się ktoś kto uruchomi u siebie i rozsądzi ;]
0
  1. Nic nie ingerowałem w kod. Było tylko kopiuj/wklej.
  2. Tak. Jestem pewien że uruchamiam to co chciałem. Dla pewności powyłączałem inne klasy i okienka, zrestartowałem Eclipse. W "run" mam możliwość uruchomienia tylko i wyłącznie jednego programu - właśnie tego.
  3. Faktycznie dobrze by było żeby ktoś spróbował to odpalić u siebie. Absolutnie nie twierdzę, że Twój kod jest zły czy coś. W 90% jestem przeświadczony, że to u mnie jest coś nie tak, skoro u Ciebie działa poprawnie. Tylko próbuję dojść do tego co robię nie tak.
0

http://student.agh.edu.pl/~pstanisl/app.html
jak się uruchomi to kliknij sobie w okolice tego czarnego kwadrata i wciskanie 'a' powinno go przesuwać (u mnie przesuwa pod każdą przeglądarką ;])

0

U mnie nie działa (FF 10.0.1, Opera 11.61), ani ten umieszczony w agh, ani lokalny z mojego komputera.
@Shalom, w aplecie setVisible(true) nie jest konieczne, moim zdaniem rozmiar apletu należy ustalać w znaczniku applet, a nie w kodzie apletu.

0

A u mnie natomiast działa ten na stronie AGH (firefox 10.0.1). Natomiast na applet viewerze dalej ni huhu :( Dziwne bardzo... Dlaczego gdzie indziej działa, a gdzieś indziej nie? ...
W każdym razie dzięki, że Ci się chciało to wrzucić na stronkę.

1

Przeprowadziłem badania, u mnie nie działało, bo w żaden sposób aplet (tzn. jego panel) nie uzyskiwał fokusu. U mnie działa takie coś:

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
 
public class Program extends JApplet{
    private static final long serialVersionUID = 1L;
    protected int x = 0;
    protected int y = 0;
 
    @Override
    public void init() {
        Panelik panelik=new Panelik(this);
        add(panelik);
        panelik.addKeyListener(new Czytacz(this));
        panelik.requestFocus();
    }
 
    public void incXY() {
        x++;
        y++;
        repaint();
    }
}

class Panelik extends JPanel
{
    private Program parent;
    public Panelik(Program p)
    {
        parent=p;
    }
    public void paint(Graphics g) {
        g.clearRect(0, 0, getSize().width, getSize().height);
        g.fillRect(parent.x, parent.y, 20, 20);
    }    
}
 
class Czytacz implements KeyListener {
 
    private final Program program;
 
    public Czytacz(Program p) {
        program = p;
    }
 
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyChar() == 'a') {
            program.incXY();
        }
    }
 
    @Override
    public void keyReleased(KeyEvent e) {
    }
 
    @Override
    public void keyTyped(KeyEvent e) {
    }
 
}
0

No u nie ten kod też działa. Nareszcie..
Czyli można wnioskować, że aby Aplet uzyskiwał ten fokus, należy dodać w nim JPanel i wywołać z jej klasy metodę requestFocus(). Tak? Wcześniejszy brak reakcji na wciśnięty, był spowodowany uzyskania fokusa na Aplecie, tak?
Tak czy siak dzięki bogdans, że Ci się chciało pobawić z tym kodem.

0

Jak to mawiają "co dwie głowy to nie jedna". A ja myślałem że programowanie jest deterministyczne, a tu taki psikus ;] Przepraszam kolegę @klapaucius za wcześniejsze docinki na temat fokusa, jak widać nie było to tak trywialne jak myślałem ;)

0

Daj spokój, nie masz za co przepraszać. Poza tym na Twojej stronce ten aplet działał bez zarzutu. Tylko widać ten applet viewer i niektóre inne przeglądarki internetowe są jakoś inaczej zbudowane, że muszą mieć taki kod, jak napisał bogdans. Nie ukrywam, że trochę mnie to zniechęciło do robienia apletów, z racji tego, iż teraz będę musiał pościągać wszystkie rodzaje przeglądarek jakie znam, applet viewery itp, i w każdym z osobna sprawdzać czy aplikacja działa...:D Trochę monotonne....

0

Nie wystarczy sprawdzić na różnych przeglądarkach, trzeba jeszcze na wszystkich komputerach ;). U Ciebie działało zdalnie, nie działało lokalnie, u @Shaloma działało tak i tak, u mnie ani tak, ani tak.

1

Działa też kod bez tworzenia nowego panelu:

    public void init() {
        getContentPane().addKeyListener(new Czytacz(this));
        getContentPane().requestFocus(); //to musi być, nie da się myszą przenieść fokusu na contentPane()
    }

A przy okazji, definicja klasy Czytacz jest niedobra, jeśli tworzymy osobną klasę do obsługi zdarzeń, to ona powinna dziedziczyć po odpowiednim adapterze, a nie implementować listenera (mniej pisania). Wyjątkiem jest ActionListener, tam jest tylko jedna metoda.

class Czytacz extends KeyAdapter {
 
    private final Program program;
 
    public Czytacz(Program p) {
        program = p;
    }
 
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyChar() == 'a') {
            program.incXY();
        }
    }
}
0

A przy okazji, definicja klasy Czytacz jest niedobra, jeśli tworzymy osobną klasę do obsługi zdarzeń, to ona powinna dziedziczyć po odpowiednim adapterze, a nie implementować listenera

Nie do końca się zgodze z tym że to jakaś reguła. To trochę taka akademicka dyskusja z serii "Dziedziczyć po Thread czy implementować Runnable". Oba podejścia są ok w zależności od sytuacji. Tutaj w przypadku implementowania tylko jednej metody z interfejsu może faktycznie dziedziczyć, ale nie robiłbym z tego jakiejś zasady ;)

0

Może i nie reguła, ci co wiedzą, że są adaptery i listenery wybiorą co trzeba. Początkującym proponowałbym jednak adapter, a nuż nie korzystają z sensownego IDE, albo nie potrafią wykorzystać jego możliwości i będą pisać puste metody.

0

Pozwolę sobie jeszcze raz odświeżyć ten temat, ale innego wyjścia nie mam. Bo znów napisałem niemalże taki sam program jak przedtem, ale coś znów nie działa.. Tzn. nie reaguje znów na klawisze. Dlaczego w tej javie ciągle cos z niewiadomych przyczyn nie dziala...?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JApplet;
import javax.swing.JPanel;


public class Snake extends JApplet {
	private Gra gra;
	public Snake(){
		KeyListener kl = new KeyListener(){
			public void keyPressed(KeyEvent arg) {
				switch(arg.getKeyChar()){
					case 'w':
						gra.kierunek = 'w';
						break;
					case 's':
						gra.kierunek = 's';
						break;
					case 'a':
						gra.kierunek = 'a';
						break;
					case 'd':
						gra.kierunek = 'd';
						break;
					default:
						break;
				}
			}
			public void keyReleased(KeyEvent arg) {
			}
			public void keyTyped(KeyEvent arg) {
			}
		};
		this.setLayout(new FlowLayout());
		gra = new Gra(this);
		getContentPane().add(gra);
		getContentPane().addKeyListener(kl);
		getContentPane().requestFocus();
	}
}
class Gra extends JPanel implements Runnable {
	private Snake snake;
	private Thread thread;
	private int x, y;
	public char kierunek;
	public Gra(Snake snake){
		this.snake = snake;
		this.setPreferredSize(new Dimension(202, 202));
		thread = new Thread(this);
		thread.start();
		x = 1;
		y = 1;
		kierunek = 'd';
	}
	public void run() {
		while(true){
			repaint();
			try {
				thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			switch(kierunek){
			case 'w':
				y -= 10;
				break;
			case 's':
				y += 10;
				break;
			case 'a':
				x -= 10;
				break;
			case 'd':
				x += 10;
				break;
			default:
				break;
			}
		}
	}
	public void paint(Graphics g){
		g.clearRect(0, 0, getSize().width, getSize().height);
		g.drawRect(0, 0, getSize().width-1, getSize().height-1);
		g.setColor(Color.red);
		g.fillRect(x, y, 10, 10);
	}
}
0

Najkrótsza odpowiedź: bo nie doczytałeś dokumentacji i nie wiesz do czego służy metoda init()
Dłuższa odpowiedź: widzę ze grochem o ścianę jeśli chodzi o jakieś sugestie co do stylu pisania...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JApplet;
import javax.swing.JPanel;

public class Snake extends JApplet {
    private static final long serialVersionUID = 1L;
    private GameThread gra;

    @Override
    public void init() {
        this.setLayout(new FlowLayout());
        GameBoard board = new GameBoard();
        gra = new GameThread(board);
        getContentPane().add(board);
        board.addKeyListener(new SnakeKeyListener(gra));
        board.requestFocus();
        gra.start();
    }
}

enum Direction {
    W, S, A, D;
}

class SnakeKeyListener extends KeyAdapter {

    private final GameThread gameThread;

    public SnakeKeyListener(GameThread gt) {
        gameThread = gt;
    }

    @Override
    public void keyPressed(KeyEvent arg) {
        switch (arg.getKeyChar()) {
        case 'w':
            gameThread.setNewDirection(Direction.W);
            break;
        case 's':
            gameThread.setNewDirection(Direction.S);
            break;
        case 'a':
            gameThread.setNewDirection(Direction.A);
            break;
        case 'd':
            gameThread.setNewDirection(Direction.D);
            break;
        default:
            break;
        }
    }
}

class GameBoard extends JPanel {
    private static final long serialVersionUID = 1L;
    private int x = 1;
    private int y = 1;

    public GameBoard() {
        this.setPreferredSize(new Dimension(202, 202));
        x = 1;
        y = 1;
    }

    @Override
    public void paint(Graphics g) {
        g.clearRect(0, 0, getSize().width, getSize().height);
        g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);
        g.setColor(Color.red);
        g.fillRect(x, y, 10, 10);
    }

    public void verticalStepBack() {
        y -= 10;
    }

    public void verticalStepForward() {
        y += 10;
    }

    public void horizontalStepBack() {
        x -= 10;
    }

    public void horizontalStepForward() {
        x += 10;
    }
}

class GameThread extends Thread {
    private static final long serialVersionUID = 1L;

    private Direction direction = Direction.D;
    private final GameBoard gameBoard;

    public GameThread(GameBoard board) {
        gameBoard = board;
    }

    public void setNewDirection(Direction newDirection) {
        direction = newDirection;
    }

    @Override
    public void run() {
        while (true) {
            gameBoard.repaint();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (direction == Direction.W) {
                gameBoard.verticalStepBack();
            } else if (direction == Direction.S) {
                gameBoard.verticalStepForward();
            } else if (direction == Direction.A) {
                gameBoard.horizontalStepBack();
            } else if (direction == Direction.D) {
                gameBoard.horizontalStepForward();
            }
        }
    }
}

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