Java Swing Scrolling By Dragging the Mouse - java

I am trying to create a hand scroller that will scroll as you drag your mouse across a JPanel. So far I cannot get the view to change. Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HandScroller extends JFrame {
public static void main(String[] args) {
new HandScroller();
}
public HandScroller() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
final JPanel background = new JPanel();
background.add(new JLabel("Hand"));
background.add(new JLabel("Scroller"));
background.add(new JLabel("Test"));
background.add(new JLabel("Click"));
background.add(new JLabel("To"));
background.add(new JLabel("Scroll"));
final JScrollPane scrollPane = new JScrollPane(background);
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JViewport viewPort = scrollPane.getViewport();
Point vpp = viewPort.getViewPosition();
vpp.translate(10, 10);
background.scrollRectToVisible(new Rectangle(vpp, viewPort.getSize()));
}
};
scrollPane.getViewport().addMouseListener(mouseAdapter);
scrollPane.getViewport().addMouseMotionListener(mouseAdapter);
setContentPane(scrollPane);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
I would think that this would move the view by 10 in the x and y directions, but it is not doing anything at all. Is there something more that I should be doing?
Thanks.

I think so you can move the view in all directions with this code
public void mouseDragged(MouseEvent e) {
JViewport viewPort = scroll.getViewport();
Point vpp = viewPort.getViewPosition();
vpp.translate(mouseStartX-e.getX(), mouseStartY-e.getY());
scrollRectToVisible(new Rectangle(vpp, viewPort.getSize()));
}
public void mousePressed(MouseEvent e) {
mouseStartX = e.getX();
mouseStartY = e.getY();
}

Your code does work. Simply, there is nothing to scroll, as the window is large enough (actually, pack() has caused the JFrame to resize to fit the preferred size and layouts of its subcomponents)
Remove pack(); and replace that line with, say, setSize(60,100); to see the effect.

It is working. However, when you run this code, the JScrollPane is made large enough to fit all your items. Add (for instance)
scrollPane.setPreferredSize(new Dimension(50, 50));
and you'll see that your mouse events work fine.

Related

Why doesn't the image paint over my JPanel?

I have been struggling with this for some time. At first, I only used ActionListener, then I added the paintComponent, but I have no idea what to put there. I read some tutorials and used their code as an example, but it still doesn't work. Right now, the end result is the same as it was without PaintComponent.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Scream extends JPanel {
private JButton button = new JButton("OK");
private Color screenColor;
private JPanel panel = new JPanel();
private JFrame frame;
private Dimension screenSize;
private ImageIcon image;
private JLabel label = new JLabel(image);
private int x;
private int y;
private boolean mouseClicked;
public Scream() {
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e ) {
if (e.getSource() == button) {
mouseClicked = true;
frame.getContentPane().add(label);
frame.setSize(image.getIconWidth(), image.getIconHeight());
panel.repaint();
}
}
});
frame = new JFrame ("Existential angst");
screenColor = new Color(150, 100, 0);
panel.setBackground( screenColor );
frame.add(button, BorderLayout.PAGE_END);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1300, 700);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
image.paintComponent(this, g, 1300, 700);
}
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Scream scream = new Scream();
}
});
}
}
If you are trying to dynamically add an image to a panel then you need to add the label to the panel. There is no need for any custom painting.
The basic code for adding components to a visible GUI is:
panel.add(...);
panel.revalidate();
panel.repaint();
Also, don't attempt to set the size of the frame to the size of the image. A frame contains a titlebar and borders. Instead you can use frame.pack();
I noticed a couple of issues:
image is never initialized to anything so it is null, effectively making the label empty. I assume maybe your example was just incomplete?
Once I initialized the image to something, your example still did not work. Turns out adding label without specifying any constraint basically does nothing (I assume since adding a component to a border layout without a constraint puts it in the center where panel already is). When I added the label to BorderLayout.NORTH, everything worked (though resizing the frame to the size of the image makes it only partially visible since the frame includes the OK button)

how to put my background image at the bottom

I want to put my background image at the very bottom in this frame, and the button on top. However the code I wrote below doesn't work. Can anyone see where the problems are?
Another thing is that even though I set the location for my button, it keep showing at the top center on the frame.
Please ignore the comment lines. (I was just guessing, and hoping them will work, but they don't apparently.)
public class Menu extends JFrame{
private JLayeredPane pane;
private JLayeredPane pane2;
public Menu(){
final JFrame f = new JFrame("Chinese Chess");
JButton play = new JButton("Play vs. AI");
f.setLayout(new FlowLayout());
f.setLocationRelativeTo(null);
f.setSize(800, 800);
f.setVisible(true);
f.setResizable(false);
//f.pack();
pane = new JLayeredPane();
pane2 = new JLayeredPane();
f.add(pane);
f.add(pane2);
//background image
JLabel background = new JLabel(new ImageIcon("res/img/background.png"));
background.setLocation(0, 0);
background.setSize(800, 800);
pane.add(background, JLayeredPane.FRAME_CONTENT_LAYER);
pane2.add(play, JLayeredPane.DEFAULT_LAYER);
//pane.moveToBack();
//button PlayAI
play.setLocation(500,500);
play.setPreferredSize(new Dimension(100,50));
//f.setLayout(new FlowLayout());
//frame menu
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.getContentPane().add(play);
play.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
new PlayAI();
}
});
}
public static void main(String[] args){
new Menu();
}
Problems/Solutions:
setLocation(...) and setBounds(...) types of calls are ignored by most layout managers. The only way to use them is to set the layout of the container to null via .setLayout(null);
But having said that, while null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
So in sum -- don't do this, don't use null layouts or setBounds, but rather nest JPanels, each using its own layout manager, and thereby create easy to maintain and decent GUI's.
If you want an image to be in the background, then draw it in a JPanel that you use as a container for your GUI components by drawing it in the JPanel's paintComponent(Graphics g) method as has been demonstrated in many many similar questions on this site -- I'll find you some of mine in a second.
If you add any JPanels on top of this image drawing JPanel, be sure that you can see through them by calling setOpaque(false) on these overlying JPanels. Otherwise you'll cover up the image.
Your code has two JFrames when only one is needed. Get rid of the one you don't use.
You call setVisible(true) too early on the JFrame, before components have been added to the GUI -- don't. Call it only after adding everything to the GUI so all display OK.
You're creating two JLayedPanes, and completely covering one by the other by adding them to the JFrame without understanding how the JFrame's BorderLayout handles added components.
I suggest that you not even use one JLayeredPane but instead draw in the JPanel as noted above, and use that as your container.
Your code looks to be opening a completely new GUI window when the play button is pressed, and if so, this can get annoying to the user fast. Consider swapping views instead with a CardLayout.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
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.*;
// extend JPanel so you can draw to its background
#SuppressWarnings("serial")
public class Menu2 extends JPanel {
private BufferedImage bgImage = null; // our background image
private JButton playButton = new JButton(new PlayVsAiAction("Play Vs. AI", KeyEvent.VK_P));
public Menu2(BufferedImage bgImage) {
this.bgImage = bgImage;
setLayout(new GridBagLayout()); // center our button
add(playButton);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bgImage != null) {
g.drawImage(bgImage, 0, 0, this);
}
}
// to size our GUI to match a constant or the image.
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// if you want to size it based on the image
if (bgImage != null) {
int width = bgImage.getWidth();
int height = bgImage.getHeight();
return new Dimension(width, height);
} else {
return super.getPreferredSize();
}
// if you want to size the GUI with constants:
// return new Dimension(PREF_W, PREF_H);
}
private class PlayVsAiAction extends AbstractAction {
public PlayVsAiAction(String name, int mnemonic) {
super(name); // have our button display this name
putValue(MNEMONIC_KEY, mnemonic); // alt-key to press button
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO code to start program
}
}
private static void createAndShowGui() {
BufferedImage img = null;
String imagePath = "res/img/background.png";
try {
// TODO: fix this -- use class resources to get image, not File
img = ImageIO.read(new File(imagePath));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
Menu2 mainPanel = new Menu2(img);
JFrame frame = new JFrame("Chinese Chess");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Apart from the solution above... you should create and launch your swing application this way:
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
// Instantiate your JFrame and show it
}

Draw Squares of colors inside a JFrame GUI

how by using a GUI create 9 different squares distributed in a grid of 3 rows and 3 columns
on a frame. The user will be able to change the color of any part just by pressing the right click and a menu will be shown to choose the desired color
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
public class Coloring extends JPanel
{
private JRadioButtonMenuItem items[];
private Color colorValue[]={Color.GREEN,Color.BLUE,Color.RED,Color.PINK,Color.GRAY,Color.YELLOW,Color.CYAN,Color.MAGENTA,Color.BLACK};
public Coloring()
{
final JPopupMenu popupMenu=new JPopupMenu();
ItemHandler handler =new ItemHandler();
String colors[]={"Green","Blue","Red","Pink","Gray","Yellow","Cyan","Magenta","Black"};
ButtonGroup colorGroup= new ButtonGroup();
items=new JRadioButtonMenuItem[9];
for(int i=0;i<items.length;i++)
{
items[i]=new JRadioButtonMenuItem(colors[i]);
popupMenu.add(items[i]);
colorGroup.add(items[i]);
items[i].addActionListener(handler);
}
JFrame frame=new JFrame("Colored Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Square());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{ checkForTrigerEvent(e); }
public void mouseReleased(MouseEvent e)
{ checkForTrigerEvent(e); }
public void checkForTrigerEvent(MouseEvent e)
{
if(e.isPopupTrigger())
popupMenu.show(e.getComponent(),e.getX(),e.getY());
}
}
);
}
private class ItemHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int i=0;i<items.length;i++)
if(e.getSource()==items[i])
{
getContentPane().setBackground(colorValue[i] );
repaint();
return;
}
}
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2d=(Graphics2D) g.create();
int width=getWidth();
int height=getHeight();
int cellWidth= width/3;
int cellHeight=height/3;
int xOffset=(width - (3*cellWidth))/2;
int yOffset=(height -(3*cellHeight))/2;
}
public static void main(String args[])
{
new Coloring();
}
}
**Now i have no idea how i can addd the squares that will change the color on right click **
This sounds like you need to break this down into several smaller parts kind of like how Arvind explained in the comments.
Use GridLayout on frame, add 9 JComponents with prefered size to it, now add JPopupMenu to those jcomponents.
I recommend looking at the following
GridLayout http://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
JComponents http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html
Like people said above this website is for help with issues not to do the work for you. Good luck with your adventure.
UPDATE
There are plenty of different ways you can do this but I suggest you change BorderLayout to GridLayout(0,3) this will automatically position the JComponents so that they are evenly positioned in the JFrame. I then suggest you add 9 JComponents or (I prefer to use JPanels) to the JFrame instead of whatever you are doing with adding the Square. you will then want to add Action listeners to each JComponent here is an example of how you can use an action listener to detect the right click.
panel.addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
if(SwingUtilities.isRightMouseButton(e)) //this checks for a right mouse click
popupMenu.show(e.getComponent(),e.getX(),e.getY());
}
public void checkForTrigerEvent(MouseEvent e)
{
}
}
);

Using scrollbars with absolute layout in Swing

I am not able to use scroll bars with absolute layout in Swing.
I don't wish to use this layout but I have to display dynamic objects on my panel on click of a button and align them using setBounds which can be done using this layout only (I guess).
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class clothes2 extends javax.swing.JFrame {
JTextField n=null;
JButton m=null;
public clothes2(){
initComponents();
}
public void initComponents() {
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
final JPanel jp = new JPanel();
contentPane.setPreferredSize(new Dimension(320,200));
jp.setLayout(null);
m=new JButton("add");
m.setBounds(0,0,50,50);
jp.add(m);
m.addMouseListener( new MouseAdapter() {
int x=0;
int y=0;
public void mouseClicked(MouseEvent me){
x+=100;
y+=100;
jp.add(n=new JTextField("Name"));
n.setBounds(x, y, 50, 50);
jp.add(n=new JTextField("code"));
x+=100;
n.setBounds(x,y, 50, 50);
jp.revalidate();
jp.repaint();
x=0;
}
});
int v = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
int h = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
JScrollPane jsp = new JScrollPane(jp, v, h);
contentPane.add(jsp, BorderLayout.CENTER);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f= new clothes2();
f.setVisible(true);
f.setSize(640,320);
}
});
}
}
Set preferred size of the container.
JScrollBar uses the preferred size of the component inside it to determine how large the scroll bars should be, and if they should be displayed.
Usually, the layout manager handles this using the preferredLayoutSize method. This can be overriden by explicitly setting the preferred size of the component.
So either you have to set the preferred size, or use a custom layout manager that calculates it for you.
see also here
might help you.
display dynamic objects .. which can be done using this layout only (I guess).
You guess wrong.
See this GUI, that can not only change PLAFs at run-time, but also dynamically add new components1. Click to..
Add Another Label
This example adds the new labels to a GridLayout - but the principle is the same for any layout (or any component).
add layout
jp.setLayout(new FlowLayout());

Programming a GUI with resizable components using Java.swing

I'm supposed to program a GUI. Every component in this GUI has to have the possibility to be resized dynamically.
So far I worked with GlassPane and ContentPane, added a JPanel and on it a button. When clicking the GlassPane gets the event, analyses the underlying component, creates a new handle for this special component and handles it over to the said component.
I added a button that should change its size automatically. Works like I wanted it to work.
BUT: When I change the size of the frame and click on the button now, nothing happens. The GlassPane is able to identify the button, but something seems to be wrong...
Here's the code for the interception of GlassPane and giving the event to the component:
private void resendEvent(MouseEvent e) {
//Point p = SwingUtilities.convertPoint(content.getGlassPane(), e.getPoint(), content.getContentPane());
Point p = e.getPoint();
Component component = SwingUtilities.getDeepestComponentAt(content.getContentPane(), p.x, p.y);
System.out.println(component.toString());
Point p2 = component.getLocation();
MouseEvent event = new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(), p2.x, p2.y, e.getClickCount(), e.isPopupTrigger());
//following lines have the same effects
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
//component.dispatchEvent(event);
}
Thanks for your help/suggestions
Okay, here's some more Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import resize.ResizablePanel;
public class ExampleProblem extends JFrame {
public ExampleProblem () {
JPanel glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
resendEvent(e);
}
#Override
public void mouseReleased(MouseEvent e) {
resendEvent(e);
}
});
this.setGlassPane(glassPane);
glassPane.setVisible(true);
JButton b = new JButton("Test");
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Button clicked");
}});
JPanel p = new JPanel();
p.add(b);
setContentPane(p);
pack();
setVisible(true);
}
private void resendEvent(MouseEvent e) {//Point p = SwingUtilities.convertPoint(content.getGlassPane(), e.getPoint(), content.getContentPane());
Point p = e.getPoint();
Component component = SwingUtilities.getDeepestComponentAt(this.getContentPane(), p.x, p.y);
System.out.println(component.toString());
Point p2 = component.getLocation();
MouseEvent event = new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(), p2.x, p2.y, e.getClickCount(), e.isPopupTrigger());
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
}
public static void main(String[] args) {
new ExampleProblem();
}
}
Hope, the problem is a bit clearer now.
Is it possible, that I shouldn't have used setContentPane()? But we had to overwrite the JContentPane for resizing the components on it...
You have to determine the component point from your coordinate. Use this instead of p2 to to create the event:
Point p2 = SwingUtilities.convertPoint(this.getGlassPane(), p, component);
You might be able to use the Component Resizer. It would need to be added to all components in advance.
Or maybe you could add the listener when you select the component and then remove the listener when you deselect the component.
The code you posted is of little help when guessing what your actual program is doing. When you have problems with your code you need to post a SSCCE.

Categories