Get knob/thumb postion JScrollPane - java

I need to get the exact middle or top point of a knob/thumb in JScrollPane, so the current focused component (the one with the yellow background) will follow the thumb. Is it possible?
ps. Sorry for missing the code example, but the implementation is quite large.

Maybe there is a way to grab the thumb/knob of the scroll pane but I don't know it. Here is a geometric way to find what you're after.
We'll add an adjustment listener, then get the value of the slider. From the value we can calculate the fraction of the scene the slider is moved along. Then we get the height of the scroll bar and we can use the fraction to find the position of the knob.
double relativePositionOnScreen = height*model.getValue()/(model.getMaximum() - model.getMinimum());
The position of the knob on screen will have to be shifted relative to the viewport, so we can just add the value to that.
Here is an example, with a scrollpane and inside of the scrollpane is a panel that has a box on it following the knob.
import javax.swing.*;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.geom.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class ScrollByMe{
public static void main(String[] args){
Rectangle2D rect = new Rectangle2D.Double(0, 0, 0, 0);
JFrame frame = new JFrame("scroll by me");
JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
((Graphics2D)g).draw(rect);
}
};
panel.setPreferredSize( new Dimension(200, 800) );
JScrollPane scroller = new JScrollPane( panel);
JScrollBar bar = scroller.getVerticalScrollBar();
bar.addAdjustmentListener( e ->{
BoundedRangeModel model = bar.getModel();
Dimension d = bar.getSize();
double height = d.getHeight();
double buttonHeight = model.getExtent() * height / (model.getMaximum() - model.getMinimum());
//maximum *value* range.
double relativePositionOnScreen = height*model.getValue()/(model.getMaximum() - model.getMinimum());
rect.setRect( 40, model.getValue() + relativePositionOnScreen, 40, buttonHeight);
panel.repaint();
});
frame.add(scroller);
frame.setSize(200, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Related

Background is not painted

I want to create a simple game menue for the game pong. In the background there is a ball that bounces of the edges just to look nice. Now I want to add Buttons to this menue screen. But then the Background isn't painted anymore. So i want to draw a live Background but the buttons should still stay in the front. This live Background is implemented with a loop. In the following are the GUI class and the DrawStartMenueClass.
I heard about Layers but everybody uses them with some kind of tool in NetBeans and I got eclipse so I don't have that opportunity. I guess you can put the buttons and the background in different containers but then again I am not sure how that would. Every help is welcome.
public class GUI {
public GUI() {
//Frame erzeugen
Var.frame = new JFrame("Pong");
Var.frame.setSize(Var.dimension);
Var.frame.setVisible(true);
Var.frame.setResizable(true);
Var.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Var.frame.setLocationRelativeTo(null);
//Komponenten erzeugen
Var.start = new JButton("Start");
Var.optionen = new JButton("Optionen");
Var.modus = new JButton("Modus");
Var.skins = new JButton("Skins");
Var.beenden = new JButton("Beenden");
//Komponenten anpassen
Var.start.setBounds((Var.screenWidth / 2) - (Var.buttonWidth / 2), 400, Var.buttonWidth, 50);
Var.optionen.setBounds((Var.screenWidth / 2) - (Var.buttonWidth / 2), 500, Var.buttonWidth, 50);
Var.modus.setBounds((Var.screenWidth / 2) - (Var.buttonWidth / 2), 600, Var.buttonWidth, 50);
Var.skins.setBounds((Var.screenWidth / 2) - (Var.buttonWidth / 2), 700, Var.buttonWidth, 50);
Var.beenden.setBounds((Var.screenWidth / 2) - (Var.buttonWidth / 2), 800, Var.buttonWidth, 50);
//Komponenten hinzufügen
Var.frame.getContentPane().add(new DrawStartMenue());
Var.frame.add(Var.start);
Var.frame.add(Var.optionen);
Var.frame.add(Var.modus);
Var.frame.add(Var.skins);
Var.frame.add(Var.beenden);
}
}
package main;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class DrawStartMenue extends JPanel{
private static final long serialVersionUID = 1L;
private GridLayout experimentLayout = new GridLayout(6,1);
public Graphics2D g2d;
public void paint(Graphics g) {
//Objekt g zum Zeichnen erzeugen
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Hintergrund
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, Var.screenWidth, Var.screenHeight);
g2d.setColor(Color.WHITE);
g2d.setStroke(new BasicStroke(8));
g2d.drawRect(Var.whiteRectX1, Var.whiteRectY1 , Var.whiteRectWidth, Var.whiteRectHeight);
//Ball im Hintergrund
for(int i = 0; i < Var.ballList.size(); i++) {
g2d.fillOval(Var.ballList.get(i).ballX, Var.ballList.get(i).ballY,
Var.ballList.get(i).ballWidth, Var.ballList.get(i).ballHeight);
}
//Schriftzug
drawCenteredString(g, "PONG", Var.rect, new Font("NO CONTINUE", Font.PLAIN, 230));
repaint();
}
Var.frame.getContentPane().add(new DrawStartMenue());
Var.frame.add(Var.start);
Var.frame.add(Var.optionen);
Var.frame.add(Var.modus);
Var.frame.add(Var.skins);
Var.frame.add(Var.beenden);
The default layout for a JFrame is the BorderLayout. When you add a component to the frame without specifying a constraint the component is added to the CENTER. Only one component can be added to the CENTER. So only the last one added is visible.
If you want the button on the background then you need to add the buttons to background panel, not the frame:
JPanel background = new DrawStartMenue();
Var.frame.add(background, BorderLayout.CENTER);
background.add(Var.start);
background.add(Var.optionen);
background.add(Var.modus);
background.add(Var.skins);
background .add(Var.beenden);
Other problems with the code:
custom painting is done by overriding paintComopnent(...) not paint(...).
Never invoke repaint() in a painting method. Swing will determine when the component should be repainted.
components should be added to the frame BEFORE the frame is made visible.
don't attempt to setBounds(...) components. Swing was designed to be used with layout managers. Use the appropriate layout manager for your desired layout.
Edit:
So you know how to:
add components to a panel
do custom painting on a panel.
So to combine both concepts you modify the SwingPaintDemo2 code as follow:
//f.add(new MyPanel());
JPanel panel = new MyPanel();
panel.add( new JButton("Testing") );
f.add(panel);
So know that you know the basics, you use the knowledge on your real code.
And don't forget, you should NEVER invoke repaint in a painting method. I already showed you how to use a Swing Timer for the animation.
Next if you don't like the location of the buttons, then you use a different layout manager on the MyPanel class. If you need help with layout manager, then the Swing tutorial also has a section on layout manager with working example for you to download and test.
Here is the MRE. I finally got it to work. However it doesn't work in my project.
package main;
import java.awt.Color;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GUI {
static JFrame frame;
static JPanel buttons;
static JPanel background;
static JButton test1;
static JButton test2;
public static void main(String[] args) {
frame = new JFrame();
frame.setResizable(false);
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Initzialize Components
buttons = new JPanel();
buttons.setBackground(Color.BLACK);
background = new JPanel();
background.setBackground(Color.BLACK);
test1 = new JButton("Test1");
test2 = new JButton("Test2");
//Adding the Buttons
buttons.add(test1);
buttons.add(test2);
//Adding panels to JFrame
frame.add(background);
//frame.add(buttons);
background.add(buttons);
frame.setVisible(true);
}
}

Need to set frame.setResizable(false) to repaint() [duplicate]

This question already has answers here:
JFrame not presenting any Components
(4 answers)
Closed 5 years ago.
I am trying to buff my java skills (been about 10 years since I coded). Currently I am just trying to make a basic program that will have balls bouncing off the edges of the JFrame. However, as a starter in this program I tried drawing a line and box on the JPanel.
The issue I am finding is I have to call frame.setResizable(false) in order or the screen to paint my box and line. It will paint them if I resize the JFrame after it comes up. However, I would like it to paint as soon as the JFrame opens.
Putting in:
frame.setResizable(false);
frame.setResizable(true);
seems redundant. Is there a cleaner way to do this so it paints when the JFrame opens?
Below is my code if this helps:
MAIN CLASS
package bbs;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class BouncingBalls {
public static void main(String[] args) {
//Create the basic frame, set its size, and tell it to be visible
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.setVisible(true);
//Get a icon for the Program
ImageIcon logoicon = new ImageIcon("ball.jpg");
Image logo = logoicon.getImage();
frame.setIconImage(logo);
frame.setResizable(false);
frame.setResizable(true);
//find the center of the screen and where the frame should go
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int w = frame.getSize().width;
int h = frame.getSize().height;
int x = (dim.width-w)/2;
int y = (dim.height-h)/2;
//Move the window
frame.setLocation(x, y);
//Tell the program to stop when the X button is selected
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Draw object = new Draw();
frame.add(object);
object.drawing();
}
}
PAINTING CLASS
package bbs;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Draw extends JPanel {
/**
* This is added to handle the serialization warning and is of the type Long to accommodate the warning
*/
private static final long serialVersionUID = 1L;
public void drawing(){
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.drawLine(10, 20, 300, 200);
g.setColor(Color.BLUE);
g.fillRect(300, 200, 150, 200);
}
}
frame.setVisible(true);
This should be the last statement executed AFTER all components have been added to the frame.
Then all the components will paint normally.

Drawing on JSplitPane not showing

Just trying to draw some lines to the screen.
I've checked to ensure all the relevant code's being run
I've tried calling repaint (and ensuring that's being run)
Since this is a JSplitPane, the layout must be the JSplitPane layout
I'm setting the color to ensure it isn't drawing using the background color.
I've checked the height and width to ensure its size isn't 0 or something
I've tried drawing text as well; same result
I've changed the coordinates all over the place, tried both arbitrary and proportional values
Or at least I think. Swing is unintuitively quirky. I'd use AWT, but I need the specificity Swing offers. Anyway, the code. It's just a split pane, which is actually displaying - resizable and all - but the contents of the top pane (the only one I've attempted to put anything in) don't show.
package derange;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
public class Derange {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Derange");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.pack();
frame.setVisible(true);
//Create a split pane with the two scroll panes in it.
PanelScore scorePane = new PanelScore();
JScrollPane instrumentPane = new JScrollPane();
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
scorePane, instrumentPane);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation((frame.getHeight() / 4) * 3 );// Three-quarters of the way down
splitPane.setDividerSize(20);
//Provide minimum sizes for the two components in the split pane
Dimension minimumSize = new Dimension(frame.getWidth(), frame.getHeight()/ 2);//width, height
scorePane.setMinimumSize(minimumSize); //Score takes up at least half the screen
instrumentPane.setMinimumSize(new Dimension(0,0));//no minimum size on the instrument panel; collapsible
frame.getContentPane().add(splitPane);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
.
package derange;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import javax.swing.JScrollPane;
#SuppressWarnings("serial")//wtf is this needed for?
public class PanelScore extends JScrollPane{
public int strings = 6;
public void drawStaffTablature(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
int xStart = 30;//insets.left;
int xEnd = getParent().getWidth() - 30;
int yCoord = this.getHeight() / 2;
System.out.println(this.isShowing());
//Space between tablature lines
int lineSpacing = 15;
//Space between staffs.
int staffSpacing = 60;`enter code here`
for(int x = 0; x < strings; x++){
g2d.drawLine(xStart, yCoord + (lineSpacing * x), xEnd, yCoord + (lineSpacing * x));
//System.out.println("String: " + (x + 1));
g.drawString("Test", xStart, yCoord); //change the co-odrinates
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStaffTablature(g);
}
}
Short answer, don't extend from JScrollPane, a JScrollPane contains single component known as a JViewport, which covers the most of the scroll pane (the rest is taken up by the JScrollBars
Instead, try extending from something like JPanel.
I'd also advise you against using anything like int xEnd = getParent().getWidth() - 30; within your paint code, the Graphics context is translated to the components location, making the top/left corner 0x0 and clipped to the components current width and height

How can I color a design in java?

Hi! I have this problem: I have to make a program in java which designs a figure of a human and I have to paint it. I have wrote the code which design the human but I dont know how to fill the shapes with color.I am aware that I have to use "java.awt.Color" but I dont know how.
The colors have to be: background of the image(yellow), head(blue), arms & legs (green), body(red).
Here is my code so far:
import javax.swing.*;
import java.awt.*;
public class DrawPanelTest {
//creates a window to display the drawing
public static void main(String[] args) {
// create a new frame to hold the panel
JFrame application = new JFrame();
Container pane=application.getContentPane();
// create a panel that contains our drawing
DrawPanel panel = new DrawPanel();
// set the frame to exit when it is closed
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// add the panel to the frame
pane.add(panel);
application.setContentPane(pane);
// set the size of the frame
application.setSize(550, 450);
// make the frame visible
application.setVisible( true );
}
}
And here is where the figure is drawn:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawPanel extends JPanel {
public void paintComponent( Graphics g ) {
//draw the human
g.drawOval(300, 100, 100, 100);
g.drawRect(300, 200, 100, 100);
g.drawRect(400,200, 100, 10);
g.drawRect(200,200, 100, 10);
g.drawRect(300,300, 10, 100);
g.drawRect(390,300, 10, 100);
}
}
use g.fillOval() in place of g.drawOval()
set the color by g.setColor()
Concerning the background color, click on one link above, search for the term "background" and boom: Graphics.clearRect()
The documentation says:
Clears the specified rectangle by filling it with the background color of the current drawing surface.

Making image scrollable in JFrame contentpane

I am trying to display a large image inside a JFrame's contentpane. I would like to make the image or contentpane scrollable as the image is large. I tried to do it using Jscrollpane and add it into the contentpane but it didn't work. Did some searching for solution but end up failed to find one. Can someone guide me? My code are below
FinalEnvironment.java
package environment;
import java.awt.*;
import java.net.URL;
import javax.swing.*;
public class FinalEnvironment{
public FinalEnvironment(){
Image Eastlake;
URL EastlakeURL = null;
EastlakeURL = FinalEnvironment.class.getResource("/image1/eastlake_night.png");
Eastlake = Toolkit.getDefaultToolkit().getImage(EastlakeURL);
JFrame frame = new JFrame("UniCat World");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
JMenuBar yellowMenuBar = new JMenuBar();
Map map = new Map(800, 550, Eastlake);
yellowMenuBar.setOpaque(true);
yellowMenuBar.setBackground(Color.YELLOW);
yellowMenuBar.setPreferredSize(new Dimension(800, 50));
frame.setJMenuBar(yellowMenuBar);
JScrollPane scroller = new JScrollPane(map);
scroller.setAutoscrolls(true);
scroller.setPreferredSize(new Dimension(800, 550));
frame.getContentPane().add(scroller, BorderLayout.CENTER);
frame.setSize(800, 600);
frame.setVisible(true);
}
public static void main(String[] args){
FinalEnvironment fe = new FinalEnvironment();
}
}
Here is my map.java
package environment;
import java.awt.*;
import javax.swing.*;
public class Map extends JPanel{
private int width;
private int height;
private Image img;
public Map(int width, int height, Image img){
this.width = width;
this.height = height;
this.img = img;
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img,0,0,2624,1696,null);
}
}
Lastly, I would like to place Jbuttons on top of this image. Should I call a Rectangle and place it on top the image in the contentpane which then I use Point to position my buttons or should I straight away use the image or the component itself to do it? I need the button to be able to synchronize with the image when it is scrolled instead of static in the contentpane.
Thanks
What I would do here:
1.Have a panel (canvas) which only responsibility is to paint a given image independent of the real image size in overridden method paintComponent()
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
2.Make sure the canvas preferred size equals to image real size.
3.Have a second panel which will serve as content pane of a frame.
4.In it you will set a JScrollPane as its centre.
5.In the scroll pane viewport will be the component from step 1.
6.Add your button to canvas panel from step 1. It will be scrolled together with the image.
7.Add the content pane, the panel from step 3, to a frame, and run the application.
EDIT:
Code sample with button added to canvas, which stays always in its place, independent of scroll position or frame size.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollImageTest extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
private JPanel canvas;
public ScrollImageTest() {
try {
this.image = ImageIO.read(new URL("http://interviewpenguin.com/wp-content/uploads/2011/06/java-programmers-brain.jpg"));
}catch(IOException ex) {
Logger.getLogger(ScrollImageTest.class.getName()).log(Level.SEVERE, null, ex);
}
this.canvas = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
canvas.add(new JButton("Currently I do nothing"));
canvas.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
JScrollPane sp = new JScrollPane(canvas);
setLayout(new BorderLayout());
add(sp, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel p = new ScrollImageTest();
JFrame f = new JFrame();
f.setContentPane(p);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
What if you use your dimensions to set the Map's preferred size. For instance, give Map this method:
// method in the Map class
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
This way the Map JPanel will take up the necessary room to show the entire image. Also, why does your drawImage method in the paintComponent method have the large magic numbers? Why not use the width and height there as well? Edit 1: or don't even specify the image size as Boro suggests in his answer (1+ to him).
Why is everybody reinventing the wheel??? There is no need for a custom panel to paint the image!!!
All you need to do is create a JLabel and add an ImageIcon to the label and you won't have a problem. The label will:
paint the image at (0, 0) at its original size (which is exactly what the custom code is doing).
determine the preferred size of the image based on the image size. Now scrolling will happen automatically.
Also there is rarely any reason to use the setPreferredSize() method since all components have a default preferred size. So you should not set the default size of the menu bar. The only time I set a preferred size would be on the JScrollPane. This will allow the frame to be packed at a reasonable size and then scrollbars will appear automatically based on the size of the image in the label.
In addition to other helpful answers, you might like studying this example that uses mouse gestures to scroll arbitrary content.

Categories