问题
My goal is moving clipping area 10 pixels at a time using arrow keys. I got the image on the panel and the clipping area is there too, but the thing is that the clipping area won't move. Here is my code, and I hope to learn what's wrong with it.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class clipping_area extends JFrame{
clipping_area(){
setTitle("OpenChallenge");
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500,500);
add(new panelOC());
}
class panelOC extends JPanel{
int xAxis=0;
int yAxis=0;
public void paintComponent(Graphics g){
super.paintComponent(g);
Image img=(new ImageIcon("images/image1.jpg")).getImage();
g.setClip(100+10*xAxis,100+10*yAxis,50,50);
g.drawImage(img,0,0,getWidth(),getHeight(),this);
}
panelOC(){
requestFocus();
addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent KE){
if(KE.getKeyCode()==KeyEvent.VK_UP){
yAxis-=1;
repaint();
}
else if(KE.getKeyCode()==KeyEvent.VK_DOWN){
yAxis+=1;
repaint();
}
else if(KE.getKeyCode()==KeyEvent.VK_LEFT){
xAxis-=1;
repaint();
}
else if(KE.getKeyCode()==KeyEvent.VK_RIGHT){
xAxis+=1;
repaint();
}
}
});
}
}
public static void main(String[] args){
new clipping_area();
}
}
回答1:
KeyListener
is a real pain in the, well, focus area. If the component it is attached to is not focusable AND has keyboard focus, it won't trigger events, that's the way it's designed. Instead, use the Key Bindings API which has been designed to overcome this.
See How to Use Key Bindings for more details
Be wary of modifying the clip
of a Graphics
context, a Graphics
context is shared resource, meaning that it will be past to other components. You could also, if you're not careful, size the clip in such away as to paint beyond the range of the component, causing some weird graphics glitches, personally, I stay away from it.
If you use ImageIO.read
instead, you can get a reference to a BufferedImage
and use getSubImage
to "fake" it instead
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ClippingArea extends JFrame {
ClippingArea() {
setTitle("OpenChallenge");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new PanelOC());
pack();
setVisible(true);
}
class PanelOC extends JPanel {
int xAxis = 0;
int yAxis = 0;
private BufferedImage img;
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
int width = 50;
if (xAxis + width > img.getWidth()) {
width = img.getWidth() - xAxis;
}
int height = 50;
if (yAxis + height > img.getHeight()) {
height = img.getHeight() - yAxis;
}
if (width > 0 && height > 0) {
BufferedImage subImage = img.getSubimage(xAxis, yAxis, width, height);
g.drawImage(subImage, xAxis, yAxis, this);
}
}
}
protected void registerKeyBinding(String name, KeyStroke keyStroke, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
public PanelOC() {
try {
img = ImageIO.read(new File("C:\\hold\\thumbnails\\_cg_836___Tilting_Windmills___by_Serena_Clearwater.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
registerKeyBinding("moveClip.up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new YKeyAction(-10));
registerKeyBinding("moveClip.down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new YKeyAction(10));
registerKeyBinding("moveClip.left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new XKeyAction(-10));
registerKeyBinding("moveClip.right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new XKeyAction(10));
}
public class XKeyAction extends AbstractAction {
private int delta;
public XKeyAction(int delta) {
this.delta = delta;
}
@Override
public void actionPerformed(ActionEvent e) {
xAxis += delta;
if (yAxis > getWidth()) {
yAxis = getWidth() - 50;
} else if (yAxis < 0) {
yAxis = 0;
}
repaint();
}
}
public class YKeyAction extends AbstractAction {
private int delta;
public YKeyAction(int delta) {
this.delta = delta;
}
@Override
public void actionPerformed(ActionEvent e) {
yAxis += delta;
if (yAxis > getHeight()) {
yAxis = getHeight() - 50;
} else if (yAxis < 0) {
yAxis = 0;
}
repaint();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
ClippingArea ca = new ClippingArea();
}
});
}
}
Take a look at Reading/Loading an Image for more details
You should also be creating and modiying your UI from within the context of the Event Dispatching Thread, see Initial Threads for more details
回答2:
instead of using a keylistener on a panel use a AWTEventListener (which will grab all events of a particular type
java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
toolkit.addAWTEventListener(new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent ae) {
if (ae instanceof KeyEvent) {
KeyEvent KE = (KeyEvent) ae;
if (KE.getID() == KeyEvent.KEY_PRESSED) {
switch(KE.getKeyCode()) {
case KeyEvent.VK_UP:
yAxis -= 1;
break;
case KeyEvent.VK_DOWN:
yAxis += 1;
break;
case KeyEvent.VK_LEFT:
xAxis -= 1;
break;
case KeyEvent.VK_RIGHT:
xAxis += 1;
break;
}
repaint();
}
}
}
}, AWTEvent.KEY_EVENT_MASK);
And a second edit, I replaced your if statements with a switch ( its significantly cleaner to read and easier to modify later.
来源:https://stackoverflow.com/questions/28206950/java-moving-clipping-area-by-keylistener