Implementing freehand draw in a MVC Java Swing project - java

I'm currently working on a project that involves using the Model-View-Controller architecture, in the project I have to implement freehand drawing inside of a JPanel, however the panel.paintComponent(g) method doesn't seem to work.
In my view package I create the GUI and give each object a getter, my controller class creates a GUI and uses these getters to instantiate the JPanels, JButtons etc. The Drawing model is passed the JPanel I want to draw on, as seen below.
public class Editor extends JPanel implements MouseListener, MouseMotionListener {
private int index = 0;
private Point[] arr = new Point[100000];
private JPanel xPanel = new JPanel();
Updater upDate = new Updater();
public void getPanel(JPanel dPanel)
{
xPanel = dPanel;
xPanel.addMouseListener(this);
xPanel.addMouseMotionListener(this);
xPanel.setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
System.out.println("Got to this point");
xPanel.paintComponents(g);
for(int i = 0; i < index - 1; i++)
{
System.out.println("And here 2");
g.drawLine(arr[i].x, arr[i].y, arr[i+1].x, arr[i+1].y);
System.out.println("And here 3");
}
}
#Override
public void mousePressed(java.awt.event.MouseEvent e) {
arr[index] = new Point(e.getX(), e.getY());
index++;
System.out.println(index);
upDate.update(xPanel);
}
#Override
public void mouseDragged(java.awt.event.MouseEvent e) {
arr[index] = new Point(e.getX(), e.getY());
index++;
System.out.println(index);
upDate.update(xPanel);
}
My Controller class looks like this.
private GUI view;
private JButton buttonLoad;
private JButton buttonZoom;
private JButton buttonDrag;
private JButton buttonSave;
private JButton buttonRotate;
private JButton buttonDraw;
private JMenuBar menuB;
private JPanel dPanel;
private MouseListener e;
Graphics g;
public Controller(GUI gui){
this.view = gui;
menuB = view.getMenu();
buttonLoad = view.getLoad();
buttonZoom = view.getZoom();
buttonRotate = view.getRotate();
buttonSave = view.getSave();
buttonDrag = view.getDrag();
buttonDraw = view.getDraw();
dPanel = view.getModel();
g = dPanel.getGraphics();
FunctionListener x = new FunctionListener();
MenuBarListener y = new MenuBarListener();
buttonLoad.addActionListener(x);
buttonZoom.addActionListener(x);
buttonRotate.addActionListener(x);
buttonSave.addActionListener(x);
buttonDrag.addActionListener(x);
buttonDraw.addActionListener(x);
//menuB.addMenuListener(y);
}
class FunctionListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource().equals(buttonZoom)){
Editor editor = new Editor();
editor.getPanel(dPanel);
editor.paintComponent(g);
}
It's worth noting that dPanel = view.getModel(); is a getter for a JPanel called Model in my GUI class. Everything seems to be working, the index of points is being returned properly yet there is no drawing, after running some simple System.out.println tests, I know that the code isn't entering the for loop in the Editor class.
Thanks for any help!

Related

JAVA cardLayout. how to call a card from different classes

i have a program with a frame that contains a main panel with the cardlayout layout, and i want it to display different cards/panel.
In my case i'm really struggling to call a new card from a button action listener.
I want a new card to appear after i click on a button but none of the codes i put in my action listener displayed the card i wanted.
I know my actionListener work because i did a println inside.
here's my code. i got rid of anything that was unnecessary so it's easier to read. Thanks for the help!
i'll take all advices about code structuration
the mainFrame :
public class MainFrame extends JFrame{
final static String CONNEXION_VIEW = "connexionView";
final static String CONNEXION_FAILED_VIEW = "connexionRefusee";
public MainFrame()
{
super();
initialize();
}
private void initialize()
{
getMainPanel();
add(getMainPanel());
}
CardLayout cardLayout;
public CardLayout getCardLayout()
{
if (cardLayout == null)
{
cardLayout = new CardLayout();
}
return cardLayout;
}
JPanel mainPanel;
public JPanel getMainPanel()
{
if (mainPanel == null)
{
mainPanel = new JPanel();
mainPanel.setLayout(getCardLayout());
mainPanel.add(CONNEXION_VIEW, getConnexionView());
mainPanel.add(CONNEXION_FAILED_VIEW, getConnexionFailedView());
}
return mainPanel;
}
ConnexionView connexionView;
protected ConnexionView getConnexionView()
{
if (connexionView == null)
{
connexionView = new ConnexionView();
}
return connexionView;
}
ConnexionFailedView connexionFailedView;
protected ConnexionFailedView getConnexionFailedView()
{
if (connexionFailedView == null)
{
connexionFailedView = new ConnexionFailedView();
}
return connexionFailedView;
}
the connexion view, the one with the button to click with the action listener where i want to put my code
public class ConnexionView extends JPanel{
GridBagLayout gbl = new GridBagLayout();
private JButton btnConnexion;
Dimension dimensionBouton = new Dimension(170, 30);
public ConnexionView()
{
super();
initialise();
}
private void initialise()
{
setLayout(gbl);
GridBagConstraints gbcbtnConnexion = new GridBagConstraints();
gbcbtnConnexion.gridwidth = GridBagConstraints.REMAINDER;
gbcbtnConnexion.gridheight = GridBagConstraints.REMAINDER;
gbcbtnConnexion.gridx = 1;
gbcbtnConnexion.gridy = 2;
add(getBtnConnexion(), gbcbtnConnexion);
}
private JButton getBtnConnexion()
{
if (btnConnexion == null)
{
btnConnexion = new JButton("Connexion");
btnConnexion.setPreferredSize(dimensionBouton);
btnConnexion.setMinimumSize(dimensionBouton);
btnConnexion.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
/////code to display the connexion_Failed_View
System.out.println("test");
}
});
}
return btnConnexion;
}
}
and the connexion failed view, the one i want to display after the button is clicked
public class ConnexionFailedView extends JPanel{
public ConnexionFailedView()
{
super();
initialise();
}
private void initialise()
{
setBackground(Color.YELLOW);
}
thanks in advance
You will need to keep the component in your Button somehow, so you can access it.
class ConnexionView {
private JComponent mainPanel;
public ConnexionView(JComponent mp) { mainPanel = mp; }
}
Obviously, that means the MainFrame needs to pass it then.
Now, the listener can do
// it would be cleaner if you passed the layout in the constructor as well
CardLayout cl = (CardLayout) mainPanel.getLayoutManager();
cl.show(mainPanel, MainFrame.CONNEXION_FAILED_VIEW);

How do I update a JLabel in a JPanel?

I am having trouble getting the image on my JLabel to update in the JPanel. I do not see what the problem is. I have tried removing the existing JLabel and adding a new one to the JPanel but that doesn't work. Here is the code I am trying to get the animaiton to work on. My key listener works fine so I am sure there is some problem in this class:
import javax.swing.*;
import java.awt.event.*;
public class Character extends JPanel{
//Constructs array that holds running frames
static ImageIcon running[] = new ImageIcon[19];
static int i = 0;
//Contstructs label that holds the current image frame
static JLabel characterLabel = new JLabel();
//Creates imageicon to be returned to the character and stored in characterLabel
static ImageIcon character = new ImageIcon();
Timer runningTimer = new Timer(300, new RunningListener());
public Character() {
running[0] = new ImageIcon("running 1.png");
running[1] = new ImageIcon("running 2.png");
running[2] = new ImageIcon("running 3.png");
running[3] = new ImageIcon("running 4.png");
running[4] = new ImageIcon("running 5.png");
running[5] = new ImageIcon("running 6.png");
running[6] = new ImageIcon("running 7.png");
running[7] = new ImageIcon("running 8.png");
running[8] = new ImageIcon("running 9.png");
running[9] = new ImageIcon("running 10.png");
running[10] = new ImageIcon("running 11.png");
running[11] = new ImageIcon("running 12.png");
running[12] = new ImageIcon("running 13.png");
running[13] = new ImageIcon("running 14.png");
running[14] = new ImageIcon("running 15.png");
running[15] = new ImageIcon("running 16.png");
running[16] = new ImageIcon("running 17.png");
running[17] = new ImageIcon("running 18.png");
running[18] = new ImageIcon("running 19.png");
characterLabel.setIcon(running[0]);
this.add(characterLabel);
}
private void refreshCharacter(){
this.remove(characterLabel);
characterLabel.setIcon(running[i]);
this.add(characterLabel);
i++;
if (i > 18){
i = 0;
}
}
private class RunningListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
refreshCharacter();
}
}
}
I suppose that this JPanel may be the problem aswell but I doubt it:
import javax.swing.*;
//Panel that manages the game
public class GamePanel extends JPanel{
//Adds stuff to GamePanel to send to Frame
public GamePanel(){
this.addKeyListener(new KeyInput());
this.add(new Character());
}
}
Here is the key listener:
import java.awt.event.*;
//Listens for key actions
public class KeyInput implements KeyListener{
//Constructs character
Character c = new Character();
public void keyPressed(KeyEvent evt) {
int key = evt.getKeyCode();
//When the right key is pressed, start the timer that starts the running animation
if(key == KeyEvent.VK_RIGHT){
c.runningTimer.start();
}
}
public void keyReleased(KeyEvent evt){
int key = evt.getKeyCode();
//When the right key is released, the timer that starts the running animation is stopped
if(key == KeyEvent.VK_RIGHT){
c.runningTimer.stop();
}
}
//There is no use for this... it just has to be there
public void keyTyped(KeyEvent evt){
}
}
and here is the frame:
import javax.swing.*;
public class Frame{
//Creates window and calls to GamePanel
public static void main (String[]args){
GamePanel gamePanel = new GamePanel();
JFrame window = new JFrame("Game");
window.add(gamePanel);
window.setLocation(50,50);
window.setSize(1000,650);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
gamePanel.requestFocus();
}
}
If you're trying to change a displayed image, if you can avoid it, don't remove JLabels or components, but rather swap a JLabel's Icon. If you're not trying to change an image, but do something different, then please give us more detail on your desired behavior, since all you tell us is that you're trying "animation", and that's somewhat broad.
e.g.,
private void refreshCharacter() {
i++;
i %= running.length;
characterLabel.setIcon(running[i]);
}

Leap Motion problems with Swing?

I'm starting to toy a little with the Leap Motion Controller and made a small GUI in Swing. That means it's just a Frame with a Label on it which should show a text of what the Leap Motion sees. Unfortunately my programm simply breaks down after two seconds. I have no Exceptions or something like that. Here's my code:
public class GUI extends JFrame{
private static final long serialVersionUID = 6411499808530678723L;
public JLabel label;
public GUI(){
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(300, 200);
this.label = new JLabel("Waiting for Gestures");
this.add(label);
setVisible(true);
}
public class LeapListener extends Listener{
JLabel label;
LeapListener(JLabel label){
this.label = label;
}
#Override
public void onInit(Controller controller){
}
#Override
public void onExit(Controller controller){
}
#Override
public void onConnect(Controller controller){
System.out.println("bin da");
controller.enableGesture(Gesture.Type.TYPE_CIRCLE);
controller.enableGesture(Gesture.Type.TYPE_KEY_TAP);
controller.enableGesture(Gesture.Type.TYPE_SCREEN_TAP);
controller.enableGesture(Gesture.Type.TYPE_SWIPE);
}
#Override
public void onDisconnect(Controller controller){
}
#Override
public void onFrame(Controller controller){
Frame frame = controller.frame();
GestureList glist = frame.gestures();
for (int i = 0; i < glist.count(); i++){
Gesture g = glist.get(i);
switch (g.type()){
case TYPE_CIRCLE:
CircleGesture circle = new CircleGesture(g);
this.label.setText("Mach mal nen Kreis!");
case TYPE_KEY_TAP:
KeyTapGesture key = new KeyTapGesture(g);
this.label.setText("Schreiben?");
case TYPE_SCREEN_TAP:
ScreenTapGesture screen = new ScreenTapGesture(g);
this.label.setText("Klicken?");
case TYPE_SWIPE:
SwipeGesture swipe = new SwipeGesture(g);
this.label.setText("Da wurde gewischt!");
default:
System.out.println("nothing!");
break;
}
}
}
}
public static void main(String[] args) {
GUI gui = new GUI();
LeapListener listener;
listener = gui.new LeapListener(gui.label);
Controller controller = new Controller();
controller.addListener(listener);
}
}
I have no idea what I've done wrong. The Windows-Error-Message says that the Java Platform SE binary is down (javaw.exe). Errormodule: Leap.dll
Is it a mistake that I've made or is my whole setup f#*ked up?
Ok, I found a solution by myself but unfortunately I can't explain it.
I have to create the Controller Object in the class declaration, not in the main-method. Then I have to add the listener to the controller in the constructor of my GUI, like that:
public class GUI extends JFrame{
private static final long serialVersionUID = 6411499808530678723L;
public JLabel label;
public Controller c = new Controller();
public LeapListener listener;
public GUI(){
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(300, 200);
this.label = new JLabel("Waiting for Gestures");
this.add(label);
listener = new LeapListener(this.label);
c.addListener(listener);
setVisible(true);
}
...
}
So, I'm happy that I got it to work but maybe somebody is interested in the problem and may find a reason for the error I've had.

Java Key Binding in a JApplet: What am I doing wrong?

I'm trying to write a game applet that responds to keys typed by the player. I'm trying to use key bindings to accomplish this. But I can't get it to work. The applet (what little of it there is, at the moment) seems to display correctly in Appletviewer, but nothing happens when I press keys. I haven't been able to test it in a browser, as it doesn't always display correctly in a browser.
I'm running Sun Java 6 on Ubuntu. I managed to find mention of a Ubuntu bug where iBus would block keyboard input to applets. However, I don't have iBus running, and I've been able to use keyboard input with other applets (not written by me).
Here is the code so far
public class AlxTestVersion extends JApplet {
private GridBagLayout layout;
private GridBagConstraints layoutConstraints;
private JPanel mainPanel;
public void init() {
this.setLayout ( new FlowLayout(FlowLayout.LEFT) );
//Main frame.
mainPanel = new JPanel();
layout = new GridBagLayout();
layoutConstraints = new GridBagConstraints();
layoutConstraints.anchor = GridBagConstraints.NORTHWEST;
layoutConstraints.fill = GridBagConstraints.NONE;
mainPanel.setLayout(layout);
mainPanel.setBackground(Color.pink);
getContentPane().add(mainPanel);
//Map display
JPanel leftPanel = new JPanel();
GlobalData.mainMap = new MapCanvas(9);
addComponent(GlobalData.mainMap, 0, 0, 1, 1);
/*
Define other components...
*/
}
public class MapCanvas extends JPanel {
private int tileSize;
private int mapTileWidth;
private int mapOffset;
private int mapDim;
private MapSquare screenTiles[];
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon testImage = new ImageIcon("tiles/test_land.gif");
int x,y;
for (x=0;x<mapTileWidth;x++) {
for (y=0;y<mapTileWidth;y++) {
g.drawImage(testImage.getImage(), x*tileSize + mapOffset, y*tileSize + mapOffset, this);
}
}
}
public MapCanvas(int numTiles) {
//Set up window
tileSize = 48;
mapTileWidth = numTiles;
mapOffset = 4;
mapDim = (tileSize * mapTileWidth) + (2 * mapOffset);
Dimension dim = new Dimension(mapDim, mapDim);
this.setPreferredSize(dim);
this.setMinimumSize(dim);
this.setMaximumSize(dim);
this.setLayout( new GridLayout(numTiles, numTiles, 0, 0) );
this.setBackground(Color.black);
screenTiles = new MapSquare[numTiles^2];
//Map-related actions
InputMap im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = this.getActionMap();
AbstractAction north = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Just for testing", "testing",
JOptionPane.PLAIN_MESSAGE);
}
};
am.put("North", north);
im.put(KeyStroke.getKeyStroke('2'), "North");
im.put(KeyStroke.getKeyStroke('i'), "North");
}
}
About the only difference I can find between what I've used and working examples found in various places is they add the keystroke to the inputmap before mapping the keystroke to the action. I tried switching the order, but it didn't seems to have any effect.
Can anyone see what I'm doing wrong here? I just know I'm missing something obvious.
Your code doesn't work for me either (on a mac), until I click inside the window. Adding the following as the last thing in init() seems to help (but is not totally reliable):
GlobalData.mainMap.requestFocus();
Your applet window probably does not have the focus when you press the keys.
Try adding this to your init():
GlobalData.mainMap.addFocusListener(new FocusDebugger("canvas"));
this.addFocusListener(new FocusDebugger("applet"));
Here's FocusDebugger:
public static class FocusDebugger implements FocusListener {
private final String name;
public FocusDebugger(String name) {
this.name = name;
}
public void focusGained(FocusEvent focusEvent) {
System.out.println(name + ".focusGained");
}
public void focusLost(FocusEvent focusEvent) {
System.out.println(name+".focusLost");
}
}

Javax.swing timer repeats fine, but ActionListener doesn't do anything

I am trying to flash the background colour in a textfield. My timer setup is as follows:
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
tmr.setInitialDelay(0);
tmr.setRepeats(true);
tmr.start();
My actionListener is as follows:
static class Flash implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.white);
}
else
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
Now, when I put this in debug and follow the action, the program does repeatedly step through flash and toggle between the two alternatives. But onscreen, only the first toggle
occurs. After that, no action, although flash is still functioning.
What is wrong here?
Thanks in advance for any help.
This example continually varies the saturation of a panel's background color:
import java.awt.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class FlashTest extends JPanel {
private static final Font font = new Font("Serif", Font.PLAIN, 32);
private static final String s = "Godzilla alert!";
FlashTest() {
this.setPreferredSize(new Dimension(256, 96));
this.setBackground(Color.red);
Timer t = new Timer(50, new Flash(this));
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
int xx = this.getWidth();
int yy = this.getHeight();
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getDescent();
g.setColor(Color.black);
g.drawString(s, xx / 2 - w2, yy / 2 + h2);
}
private static class Flash implements ActionListener {
private final float N = 32;
private final JComponent component;
private final Queue<Color> clut = new LinkedList<Color>();
public Flash(JComponent component) {
this.component = component;
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, 1 - (i / N), 1));
}
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, i / N, 1));
}
}
#Override
public void actionPerformed(ActionEvent e) {
component.setBackground(clut.peek());
clut.add(clut.remove());
}
}
static public void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new FlashTest());
f.pack();
f.setVisible(true);
}
});
}
}
There are a couple of problems here.
The first obvious thing is that you appear to be using mutable statics. This is a really bad idea and indicates (and causes!) confusion. In this particular case, one of the problems caused is that the flasher static is shared.
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
We are adding two Flash actions. Ordinarily this would be bad, but just produce an undetectable "bug". The colour would be set twice.
Bring these two things together, and we have two actions without a break that perform the same toggle. Two toggles. The state does not change (although there are repaint, property change events, etc.).
So, don't use mutable statics, and keep the code clean.
tmr = new javax.swing.Timer(1000, flash);
I tried your code and it works fine.
Why do you use a static context for SpreademPanel.historyPnl.NameTxt?
EDIT
You might want to redesign your class to pass the component in the constructor.
private class Flash implements ActionListener
{
private boolean flasher = false;
private JComponent component;
public Flash(JComponent component) {
this.component = component;
}
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
component.setBackground(Color.white);
}
else
{
component.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
and then init it with
Flash flash = new Flash(SpreademPanel.historyPnl.NameTxt);
Timer tmr = new javax.swing.Timer(1000, flash);
tmr.start();

Categories