Image gets disappeared in the vertical scrollpane view port - java

duke.png ->
arrowToLeft.gif ->
arrowToRight.gif ->
I need a UI where I want to depict a network device's graphical representation in Swing. For this, I am just loading multiple images and overlap them one by one to show the current state of the device. I have to support zooming for this view. The zoom code looks like this.
public class LayeredPaneTest{
private JLayeredPane layeredPane;
private JScrollPane scrollPane;
private static double MIN_ZOOM_VALUE = 0.60; // 1.0;
private static double MAX_ZOOM_VALUE = 2.0;
private static final int LAYER1 = 0;
private static final int LAYER2 = 1;
private static final int LAYER3 = 3;
private double scaleFactor = 1.0;
private JInternalFrame internalFrame =
new JInternalFrame("LayeredPaneTest",false, false, false, false);
public LayeredPaneTest(){
loadView();
}
public JInternalFrame getView(){
return internalFrame;
}
private void loadView(){
((javax.swing.plaf.basic.BasicInternalFrameUI)internalFrame.getUI())
.setNorthPane(null);
internalFrame.setBorder(null);
internalFrame.getContentPane().add(buildCenterPane());
internalFrame.setSize(new Dimension(200, 200));
internalFrame.setLocation(0, 0);
internalFrame.setResizable(false);
internalFrame.setVisible(true);
}
private JPanel buildCenterPane(){
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
layeredPane = new JLayeredPane();
Position shelfPosition = new Position(0,0,200,200);
setLayeredPaneSize(shelfPosition);
JLabel label = getImageLabel(getImage("duke.png"),
new Position(0,0,100,100));
layeredPane.add(label, Integer.valueOf(LAYER1));
label = getImageLabel(getImage("arrowToLeft.gif"),
new Position(100,100,50,50));
layeredPane.add(label, Integer.valueOf(LAYER2));
label = getImageLabel(getImage("arrowToRight.gif"),
new Position(50,50,50,50));
layeredPane.add(label, Integer.valueOf(LAYER3));
scrollPane = new JScrollPane(layeredPane);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(getSliderPane(), BorderLayout.SOUTH);
return panel;
}
private Image getImage(String key){
InputStream is = this.getClass().getResourceAsStream(key);
try {
return ImageIO.read(is);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void setLayeredPaneSize(Position shelfPosition){
int width = (int)(shelfPosition.getWidth() * scaleFactor + 10);
int height = (int)(shelfPosition.getHeight() * scaleFactor + 10);
layeredPane.setPreferredSize(new Dimension(width,height));
}
public void zoom(double factor){
if (factor < MIN_ZOOM_VALUE)
factor = MIN_ZOOM_VALUE;
else if (factor > MAX_ZOOM_VALUE)
factor = MAX_ZOOM_VALUE;
scaleFactor = factor;
Position shelfPosition = new Position(0,0,200,200);
setLayeredPaneSize(shelfPosition);
scrollPane.getViewport().repaint();
scrollPane.repaint();
scrollPane.getViewport().revalidate();
scrollPane.revalidate();
}
private JLabel getImageLabel(Image image, Position position) {
position = position.moveInRatio(scaleFactor);
ImageLabel label = new ImageLabel(new ImageIcon(image), position);
label.setOpaque(false);
label.setBounds(position.getLeft(),
position.getTop(),position.getWidth(),position.getHeight());
return label;
}
private JPanel getSliderPane(){
JPanel outer = new JPanel();
outer.setLayout(new FlowLayout());
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
final JSlider slider = new JSlider((int) (MIN_ZOOM_VALUE * 100),
(int) (MAX_ZOOM_VALUE * 100), 100);
slider.setMajorTickSpacing(1);
slider.setMinorTickSpacing(1);
double sliderValue = scaleFactor * 100.0;
slider.setValue((int) sliderValue);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
scaleFactor = slider.getValue() / 100.0;
zoom(scaleFactor);
}
});
panel.add(slider, BorderLayout.CENTER);
outer.add(panel);
return outer;
}
public static void main(String...strings){
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new LayeredPaneTest().getView(),
BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
static class Position{
private final int left,top,width,height;
Position(int x1, int y1, int x2, int y2){
this.left = x1;
this.top = y1;
this.width = x2;
this.height = y2;
}
public int getLeft() {
return left;
}
public int getTop() {
return top;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
private class ImageLabel extends JLabel{
private static final long serialVersionUID = 6152144095443433296L;
private ImageIcon image;
private Position position;
public ImageLabel(ImageIcon image, Position position){
super(image);
this.image = image;
this.position = position;
}
public void paintComponent(Graphics g) {
int newX = (int)(position.getLeft() * scaleFactor);
int newY = (int)(position.getTop() * scaleFactor);
Graphics2D g2 = (Graphics2D)g;
int newW = (int)(position.getWidth() * scaleFactor);
int newH = (int)(position.getHeight() * scaleFactor);
setBounds(newX, newY, newW, newH);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(image.getImage(), 0, 0, newW, newH, null);
}
}
}
But the problem here is, when I zoom in once and zoom out, some of the images disappear. Any idea why it behaves like this?
Following are the steps to reproduce the bug;
a. Drag the slider bar to the maximum,
b. Witness the image being expanded to maximum and the scroll bar is introduced,
c. Now few portion of the image is hidden,
d. Now drag the slider bar to minimum.
e. The hidden portion of the image disappears.

You do not honor the paint chain by calling super.XXX implementation of overridden paintComponent method like so:
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
//do the rest of the painting here
}
As the docs clearly state:
if you do not invoker super's implementation you must honor the opaque
property, that is if this component is opaque, you must completely
fill in the background in a non-opaque color. If you do not honor the
opaque property you will likely see visual artifacts.
Also note the #Override annotation to make sure I am overriding the correct method, and by default paintComponent is protected keep it that way as you dont want to expose this method to other classes they should use repaint()

Related

How to keep the other components in front when redrawing a JPanel used as a background?

I've looked all over and I can only find cases of people using their custom JPannel to draw an image over another image but what I'm trying to do is draw a component over the JPannel and no matter what I try it always moves the JPannel when it draws the next frame. It feels like ther must be a way to override the meathod that draws it on front and allows you to draw it in the very back but there was none that i could find.
public class MovingBackgroundDemo {
private JToggleButton button = new JToggleButton("Button");
private JFrame frame = new JFrame();
private int framewidth =frame.getWidth();
private int frameheight =frame.getHeight();
public MovingBackgroundDemo() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent evt) {
framewidth =frame.getWidth();
frameheight =frame.getHeight();
button.setBounds(framewidth/2 - framewidth/14 - framewidth/6,frameheight/6, framewidth/6, frameheight / 12);
}
});
frame.add(button);
frame.add(new AnimatingPanel());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class AnimatingPanel extends JPanel {
private static final int DIM_W = 350;
private static final int DIM_H = 350;
private static final int INCREMENT = 10;
private BufferedImage backgroundImage;
private Image runnerImage;
private int dx1, dy1, dx2, dy2;
private int srcx1, srcy1, srcx2, srcy2;
private int IMAGE_WIDTH;
public AnimatingPanel() {
initImages();
initImagePoints();
Timer timer = new Timer(40, new ActionListener() {
public void actionPerformed(ActionEvent e) {
moveBackground();
repaint();
}
});
timer.start();
FlowLayout layout = (FlowLayout)getLayout();
layout.setHgap(0);
layout.setVgap(0);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(backgroundImage, dx1, dy1, dx2, dy2, srcx1, srcy1,
srcx2, srcy2, this);
g.drawImage(runnerImage, 0, 0, getWidth(), getHeight(), this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(350, 350);
}
private void initImagePoints() {
dx1 = 0;
dy1 = 0;
dx2 = DIM_W;
dy2 = DIM_H;
srcx1 = 0;
srcy1 = 0;
srcx2 = DIM_W;
srcy2 = DIM_H;
}
private void initImages() {
try {
File icoen = new File("the picture");
runnerImage = ImageIO.read(icoen);
File icon = new File("the other one");
backgroundImage = ImageIO.read(icon);
IMAGE_WIDTH = backgroundImage.getWidth();
System.out.println(IMAGE_WIDTH);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void moveBackground() {
if (srcx1 > IMAGE_WIDTH) {
srcx1 = 0 - DIM_W;
srcx2 = 0;
} else {
srcx1 += INCREMENT;
srcx2 += INCREMENT;
}
}
}
public static void main(String[] args) {
new MovingBackgroundDemo();
}
}
Any tips?
This:
frame.add(button);
frame.add(new AnimatingPanel());
Should be:
JPanel animationPanel = new AnimatingPanel();
animationPanel.add(button);
frame.add(animationPanel);
The first code is trying to add the two components (button and panel) to the same exact layout position in the frame. That won't work (properly, or in some cases, at all).

How to resize JComponent forcefully in a null layout?

I am trying to resize a panel in a container panel where the container panel's layout is set to null.
I am setting the location and changing the size like this:
panelCapture.setLocation(
scale.scale(((capture.getTotalBounds().width + 500) / 2) - (capture.getTotalBounds().width / 2)),
scale.scale(((capture.getTotalBounds().height + 500) / 2) - (capture.getTotalBounds().height / 2))
);
Dimension pcSize = panelCapture.getSize();
Dimension pcNewSize = new Dimension(scale.scale(pcSize.width), scale.scale(pcSize.height));
panelCapture.setSize(pcNewSize);
panelCapture.setPreferredSize(pcNewSize);
The scaling multiplies it by my current scale which is 1.0f, 1.1f, etc.
Changing the position seems to work but changing the size does not.
Instead of using setLocation and setSize, try setBounds(...) or setPreferredSize(...). You may also need a call to repaint() after that. You may want to refer to the tutorial on using absolute/null layout here.
Here's a sample I wrote to change the size of a JPanel inside another JPanel which has a null layout (using only setBounds).
public class Capture
{
JPanel capture;
JPanel panelCapture;
JTextField scaleField;
JButton changeScale;
static final int PARENT_WIDTH = 800;
static final int PARENT_HEIGHT = 600;
static final int CHILD_WIDTH = 100;
static final int CHILD_HEIGHT = 100;
public static void main(String [] args)
{
Capture c = new Capture();
c.doStuff();
}
public void doStuff()
{
capture = new JPanel();
capture.setLayout(null);
capture.setPreferredSize(new Dimension(PARENT_WIDTH, PARENT_HEIGHT));
scaleField = new JTextField();
scaleField.setBounds(100, 550, 200, 25);
capture.add(scaleField);
changeScale = new JButton("Scale");
changeScale.setBounds(325, 550, 100, 25);
changeScale.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
panelCapture.setBounds(getBounds(CHILD_WIDTH * Float.parseFloat(scaleField.getText()), CHILD_HEIGHT * Float.parseFloat(scaleField.getText())));
}
});
capture.add(changeScale);
panelCapture = new JPanel();
panelCapture.setBackground(Color.blue);
panelCapture.setBounds(getBounds(CHILD_WIDTH, CHILD_HEIGHT));
capture.add(panelCapture);
JFrame frame = new JFrame();
frame.setContentPane(capture);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private Rectangle getBounds(float width, float height)
{
int left = (int) (PARENT_WIDTH - width) / 2;
int top = (int) (PARENT_HEIGHT - height) / 2;
return new Rectangle(left, top, (int) width, (int) height);
}
}

How to slowly change object color from one to another?

I am trying to achieve a scenario where the color of an object changes slowly from one color to another color.
I have the initial color as targetColor and final color as updateColor. The changingSpeed variable is set to 5.
The mechanism that I have to use is
Use getRed(), getGreen(), getBlue() to obtain the red, green and blue color
Compute the difference of color of target bytargetColor–color = [ dr dg db]
Normalize [ dr dg db] by dividing the norm of the vector [ dr dg db] T(Beware of div by zero)
Multiply it by changingSpeed to control the speed in changing the color
Update the color to color + [ dr’ dg’ db’ ]
So far, i have been able to make the following code:
dr=targetColor.getRed()-updateColor.getRed();
dg=targetColor.getGreen()-updateColor.getGreen();
db=targetColor.getBlue()-updateColor.getBlue();
double nrml= Math.sqrt((dr*dr)+(dg*dg)+(db*db));
dr=dr/nrml;
dg=dg/nrml;
db=db/nrml;
How to execute the 4th and 5th steps?
Can please anyone specify how to do this via code example?
Also please check if the above code is correct.
Here is an example the fades the background as you move from component to component:
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import java.util.ArrayList;
import javax.swing.*;
public class Fader
{
// background color when component has focus
private Color fadeColor;
// steps to fade from original background to fade background
private int steps;
// apply transition colors at this time interval
private int interval;
// store transition colors from orginal background to fade background
private Hashtable backgroundColors = new Hashtable();
/*
* Fade from a background color to the specified color using
* the default of 10 steps at a 50 millisecond interval.
*
* #param fadeColor the temporary background color
*/
public Fader(Color fadeColor)
{
this(fadeColor, 10, 50);
}
/*
* Fade from a background color to the specified color in the
* specified number of steps at the default 5 millisecond interval.
*
* #param fadeColor the temporary background color
* #param steps the number of steps to fade in the color
*/
public Fader(Color fadeColor, int steps)
{
this(fadeColor, steps, 50);
}
/*
* Fade from a background color to the specified color in the
* specified number of steps at the specified time interval.
*
* #param fadeColor the temporary background color
* #param steps the number of steps to fade in the color
* #param intevral the interval to apply color fading
*/
public Fader(Color fadeColor, int steps, int interval)
{
this.fadeColor = fadeColor;
this.steps = steps;
this.interval = interval;
}
/*
* Add a component to this fader.
*
* The fade color will be applied when the component gains focus.
* The background color will be restored when the component loses focus.
*
* #param component apply fading to this component
*/
public Fader add(JComponent component)
{
// Get colors to be used for fading
ArrayList colors = getColors( component.getBackground() );
// FaderTimer will apply colors to the component
new FaderTimer( colors, component, interval );
return this;
}
/*
** Get the colors used to fade this background
*/
private ArrayList getColors(Color background)
{
// Check if the color ArrayList already exists
Object o = backgroundColors.get( background );
if (o != null)
{
return (ArrayList)o;
}
// Doesn't exist, create fader colors for this background
ArrayList colors = new ArrayList( steps + 1 );
colors.add( background );
int rDelta = ( background.getRed() - fadeColor.getRed() ) / steps;
int gDelta = ( background.getGreen() - fadeColor.getGreen() ) / steps;
int bDelta = ( background.getBlue() - fadeColor.getBlue() ) / steps;
for (int i = 1; i < steps; i++)
{
int rValue = background.getRed() - (i * rDelta);
int gValue = background.getGreen() - (i * gDelta);
int bValue = background.getBlue() - (i * bDelta);
colors.add( new Color(rValue, gValue, bValue) );
}
colors.add( fadeColor );
backgroundColors.put(background, colors);
return colors;
}
class FaderTimer implements FocusListener, ActionListener
{
private ArrayList colors;
private JComponent component;
private Timer timer;
private int alpha;
private int increment;
FaderTimer(ArrayList colors, JComponent component, int interval)
{
this.colors = colors;
this.component = component;
component.addFocusListener( this );
timer = new Timer(interval, this);
}
public void focusGained(FocusEvent e)
{
alpha = 0;
increment = 1;
timer.start();
}
public void focusLost(FocusEvent e)
{
alpha = steps;
increment = -1;
timer.start();
}
public void actionPerformed(ActionEvent e)
{
alpha += increment;
component.setBackground( (Color)colors.get(alpha) );
if (alpha == steps || alpha == 0)
timer.stop();
}
}
public static void main(String[] args)
{
// Create test components
JComponent textField1 = new JTextField(10);
textField1.setBackground( Color.YELLOW );
JComponent textField3 = new JTextField(10);
JComponent textField4 = new JTextField(10);
JComponent button = new JButton("Start");
JComponent checkBox = new JCheckBox("Check Box");
JFrame frame = new JFrame("Fading Background");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add(textField1, BorderLayout.NORTH );
frame.getContentPane().add(button, BorderLayout.SOUTH );
frame.getContentPane().add(textField3, BorderLayout.WEST );
frame.getContentPane().add(textField4, BorderLayout.EAST );
frame.getContentPane().add(checkBox);
// Gradual Fading (using defaults)
// Fader fader = new Fader( new Color(155, 255, 155) );
Fader fader = new Fader( new Color(155, 255, 155), 10, 50 );
fader.add( textField1 );
fader.add( textField3 );
fader.add( checkBox );
// Instant Fading
fader = new Fader( new Color(255, 155, 155), 1, 1 );
fader.add( textField4 );
fader.add( button );
frame.pack();
frame.setVisible( true );
}
}
It uses a Timer to update the backgrounds at the specified interval. It then just interpolates between the two color based on the number of steps desired.
Look at this example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test {
public static void main(String args[]) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set some random initial color
final Component comp = frame.getContentPane();
comp.setBackground(new Color(
(float) Math.random(),
(float) Math.random(),
(float) Math.random()));
frame.setVisible(true);
final Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
final Color targetColor = new Color(30,40,50);
final int changingSpeed = 5;
final Color currentColor = comp.getBackground();
// step 1
int r = currentColor.getRed();
int g = currentColor.getGreen();
int b = currentColor.getBlue();
// step 2
double dr = targetColor.getRed() - r;
double dg = targetColor.getGreen() - g;
double db = targetColor.getBlue() - b;
// step 3
double norm = Math.sqrt(dr*dr+dg*dg+db*db);
if (norm < .001) {
((Timer)(evt.getSource())).stop();
return;
}
dr /= norm;
dg /= norm;
db /= norm;
// step 4
dr *= Math.min(changingSpeed, norm);
dg *= Math.min(changingSpeed, norm);
db *= Math.min(changingSpeed, norm);
// step 5
r += dr;
g += dg;
b += db;
comp.setBackground(new Color(r,g,b));
frame.repaint();
}
});
timer.start();
}
}
A few things to note:
Use a timer to fire the updates. This ensures, they are done in the EventThread, what is mandatory for manipulating Swing GUIs.
Test the norm for beeing very small. This means, your delta is near zero and you should stop updating the color.
Use the minimum of the norm and your changing speed. If your changing speed is high enough, your color will alternate around the target color and your delta will swap the sign forever and the process doesn't terminate. So if your delta is smaller than your changing speed, use the delta!
Don't forget to call repaint, when you are done with manipulating the color.
Yea, instead of using the for loop in line
while(i<=10);
you can actually use your changing speed. Meanwhile your changingSpeed should
be an array of length 3 because the different offset of colors interval i.e
dr, dg, db so that they can be taking care of independently.
like this...
int [] changingSpeed(int []Offset){
int loop = 5;
// 5 means the color should be change 5 times
int [] changeSpeed = new int[3];
changeSpeed[0]= offset[0]/loop;
changeSpeed[1]= offset[1]/loop;
changeSpeed[2]= offset[2]/loop;
return changeSpeed;
}
// your update method will now look like this
updateColor(int [] changeSpeed) throws AWTException{
int dr = changeSpeed[0];
int dg = changeSpeed[1];
int db = changeSpeed[2];
Robot slow = new Robot();
int i=0;
int f= loop; // the number of time you want the color to change
while(i<=f){
slow.delay(1000)
//sleep will sleep for 1000ms
setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10);
i++;
}
}
I wouldnt rely on a specific delay time to compute the next step in an animation as this will certainly provide different execution times in different machines, although it might not be a relevant difference.
Instead of a changing speed factor your could use a long representing the animation total time and use a Thread (or another multithreading mecanism) to control the animation lifecycle (such as calculating how much time has passed since the last repaint and whats the percentage of completion for the next iteration).
I don't know if I followed your math instructions.
I created a Swing GUI so i could watch the transition from one color to another.
The Reset Colors button generates a random starting color and a random finishing color. The Start button transforms the bottom panel from the starting color to the finishing color. Each transformation moves about 5 values in either the red, green, or blue dimension, with a delay of 300 milliseconds.
I tended to watch the numbers change, rather than the color. I wanted to make sure that the transformation was fairly even.
Anyway, here's the code. You can use this to model other Swing GUI's. I put all the classes together in one file so I could paste the code here. You should separate the classes into separate files.
package com.ggl.testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ColorChangeTest implements Runnable {
private static final Insets normalInsets = new Insets(10, 10, 0, 10);
private Color currentColor;
private Color targetColor;
private ColorPanel changingColorPanel;
private ColorPanel currentColorPanel;
private ColorPanel targetColorPanel;
private JLabel changingLabel;
private JLabel currentLabel;
private JLabel targetLabel;
public static void main(String args[]) {
SwingUtilities.invokeLater(new ColorChangeTest());
}
#Override
public void run() {
JFrame frame = new JFrame("Color Change Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(createColorPanel(), BorderLayout.NORTH);
mainPanel.add(createChangingPanel(), BorderLayout.CENTER);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createColorPanel() {
JPanel colorPanel = new JPanel();
setNewColors();
JPanel currentPanel = new JPanel();
currentPanel.setLayout(new BorderLayout());
JPanel currentLabelPanel = new JPanel();
currentLabelPanel.setLayout(new BorderLayout());
JLabel startLabel = new JLabel("Starting Color");
startLabel.setHorizontalAlignment(JLabel.CENTER);
currentLabelPanel.add(startLabel, BorderLayout.NORTH);
currentLabel = new JLabel(getColorString(currentColor));
currentLabel.setHorizontalAlignment(JLabel.CENTER);
currentLabelPanel.add(currentLabel, BorderLayout.SOUTH);
currentPanel.add(currentLabelPanel, BorderLayout.NORTH);
currentColorPanel = new ColorPanel(100, 100, currentColor);
currentPanel.add(currentColorPanel, BorderLayout.CENTER);
colorPanel.add(currentPanel);
JPanel targetPanel = new JPanel();
targetPanel.setLayout(new BorderLayout());
JPanel targetLabelPanel = new JPanel();
targetLabelPanel.setLayout(new BorderLayout());
JLabel finishLabel = new JLabel("Finishing Color");
finishLabel.setHorizontalAlignment(JLabel.CENTER);
targetLabelPanel.add(finishLabel, BorderLayout.NORTH);
targetLabel = new JLabel(getColorString(targetColor));
targetLabel.setHorizontalAlignment(JLabel.CENTER);
targetLabelPanel.add(targetLabel, BorderLayout.SOUTH);
targetPanel.add(targetLabelPanel, BorderLayout.NORTH);
targetColorPanel = new ColorPanel(100, 100, targetColor);
targetPanel.add(targetColorPanel, BorderLayout.CENTER);
colorPanel.add(targetPanel);
colorPanel.add(createButtonPanel());
return colorPanel;
}
private JPanel createButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridBagLayout());
int gridy = 0;
JButton resetButton = new JButton("Reset Colors");
resetButton.addActionListener(new ResetColorsListener(this));
addComponent(buttonPanel, resetButton, 0, gridy++, 1, 1, normalInsets,
GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);
JButton startButton = new JButton("Start");
startButton.addActionListener(new ColorChangeListener(this));
addComponent(buttonPanel, startButton, 0, gridy++, 1, 1, normalInsets,
GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);
return buttonPanel;
}
private JPanel createChangingPanel() {
JPanel changingPanel = new JPanel();
changingPanel.setLayout(new BorderLayout());
changingLabel = new JLabel(getColorString(currentColor));
changingLabel.setHorizontalAlignment(JLabel.CENTER);
changingPanel.add(changingLabel, BorderLayout.NORTH);
changingColorPanel = new ColorPanel(300, 200, currentColor);
changingPanel.add(changingColorPanel, BorderLayout.CENTER);
return changingPanel;
}
public void setChangingColorLabelText(Color color) {
changingLabel.setText(getColorString(color));
}
public void setNewColors() {
currentColor = getRandomColor();
targetColor = getRandomColor();
}
public void displayNewColors() {
currentLabel.setText(getColorString(currentColor));
targetLabel.setText(getColorString(targetColor));
changingLabel.setText(getColorString(currentColor));
currentColorPanel.setColor(currentColor);
targetColorPanel.setColor(targetColor);
changingColorPanel.setColor(currentColor);
}
public Color getCurrentColor() {
return currentColor;
}
public Color getTargetColor() {
return targetColor;
}
public ColorPanel getChangingColorPanel() {
return changingColorPanel;
}
private Color getRandomColor() {
return new Color((float) Math.random(), (float) Math.random(),
(float) Math.random());
}
private String getColorString(Color color) {
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
return "(" + r + ", " + g + ", " + b + ")";
}
private void addComponent(Container container, Component component,
int gridx, int gridy, int gridwidth, int gridheight, Insets insets,
int anchor, int fill) {
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, insets, 0, 0);
container.add(component, gbc);
}
public class ColorPanel extends JPanel {
private static final long serialVersionUID = -2894328511698328096L;
private Color color;
public ColorPanel(int width, int height, Color color) {
this.color = color;
this.setPreferredSize(new Dimension(width, height));
}
public void setColor(Color color) {
this.color = color;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ResetColorsListener implements ActionListener {
private ColorChangeTest colorChangeTest;
public ResetColorsListener(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void actionPerformed(ActionEvent event) {
colorChangeTest.setNewColors();
colorChangeTest.displayNewColors();
}
}
public class ColorChangeListener implements ActionListener {
private ColorChangeTest colorChangeTest;
public ColorChangeListener(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void actionPerformed(ActionEvent event) {
ColorChange colorChange = new ColorChange(colorChangeTest);
new Thread(colorChange).start();
}
}
public class ColorChange implements Runnable {
private static final long sleepTime = 300L;
private double r, g, b, dr, dg, db;
private int tr, tg, tb, cr, cg, cb;
private ColorChangeTest colorChangeTest;
public ColorChange(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void run() {
calculateColorChange();
sleep(sleepTime);
while (calculateNextColor()) {
sleep(sleepTime);
}
setColor(colorChangeTest.getTargetColor());
}
private void calculateColorChange() {
double increment = 5D;
// step 1
r = cr = colorChangeTest.getCurrentColor().getRed();
g = cg = colorChangeTest.getCurrentColor().getGreen();
b = cb = colorChangeTest.getCurrentColor().getBlue();
// step 2
tr = colorChangeTest.getTargetColor().getRed();
tg = colorChangeTest.getTargetColor().getGreen();
tb = colorChangeTest.getTargetColor().getBlue();
dr = tr - cr;
dg = tg - cg;
db = tb - cb;
// step 3
double d = Math.sqrt(dr * dr + dg * dg + db * db);
int steps = (int) (d / increment);
dr /= (double) steps;
dg /= (double) steps;
db /= (double) steps;
setColor(new Color(cr, cg, cb));
}
private boolean calculateNextColor() {
// step 5
r += dr;
g += dg;
b += db;
if (isFinished()) {
return false;
} else {
setColor(new Color(round(r), round(g), round(b)));
return true;
}
}
private boolean isFinished() {
return isColorFinished(cr, tr, round(r))
|| isColorFinished(cg, tg, round(g))
|| isColorFinished(cb, tb, round(b));
}
private int round(double value) {
return (int) Math.round(value);
}
private boolean isColorFinished(int original, int target, int current) {
boolean isFinished = false;
if (current < 0 || current > 255) {
isFinished = true;
} else if ((target >= original) && (current >= target)) {
isFinished = true;
} else if ((target <= original) && (current <= target)) {
isFinished = true;
}
return isFinished;
}
private void setColor(final Color color) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
colorChangeTest.getChangingColorPanel().setColor(color);
colorChangeTest.setChangingColorLabelText(color);
}
});
}
private void sleep(long sleepTime) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
}
In order to achieve the act of slowness, it might be a good idea to use the instance of Robot class.
//Method to obtain the offset of the color
static int [] getColorOffset(Color initial, Color final){
int [] colorOffset = new int[3];
colorOffset[0]= final.getRed()-initial.getRed();
colorOffset[1] = final.getGreen()-initial.getGreen();
colorOffset[2]= final.getBlue()-initial.getBlue();
return colorOffset;
}
updateColor(int [] colorOffset) throws AWTException{
int dr = colorOffset[0];
int dg = colorOffset[1];
int db = colorOffset[2];
Robot slow = new Robot();
int i=0;
while(i<=10){
slow.delay(1000)
//sleep will sleep for 1000ms
setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10);
i++;
}
}
public static void main(String args[]) throws AWTException{
Color initial = Color.black;
Color final = Color,white;
int [] colorOffset = getColorOffset(initial, final);
updateColor(colorOffset);
}

Click on icon in a JTextField and clear its content

I am trying to create a JTextField with an image and a hint. The function of the textfield is a search field to search some books. Now, I like to go a little bit further. I would like to give the image a function. For example, if I click on the image the text in the textfield should be cleared.
To achieve this implementation I created a new class and extended it with JTextField.
This is the code:
public class JSearchTextField extends JTextField implements FocusListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private String textWhenNotFocused;
private Icon icon;
private Insets dummyInsets;
private JTextField dummy;
public JSearchTextField() {
super();
Border border = UIManager.getBorder("TextField.border");
dummy = new JTextField("Suchen...");
this.dummyInsets = border.getBorderInsets(dummy);
icon = new ImageIcon(JSearchTextField.class.getResource("/images/clearsearch.png"));
this.addFocusListener(this);
}
public JSearchTextField(String textWhenNotFocused) {
this();
this.textWhenNotFocused = textWhenNotFocused;
}
public void setIcon(ImageIcon newIcon){
this.icon = newIcon;
}
public String getTextWhenNotFocused() {
return this.textWhenNotFocused;
}
public void setTextWhenNotFocused(String newText) {
this.textWhenNotFocused = newText;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
int textX = 2;
if(!this.hasFocus() && this.getText().equals("")) {
int height = this.getHeight();
Font prev = this.getFont();
Font italic = prev.deriveFont(Font.ITALIC);
Color prevColor = g.getColor();
g.setFont(italic);
g.setColor(UIManager.getColor("textInactiveText"));
int h = g.getFontMetrics().getHeight();
int textBottom = (height - h) / 2 + h - 4;
int x = this.getInsets().left;
Graphics2D g2d = (Graphics2D) g;
RenderingHints hints = g2d.getRenderingHints();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.drawString(textWhenNotFocused, x, textBottom);
g2d.setRenderingHints(hints);
g.setFont(prev);
g.setColor(prevColor);
} else {
int iconWidth = icon.getIconWidth();
int iconHeight = icon.getIconHeight();
int x = dummy.getWidth() + dummyInsets.right;
textX = x - 420;
int y = (this.getHeight() - iconHeight)/2;
icon.paintIcon(this, g, x, y);
}
setMargin(new Insets(2, textX, 2, 2));
}
#Override
public void focusGained(FocusEvent arg0) {
this.repaint();
}
#Override
public void focusLost(FocusEvent arg0) {
this.repaint();
}
}
And this is where I create the fields;
txtSearchBooks = new JSearchTextField("Buch suchen...");
Now back to my question. Do you have any idea how I can give the image a function where the text will be automatically cleared? I tried to implement a MouseListener and set the text of "txtSearchBooks" to null but it hasn't worked.
I hope I didn't go off in the wrong direction.
Sorry for the long post but I would really appreciate to get some advice.
A JTextField is a JComponent, meaning it is also a container for other components. You can use the add(Component c) method to add other components to it. BUT A JTextField won't show its added components unless you provide a LayoutManager to it. Then it behaves just like a normal JPanel.
I made a small example how you can manage what you need. The label is showed to the right, and clicking it will clear the field. You can use a button as well, instead of label.
Please note you don't need to create the Image object from scratch as I do, you can load it from a file. I create it this way so that the example doesn't rely on other files.
public class TextFieldWithLabel {
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Search...");
textField.setLayout(new BorderLayout());
//creating dummy image...
Image image = new BufferedImage(25, 25, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, 25, 25);
graphics.setColor(Color.RED);
graphics.fillRect(2, 11, 21, 3);
graphics.fillRect(11, 2, 3, 21);
JLabel label = new JLabel(new ImageIcon(image));
textField.add(label, BorderLayout.EAST);
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
textField.setText("");
}
});
frame.add(textField);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

Java GridBagLayout: Rows are overlapping

I have a pretty simple JFrame. There are three main panels: the banner image at the top, a list of buttons on the left-hand side, and the main panel where the user will input his login information to access the rest of the application.
I'm using a GridBagLayout, and despite most people avoiding it like the plague, it's very straightforward to me, though it does add many lines of code into the mix. However I'm getting this strange problem where the top row (banner image) is overlapping the bottom row (button and login panels). I've checked and double-checked and looked all over the web for an answer but can't figure out what I'm doing wrong.
Basically the bottom row is vertically centered in the JFrame as a whole, and not in the second GridBag row as it should. And somehow the BannerPanel is being drawn on top of that, despite being added to the screen beforehand. I think it may have something to do with the way the BannerPanel works but I for one can't find a workaround.
This is what it looks like:
https://sphotos-a.xx.fbcdn.net/hphotos-snc7/375898_3720190211823_1073177291_n.jpg
This is what it SHOULD look like:
https://sphotos-a.xx.fbcdn.net/hphotos-ash4/314993_3720190291825_1429407717_n.jpg
Here's my code:
public class LoginWindow extends JFrame implements ActionListener {
final static String unlockCode = "unlock";
ArrayList <User> userlist = new ArrayList <User> ();
User user = null;
// The visible parts of the window
GridBagConstraints gridbag;
JLabel inputLabel, errorLabel, lockedLabel, unlockLabel;
JTextField usernameField, unlockField;
JPasswordField passwordField;
JPanel inputPanel, usernamePanel, passwordPanel, unlockPanel;
public static void main(String[] args) {
LoginWindow win = new LoginWindow ();
win.userlist.add(new User ("username", "password", true));
}
public LoginWindow () {
setTitle("Login");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(Color.GRAY);
setSize(640, 480);
setResizable(false);
resetGridbag();
// This is where I declare all the JLabels, JPanels, etc
inputLabel = new JLabel ("Secure Login");
inputLabel.setFont(new Font ("SansSerif", Font.BOLD, 24));
inputLabel.setForeground(Color.WHITE);
JLabel usernameLabel = new JLabel ("Username ");
usernameLabel.setForeground(Color.WHITE);
usernameField = new JTextField (10);
usernameField.setActionCommand("Login");
usernameField.addActionListener(this);
usernamePanel = new JPanel ();
usernamePanel.setBackground(Color.GRAY);
usernamePanel.add(usernameLabel);
usernamePanel.add(usernameField);
JLabel passwordLabel = new JLabel ("Password ");
passwordLabel.setForeground(Color.WHITE);
passwordField = new JPasswordField (10);
passwordField.setActionCommand("Login");
passwordField.addActionListener(this);
passwordPanel = new JPanel ();
passwordPanel.setBackground(Color.GRAY);
passwordPanel.add(passwordLabel);
passwordPanel.add(passwordField);
errorLabel = new JLabel ("");
errorLabel.setForeground(Color.WHITE);
lockedLabel = new JLabel ("You've been locked out!");
lockedLabel.setForeground(Color.WHITE);
unlockLabel = new JLabel ("Unlock Code");
unlockLabel.setForeground(Color.WHITE);
unlockField = new JTextField (10);
unlockField.setActionCommand("Unlock");
unlockField.addActionListener(this);
unlockPanel = new JPanel ();
unlockPanel.setBackground(Color.GRAY);
unlockPanel.add(unlockLabel);
unlockPanel.add(unlockField);
JLabel newPassword = new JLabel ("Request a new password");
newPassword.setForeground(Color.WHITE);
JPanel optionPanel = new JPanel ();
optionPanel.setBackground(Color.GRAY);
optionPanel.add(newPassword);
inputPanel = new JPanel ();
inputPanel.setBackground(Color.GRAY);
inputPanel.setLayout(new GridBagLayout ());
// Now I'm going to add them all to the screen
GridBagLayout gbl = new GridBagLayout ();
gbl.columnWeights = new double [] {0.0f, 1.0f};
gbl.rowWeights = new double [] {0.0f, 1.0f};
setLayout(gbl);
gridbag.gridwidth = 2;
gridbag.gridy = 0;
gridbag.fill = GridBagConstraints.HORIZONTAL;
add(new BannerPanel (), gridbag);
gridbag.gridy = 1;
gridbag.gridwidth = 1;
gridbag.anchor = GridBagConstraints.NORTHWEST;
add(optionPanel, gridbag);
gridbag.gridx++;
gridbag.anchor = GridBagConstraints.CENTER;
add(inputPanel, gridbag);
redraw();
setVisible(true);
}
public void resetGridbag () {
gridbag = new GridBagConstraints ();
gridbag.anchor = GridBagConstraints.CENTER;
gridbag.gridx = gridbag.gridy = 0;
}
public void reset () {
inputPanel.removeAll();
resetGridbag();
validate();
repaint();
}
public void redraw () {
reset();
if (user == null || !user.locked()) {
inputPanel.add(inputLabel, gridbag);
gridbag.gridy++;
inputPanel.add(new JLabel (" "), gridbag);
gridbag.gridy++;
inputPanel.add(usernamePanel, gridbag);
gridbag.gridy++;
inputPanel.add(passwordPanel, gridbag);
gridbag.gridy++;
inputPanel.add(new JLabel (" "), gridbag);
gridbag.gridy++;
inputPanel.add(errorLabel, gridbag);
}
else {
inputPanel.add(lockedLabel, gridbag);
gridbag.gridy++;
inputPanel.add(unlockPanel, gridbag);
gridbag.gridy++;
inputPanel.add(errorLabel, gridbag);
errorLabel.setText("");
}
validate();
repaint();
}
public void actionPerformed (ActionEvent e) {
String button = e.getActionCommand();
if (button.equals("Login")) {
boolean usernameMatch = false;
boolean passwordMatch = false;
for (int i = 0; i < userlist.size(); i++) {
if (usernameField.getText().equals(userlist.get(i).username())) {
usernameMatch = true;
user = userlist.get(i);
}
if (new String (passwordField.getPassword()).equals(userlist.get(i).password()))
passwordMatch = true;
}
passwordField.setText("");
if (usernameMatch) {
if (passwordMatch) {
user.unlock();
//new MainWindow ();
dispose();
}
else {
user.loginFail();
if (!user.locked())
errorLabel.setText("Login unsuccessful. " +
user.loginAttempts() + " attempts left!");
else
redraw();
}
}
else
errorLabel.setText("Login unsuccessful.");
validate();
}
else if (button.equals("Unlock")) {
if (unlockField.getText().equals(unlockCode)) {
errorLabel.setText("");
user.unlock();
redraw();
}
else {
errorLabel.setText("Invalid unlock code.");
validate();
}
unlockField.setText("");
}
}
}
class BannerPanel extends JPanel {
Image image;
int width = 0, height = 0;
double ratio = 0.0;
public BannerPanel () {
try {
image = ImageIO.read(BannerPanel.class
.getClassLoader().getResourceAsStream("banner.png"));
}
catch (Exception e) { e.printStackTrace(); }
}
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
ratio = (double) getWidth() / image.getWidth(null);
width = getWidth();
height = getImageHeight();
setSize(width, height);
if (image != null) {
g.drawImage(image, 0, 0, width, height, this);
}
}
public int getImageHeight () {
return (int) (image.getHeight(null) * ratio);
}
}
public class User {
String username = "";
String password = "";
boolean superuser = false;
int loginAttempts = 3;
public User (String username, String password, boolean superuser) {
this.username = username;
this.password = password;
this.superuser = superuser;
}
public String username () {
return username;
}
public String password () {
return password;
}
public boolean superuser () {
return superuser;
}
public int loginAttempts () {
return loginAttempts;
}
public void loginFail () {
if (loginAttempts > 0)
loginAttempts--;
}
public boolean locked () {
return (loginAttempts == 0);
}
public void lock () {
loginAttempts = 0;
}
public void unlock () {
loginAttempts = 3;
}
}
The height of your BannerPanel is based on the height of the image (height = getImageHeight();). However, this is the height the BannerPanel is drawn at, not the height it asks for. You need to override getPreferredSize() to deliver the correct desired height based on the image and ratio - otherwise the layout will be made assuming 0 height for the BannerPanel.
Edit
I think the problem is that you're trying to create a component with a fixed ratio between height and width, while deferring the width to the parent container. This creates a situation where you have to wait for the layout to be performed once before you can know the component's preferred size. Performing these calculations in paintComponent will work, as you've experienced, by waiting for layout, resizing, drawing, waiting for layout and drawing again. It's not ideal - you do have to perform the layout twice, but there's no intrinsic need to do the drawing twice. I don't think Swing gives you enough control to reliably avoid this, but there are more idiomatic ways to do it! For example, you can override setBounds to change the preferred height based on the actual width:
#Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
int pWidth = getPreferredSize().width;
setPreferredSize(new Dimension(pWidth, width / 2));
}
Jacob Raihle had the right idea, however it didn't work for me.
What did work was adding a call to setPreferredSize() to paintComponent(), right next to setSize().
Apparently setSize() allows the entire image to be drawn while setPreferredSize() tells the rest of the layout how much space it's taking up. Would be great to have it all in one method without having to override but hey, what are you gonna do?
Here's the new code for my BannerPanel object:
class BannerPanel extends JPanel {
Image image;
int width = 0, height = 0;
double ratio = 0.0;
public BannerPanel () {
try {
image = ImageIO.read(BannerPanel.class
.getClassLoader().getResourceAsStream("banner.png"));
}
catch (Exception e) { e.printStackTrace(); }
}
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
ratio = (double) getWidth() / image.getWidth(null);
width = getWidth();
height = getImageHeight();
setPreferredSize(new Dimension (width, height));
setSize(width, height);
if (image != null) {
g.drawImage(image, 0, 0, width, height, this);
}
}
public int getImageHeight () {
return (int) (image.getHeight(null) * ratio);
}
}

Categories