Creating custom JComponent with JLayer over JProgressBar - java

I'm attempting to create a customized JProgressBar that utilizes the JLayer class so that it can be colored differently depending on the situation, a la this solution. The thing is that I want to wrap it up as a JComponent of some sort since it makes it more manageable. I would expose it as a customized JLayer, but that class is sealed, so nothing doing.
I've tried just making it a JComponent, but nothing is drawn on the screen (probably because I have no idea how to make a custom JComponent that contains other components inside of it?). I've tried a JFrame, which works, but the sizing is all wrong, likely because the progress bar is using the layout manager of the JFrame I made instead of the layout manager containing the JFrame. I've tried JProgressBar, which draws, but then I have no way of returning the JLayer and preserving the correct hierarchy without additional method calls after the constructor, which just doesn't seem elegant.
Here's my sample code, based heavily on the code from the question I linked to above:
public class Test {
public JComponent makeUI() {
final BoundedRangeModel model = new DefaultBoundedRangeModel();
model.setValue(20);
final JPanel p = new JPanel(new GridLayout(4, 1, 12, 12));
p.setBorder(BorderFactory.createEmptyBorder(24,24,24,24));
// This does not draw
final ColorProgressBar pb4 = new ColorProgressBar(model);
p.add(pb4);
JPanel panel = new JPanel(new BorderLayout());
panel.add(p, BorderLayout.NORTH);
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Test().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ColorProgressBar extends JComponent {
private static final long serialVersionUID = -1265489165072929352L;
private BlockedColorLayerUI colorUI = new BlockedColorLayerUI();
private JLayer<JProgressBar> layer;
private JProgressBar bar;
private PropertyChangeSupport supporter = new PropertyChangeSupport(new WeakReference<ColorProgressBar>(this));
public ColorProgressBar(BoundedRangeModel model) {
bar = new JProgressBar(model);
layer = new JLayer<JProgressBar>(bar, colorUI);
this.add(layer);
}
public Color getColor() {
if (colorUI == null)
return null;
return colorUI.color;
}
public void setColor(Color color) {
Color oldColor = colorUI.color;
colorUI.color = color;
supporter.firePropertyChange("color", oldColor, color);
}
#Override
public void paintComponents(Graphics g) {
layer.paintComponents(g);
}
class BlockedColorLayerUI extends LayerUI<JProgressBar> {
public Color color = null;
private BufferedImage bi;
private int prevw = -1;
private int prevh = -1;
#Override public void paint(Graphics g, JComponent c) {
if(color != null) {
JLayer<?> jlayer = (JLayer<?>)c;
JProgressBar progress = (JProgressBar)jlayer.getView();
int w = progress.getSize().width;
int h = progress.getSize().height;
if(bi==null || w!=prevw || h!=prevh) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
prevw = w;
prevh = h;
Graphics2D g2 = bi.createGraphics();
super.paint(g2, c);
g2.dispose();
Image image = c.createImage(
new FilteredImageSource(bi.getSource(),
new ColorFilter(color)));
g.drawImage(image, 0, 0, c);
} else {
super.paint(g, c);
}
}
}
class ColorFilter extends RGBImageFilter {
Color color;
public ColorFilter(Color color) {
this.color = color;
}
#Override public int filterRGB(int x, int y, int argb) {
int r = (int)((argb >> 16) & 0xff);
int g = (int)((argb >> 8) & 0xff);
int b = (int)((argb ) & 0xff);
return (argb & color.getRGB()) | (g<<16) | (r<<8) | (b);
}
}
}
Anyone know where I'm going wrong? Thanks!
EDIT: I've trimmed down the example slightly and modified it to better express my issue. Sorry for the confusion.

You may need to call super(model); and p.add(pb4.layer);
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.beans.*;
import java.lang.ref.WeakReference;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class Test2 {
public JComponent makeUI() {
final BoundedRangeModel model = new DefaultBoundedRangeModel();
final JPanel p = new JPanel(new GridLayout(4, 1, 12, 12));
p.setBorder(BorderFactory.createEmptyBorder(24,24,24,24));
final JProgressBar pb1 = new JProgressBar(model);
pb1.setStringPainted(true);
p.add(pb1);
final JProgressBar pb2 = new JProgressBar(model);
pb2.setStringPainted(true);
p.add(pb2);
p.add(new JProgressBar(model));
final ColorProgressBar pb4 = new ColorProgressBar(model);
p.add(pb4.layer);
JPanel box = new JPanel();
box.add(new JButton(new AbstractAction("+10") {
private int i = 0;
#Override public void actionPerformed(ActionEvent e) {
model.setValue(i = (i>=100) ? 0 : i + 10);
}
}));
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa511486.aspx
box.add(new JCheckBox(new AbstractAction(
"<html>Turn the progress bar red<br />"+
" when there is a user recoverable condition<br />"+
" that prevents making further progress.") {
#Override public void actionPerformed(ActionEvent e) {
boolean b = ((JCheckBox)e.getSource()).isSelected();
pb2.setForeground(b? new Color(255,0,0,100) : new Color(255,255,0,100));
if (b)
pb4.setColor(Color.RED);
else
pb4.setColor(Color.YELLOW);
p.repaint();
}
}));
JPanel panel = new JPanel(new BorderLayout());
panel.add(p, BorderLayout.NORTH);
panel.add(box, BorderLayout.SOUTH);
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Test2().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ColorProgressBar extends JProgressBar {
private static final long serialVersionUID = -1265489165072929352L;
private BlockedColorLayerUI colorUI = new BlockedColorLayerUI();
public JLayer<JProgressBar> layer;
private PropertyChangeSupport supporter = new PropertyChangeSupport(new WeakReference<ColorProgressBar>(this));
public ColorProgressBar(BoundedRangeModel model) {
super(model);
layer = new JLayer<JProgressBar>(this, colorUI);
}
public Color getColor() {
if (colorUI == null)
return null;
return colorUI.color;
}
public void setColor(Color color) {
Color oldColor = colorUI.color;
colorUI.color = color;
supporter.firePropertyChange("color", oldColor, color);
}
// #Override
// public void paintComponents(Graphics g) {
// layer.paintComponents(g);
// }
class BlockedColorLayerUI extends LayerUI<JProgressBar> {
public Color color = null;
private BufferedImage bi;
private int prevw = -1;
private int prevh = -1;
#Override public void paint(Graphics g, JComponent c) {
if(color != null) {
JLayer<?> jlayer = (JLayer<?>)c;
JProgressBar progress = (JProgressBar)jlayer.getView();
int w = progress.getSize().width;
int h = progress.getSize().height;
if(bi==null || w!=prevw || h!=prevh) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
prevw = w;
prevh = h;
Graphics2D g2 = bi.createGraphics();
super.paint(g2, c);
g2.dispose();
Image image = c.createImage(
new FilteredImageSource(bi.getSource(),
new ColorFilter(color)));
g.drawImage(image, 0, 0, c);
} else {
super.paint(g, c);
}
}
}
class ColorFilter extends RGBImageFilter {
Color color;
public ColorFilter(Color color) {
this.color = color;
}
#Override public int filterRGB(int x, int y, int argb) {
int r = (int)((argb >> 16) & 0xff);
int g = (int)((argb >> 8) & 0xff);
int b = (int)((argb ) & 0xff);
return (argb & color.getRGB()) | (g<<16) | (r<<8) | (b);
}
}
}

Related

Adding ViewPort to JPanel

I would like to achieve the following for my swing project,
I was able to get this by extending a JPanel and and using JLayer.
I event tried using AlphaComposite but it didn't work.
Edit1: I think JTable or JViewport might help me to get what I want but I don't know how to use them.
Edit2: Updated the SSCCE, thank you trashgod for the suggestion.
I have made use of the Scalr library because after using getScaledInstance method of Image class, if i tried to use the getSubImage method of BufferedImage,the following exception is thrown:
java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be
cast to java.awt.image.BufferedImage
since the Image generated by getScaledInstance method is a instance of ToolkitImage so, it cannot be cast into a BufferedImage.
If you don't want to use the Scalr library,you can use the code suggested in this post to scale the BufferedImage and than use getSubImage method.
SCSE.java
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
import org.imgscalr.Scalr;
public class SCSE {
private JFrame mainFrame;
private JPanel mainPanel;
private GridView gridView;
private JButton imgBtn, shuffleBtn;
private int gridX = -1, gridY = -1, gridWidth = -1, gridHeight = -1;
private boolean isGridEmpty = false;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SCSE sc = new SCSE();
sc.createGUI();
});
}
private void createGUI() {
mainFrame = new JFrame();
mainFrame.setSize(500, 500);
mainFrame.setResizable(false);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainPanel = new JPanel(new BorderLayout());
gridView = new GridView();
imgBtn = new JButton("Get-Image");
shuffleBtn = new JButton("Shuffle-ViewPort");
imgBtn.addActionListener((ActionEvent evt) -> {
try {
gridView.setImage(ImageIO.read(new URL("http://www.keenthemes.com/preview/metronic/theme/assets/global/plugins/jcrop/demos/demo_files/image1.jpg")));
} catch (IOException ex) {
System.out.println(ex);
}
});
shuffleBtn.addActionListener((ActionEvent evt) -> {
gridView.startShuffle();
});
mainPanel.add(gridView.getComponent(), BorderLayout.CENTER);
mainPanel.add(imgBtn, BorderLayout.NORTH);
mainPanel.add(shuffleBtn, BorderLayout.SOUTH);
mainFrame.add(mainPanel);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
}
class GridView {
private Random shuffleRandom;
private RegisterUI layerUi = null;
private JLayer<JPanel> gridLayer = null;
private ImagePanel mainPanel = null;
private int gridNos = 21; //number of grids
int digit[];
private int viewportDimensions = 4; //no of rows and columns in viewport
private JLabel gridLabel[][] = new JLabel[gridNos][gridNos];
private int gridX = -1, gridY = -1, gridWidth = -1, gridHeight = -1;
private boolean isGridInitialized = false;
public GridView() {
initPersuasive();
initPanel();
initGrids();
}
private void initPanel() {
mainPanel = new ImagePanel();
mainPanel.setLayout(new GridLayout(gridNos, gridNos, 0, 0)); //creates layout to place labels in grid form
layerUi = new RegisterUI();
gridLayer = new JLayer<>(mainPanel, layerUi);
}
private void initGrids() {
for (int i = 0; i < gridNos; i++) {
for (int j = 0; j < gridNos; j++) {
gridLabel[i][j] = new JLabel();
gridLabel[i][j].setOpaque(false);
gridLabel[i][j].setName("" + (i + 1)); // Since for loop index is 0 to 80, we add 1 to the name to make it 1 to 81
mainPanel.add(gridLabel[i][j]); //add it to mainPanel
}
}
}
private void initPersuasive() {
shuffleRandom = new Random();
digit = new int[2];
}
private void random() {
digit[0] = shuffleRandom.nextInt(gridNos - viewportDimensions);
digit[1] = shuffleRandom.nextInt(gridNos - viewportDimensions);
}
public void startShuffle() {
random();
int x = gridLabel[digit[0]][digit[1]].getX();
int y = gridLabel[digit[0]][digit[1]].getY();
layerUi.placeViewport(x, y);
}
public void stopShuffle() {
layerUi.removeViewPort();
}
public void setupGridView() {
gridX = gridLabel[0][0].getX();
gridY = gridLabel[0][0].getY();
gridWidth = gridLabel[0][0].getWidth();
gridHeight = gridLabel[0][0].getHeight();
mainPanel.setValues(gridX, gridY);
layerUi.setViewSize(gridWidth * viewportDimensions, gridHeight * viewportDimensions);
}
public void setImage(BufferedImage img) {
if (!isGridInitialized) {
setupGridView();
isGridInitialized = true;
}
BufferedImage sendImg = Scalr.resize(img, Scalr.Mode.FIT_EXACT, gridWidth * gridNos, gridHeight * gridNos, Scalr.OP_ANTIALIAS);
layerUi.setupViewport(img);
mainPanel.paintImage(img);
}
public JLayer<JPanel> getComponent() {
return gridLayer;
}
}
class RegisterUI extends LayerUI<JPanel> {
private int viewX, viewY, viewWidth, viewHeight;
private boolean shuffleIsRunning = false;
private BufferedImage viewportImage = null;
private void drawPCCP(Graphics g, int w, int h) {
Graphics2D g2 = ((Graphics2D) g);
Color c = new Color(1.0f, 1.0f, 1.0f, 0.7f);
g2.setPaint(c);
g2.fillRect(0, 0, w, h);
BufferedImage highlightGrid = Scalr.pad(Scalr.crop(viewportImage, viewX, viewY, viewWidth, viewHeight), 2, Color.BLACK, Scalr.OP_ANTIALIAS);
g2.drawImage(highlightGrid, viewX, viewY, null);
g2.dispose();
}
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
int w = c.getWidth();
int h = c.getHeight();
if (shuffleIsRunning) {
drawPCCP(g, w, h);
}
}
public void setupViewport(BufferedImage bi) {
viewportImage = bi;
}
public void setViewSize(int w, int h) {
viewWidth = w;
viewHeight = h;
}
public void placeViewport(int x, int y) {
viewX = x;
viewY = y;
if (!shuffleIsRunning) {
shuffleIsRunning = true;
}
firePropertyChange("shuffleui", 0, 1);
}
public void removeViewPort() {
if (!shuffleIsRunning) {
return;
}
viewX = 0;
viewY = 0;
viewWidth = 0;
viewHeight = 0;
shuffleIsRunning = false;
firePropertyChange("shuffleui", 0, 1);
}
#Override
public void applyPropertyChange(PropertyChangeEvent evt, JLayer<? extends JPanel> l) {
if ("disableui".equals(evt.getPropertyName()) || "shuffleui".equals(evt.getPropertyName())) {
l.repaint();
}
}
}
class ImagePanel extends JPanel {
private BufferedImage displayImage = null;
private int x, y;
public void setValues(int x, int y) {
this.x = x;
this.y = y;
}
public void paintImage(BufferedImage bi) {
System.out.print(bi);
displayImage = bi;
repaint(); // repaint calls paintComponent method internally
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(displayImage, x, y, this); // To Paint the image on the panel
}
}
}
Instead of using AlphaComposite directly, as shown here, try RescaleOp to alter the image's color/alpha bands, as shown in this example. This will allow you to mute the tone of the entire image as desired. Copy a portion of the original image using getSubimage() to restore the highlight.

Java swing: Extended jpanel class will not brighten upon repainting.

I have done much more complicated problems with no issues for my course, but this is giving me fits! My problem is simple, I cannot brighten or change the color of my jpanels. I feel I a missing something crucial in how my extended jpanel class interacts with my overridden paint component as well as my other classes. The mission is simple, when one jpanel is clicked it's color should brighten, while the other two colors dim.
Run down: I extended Jframe and added 3 panels to it. I have added a mouse listener to each. When each is pressed the mouse listener does work "print statements confirmed". However it does not change the objects color. I thought that by including my mouse adapter in my TrafficLight class I would be able to change the color and call repaint. I have played with in all the ways I can think, is my organization wrong?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class TrafficLight3 extends JFrame {
//class variables
public JPanel red;
public JPanel yellow;
public JPanel green;
public Color r;
public Color y;
public Color gr;
public static void main ( String [] args ) {
TrafficLight3 tl = new TrafficLight3 ( );
}
// Constructor
public TrafficLight3( ) {
setTitle( "Traffic Light" );
setLayout ( new GridLayout ( 3, 1 ) );
setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
r = Color.RED;
red = new StopLightPanel( 100, r );
red.setPreferredSize( new Dimension ( 120, 120 ) );
red.addMouseListener ( new MouseClicked( ) );
add( red );
y = Color.YELLOW;
yellow = new StopLightPanel( 100, y );
yellow.setPreferredSize( new Dimension ( 120, 120 ) );
yellow.addMouseListener ( new MouseClicked( ) );
add( yellow );
gr = Color.GREEN;
green = new StopLightPanel( 100, gr );
green.addMouseListener ( new MouseClicked( ) );
green.setPreferredSize( new Dimension ( 120, 120 ) );
add ( green );
pack();
setLocationRelativeTo(null);
setVisible( true );
}
class MouseClicked extends MouseAdapter {
#Override
public void mouseClicked ( MouseEvent me ) {
if (me.getSource().equals( red ) ) {
r = r.brighter();
y = y.darker();
gr = gr.darker();
repaint();
}
if (me.getSource().equals( yellow )) {
r = r.darker();
y = y.brighter();
gr = gr.darker();
repaint();
}
if (me.getSource().equals( red )) {
r = r.darker();
y = y.darker();
gr = gr.brighter();
repaint();
}
}
}
class StopLightPanel extends JPanel {
private int diameter;
public Color color;
public StopLightPanel ( int d, Color c) {
diameter = d;
color = c;
}
public void paintComponent ( Graphics g ) {
super.paintComponent( g );
g.setColor ( color );
g.fillOval ( 10, 10, diameter, diameter );
}
}
}
You're changing a variable's value in one location, and expecting a completely different variable, the one owned by the JPanel, to change in concert, but that's not how variables work.
Instead, I would give my stop light panel a method, say public void setLightOn(boolean lightOn) and would call this method in my MouseListener. The method would change the color of the JPanel and repaint it.
For example:
class StopLightPanel extends JPanel {
private int diameter;
private Color onColor;
private Color offColor;
private boolean lightOn;
public boolean isLightOn() {
return lightOn;
}
public void setLightOn(boolean lightOn) {
this.lightOn = lightOn;
repaint();
}
public StopLightPanel(int d, Color c) {
diameter = d;
onColor = c.brighter();
offColor = c.darker().darker();
lightOn = false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Color color = lightOn ? onColor : offColor;
g.setColor(color);
g.fillOval(10, 10, diameter, diameter);
}
}
For example,
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
#SuppressWarnings("serial")
class TrafficLight3 extends JFrame {
private static final int DIAMETER = 100;
private static final Color[] COLORS = { Color.red, Color.yellow, Color.green };
private StopLightPanel[] lightPanels = new StopLightPanel[COLORS.length];
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TrafficLight3();
}
});
}
public TrafficLight3() {
setTitle("Traffic Light");
setLayout(new GridLayout(3, 1));
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
MouseAdapter mouseClicked = new MouseClicked();
for (int i = 0; i < COLORS.length; i++) {
lightPanels[i] = new StopLightPanel(DIAMETER, COLORS[i]);
lightPanels[i].addMouseListener(mouseClicked);
add(lightPanels[i]);
}
pack();
setLocationRelativeTo(null);
setVisible(true);
}
class MouseClicked extends MouseAdapter {
// better overriding mousePressed not mouseClicked
#Override
public void mousePressed(MouseEvent me) {
// set all lights dark
for (StopLightPanel lightPanel : lightPanels) {
lightPanel.setLightOn(false);
}
// turn only selected light on.
((StopLightPanel) me.getSource()).setLightOn(true);
}
}
class StopLightPanel extends JPanel {
// avoid "magic" numbers
private static final int GAP = 10;
private int diameter;
private Color onColor;
private Color offColor;
private boolean lightOn;
public boolean isLightOn() {
return lightOn;
}
public void setLightOn(boolean lightOn) {
this.lightOn = lightOn;
repaint();
}
public StopLightPanel(int diameter, Color color) {
this.diameter = diameter;
this.onColor = color.brighter();
this.offColor = color.darker().darker();
this.lightOn = false;
}
#Override
public Dimension getPreferredSize() {
int prefW = diameter + 2 * GAP;
int prefH = prefW;
return new Dimension(prefW, prefH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// smooth out the edge of our circle
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = lightOn ? onColor : offColor;
g2.setColor(color);
g2.fillOval(GAP, GAP, diameter, diameter);
}
}
}
Somewhat MCV-ified. Missing interfaces though
import java.awt.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumMap;
import java.util.Map;
#SuppressWarnings("serial")
class TrafficLight3 {
private static final int DIAMETER = 100;
private static void createAndShowGui() {
StopLightModel model = new StopLightModel();
StopLightView view = new StopLightView(DIAMETER);
new StopLightControl(view, model);
JFrame frame = new JFrame("Traffic Light");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(view.getMainPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum MyColor {
RED(Color.red, "Red"),
YELLOW(Color.yellow, "Yellow"),
GREEN(Color.green, "Green");
public Color getColor() {
return color;
}
public String getName() {
return name;
}
private MyColor(Color color, String name) {
this.color = color;
this.name = name;
}
private Color color;
private String name;
}
class StopLightModel {
public static final String SELECTED_COLOR = "selected color";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private MyColor selectedColor = null;
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public MyColor getSelectedColor() {
return selectedColor;
}
public void setSelectedColor(MyColor selectedColor) {
MyColor oldValue = this.selectedColor;
MyColor newValue = selectedColor;
this.selectedColor = selectedColor;
pcSupport.firePropertyChange(SELECTED_COLOR, oldValue, newValue);
}
}
class StopLightControl {
private StopLightView view;
private StopLightModel model;
public StopLightControl(final StopLightView view, final StopLightModel model) {
this.view = view;
this.model = model;
view.setStopLightControl(this);
model.addPropertyChangeListener(new ModelListener());
}
public void setSelectedColor(MyColor myColor) {
model.setSelectedColor(myColor);
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (StopLightModel.SELECTED_COLOR.equals(evt.getPropertyName())) {
MyColor myColor = model.getSelectedColor();
view.setSelectedColor(myColor);
}
}
}
}
class StopLightView {
private JPanel mainPanel = new JPanel(new GridLayout(0, 1));
private StopLightControl control;
private Map<MyColor, StopLightPanel> colorPanelMap = new EnumMap<>(MyColor.class);
public StopLightView(int diameter) {
MouseAdapter mouseClicked = new MouseClicked();
for (MyColor myColor : MyColor.values()) {
StopLightPanel lightPanel = new StopLightPanel(diameter, myColor);
lightPanel.addMouseListener(mouseClicked);
mainPanel.add(lightPanel);
colorPanelMap.put(myColor, lightPanel);
}
}
public void setSelectedColor(MyColor myColor) {
for (MyColor color : MyColor.values()) {
colorPanelMap.get(color).setLightOn(color == myColor);
}
}
public JComponent getMainPanel() {
return mainPanel;
}
public void setStopLightControl(StopLightControl control) {
this.control = control;
}
private class MouseClicked extends MouseAdapter {
#Override
public void mousePressed(MouseEvent mEvt) {
if (control == null) {
return;
}
StopLightPanel panel = (StopLightPanel) mEvt.getSource();
control.setSelectedColor(panel.getMyColor());
}
}
}
#SuppressWarnings("serial")
class StopLightPanel extends JPanel {
// avoid "magic" numbers
private static final int GAP = 10;
private int diameter;
private MyColor myColor;
private Color onColor;
private Color offColor;
private boolean lightOn;
public boolean isLightOn() {
return lightOn;
}
public void setLightOn(boolean lightOn) {
this.lightOn = lightOn;
repaint();
}
public StopLightPanel(int diameter, MyColor myColor) {
this.diameter = diameter;
this.myColor = myColor;
this.onColor = myColor.getColor().brighter();
this.offColor = myColor.getColor().darker().darker();
this.lightOn = false;
}
public MyColor getMyColor() {
return myColor;
}
#Override
public Dimension getPreferredSize() {
int prefW = diameter + 2 * GAP;
int prefH = prefW;
return new Dimension(prefW, prefH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// smooth out the edge of our circle
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = lightOn ? onColor : offColor;
g2.setColor(color);
g2.fillOval(GAP, GAP, diameter, diameter);
}
}
There is no relationship between the color you are changing in the actionListener and the color the panel is using to paint itself.
Taking a look at the JavaDocs you would note that
darker/brighter
Returns: Creates a new Color that is a darker/brighter version
of this Color.
darker/brighter doesn't change the Color object itself, but creates a new Color object which is darker/brighter than the color you are using
What you should probably do is set the color you want to each panel when the actionListener triggered.
Better yet, you should be using some kind of model, changing the model would notify each instance of the panel and it would then respond by updating itself...

How to make JProgressBar match Windows LaF?

I've been working for a while with JProgressBar using WindowsLookAndFeel, but I found some major differences from the native progress bar:
Here is the native bar, in its regular state:
While this is Java's:
A sharp eye might see Java's top white color much brighter, and the green a little brighter too. Also the native is "filled" while Java's has an empty border.
Now, the native indeterminate:
And Java's:
The flicker is much shorter, and much much faster (not shown). Also as the native could look like a glow, Java's looks much like a "comet" (with a tail, only from one side...).
And my final question, the blocked bar:
Can this be done in Java?
This is just an example for the final question:
Windows 7 (green progress bar)
JDK 1.7.0_25 (WindowsLookAndFeel)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class TurnProgressBarRedTest {
public JComponent makeUI() {
final BoundedRangeModel model = new DefaultBoundedRangeModel();
final BlockedColorLayerUI layerUI = new BlockedColorLayerUI();
final JPanel p = new JPanel(new GridLayout(4, 1, 12, 12));
p.setBorder(BorderFactory.createEmptyBorder(24,24,24,24));
final JProgressBar pb1 = new JProgressBar(model);
pb1.setStringPainted(true);
p.add(pb1);
final JProgressBar pb2 = new JProgressBar(model);
pb2.setStringPainted(true);
p.add(pb2);
p.add(new JProgressBar(model));
p.add(new JLayer<JProgressBar>(new JProgressBar(model), layerUI));
JPanel box = new JPanel();
box.add(new JButton(new AbstractAction("+10") {
private int i = 0;
#Override public void actionPerformed(ActionEvent e) {
model.setValue(i = (i>=100) ? 0 : i + 10);
}
}));
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa511486.aspx
box.add(new JCheckBox(new AbstractAction(
"<html>Turn the progress bar red<br />"+
" when there is a user recoverable condition<br />"+
" that prevents making further progress.") {
#Override public void actionPerformed(ActionEvent e) {
boolean b = ((JCheckBox)e.getSource()).isSelected();
pb2.setForeground(b? new Color(255,0,0,100) : pb1.getForeground());
layerUI.isPreventing = b;
p.repaint();
}
}));
JPanel panel = new JPanel(new BorderLayout());
panel.add(p, BorderLayout.NORTH);
panel.add(box, BorderLayout.SOUTH);
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TurnProgressBarRedTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class BlockedColorLayerUI extends LayerUI<JProgressBar> {
public boolean isPreventing = false;
private BufferedImage bi;
private int prevw = -1;
private int prevh = -1;
#Override public void paint(Graphics g, JComponent c) {
if(isPreventing) {
JLayer jlayer = (JLayer)c;
JProgressBar progress = (JProgressBar)jlayer.getView();
int w = progress.getSize().width;
int h = progress.getSize().height;
if(bi==null || w!=prevw || h!=prevh) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
prevw = w;
prevh = h;
Graphics2D g2 = bi.createGraphics();
super.paint(g2, c);
g2.dispose();
Image image = c.createImage(
new FilteredImageSource(bi.getSource(),
new RedGreenChannelSwapFilter()));
g.drawImage(image, 0, 0, null);
} else {
super.paint(g, c);
}
}
}
class RedGreenChannelSwapFilter extends RGBImageFilter {
#Override public int filterRGB(int x, int y, int argb) {
int r = (int)((argb >> 16) & 0xff);
int g = (int)((argb >> 8) & 0xff);
int b = (int)((argb ) & 0xff);
return (argb & 0xff000000) | (g<<16) | (r<<8) | (b);
}
}

Java JScrollBar Design

I want customize the JScrollBar Design. I use Mac to develop the app with eclipse. I already tried to scrollPane.getVerticalScrollBar().setBackground(Color.BLACK); but nothing happen.
My code:
scrollPane = new JScrollPane(scriptView);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
scrollPane.getVerticalScrollBar().setUnitIncrement(6);
window.getContentPane().add(scrollPane);
The Object scriptView is from the class JEditorPane.
How it should look:
Thanks for every help.
I guess you are looking for a transparent scrollbar.
This is just presented as an idea(NOT tested code):
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.basic.*;
public class TranslucentScrollBarTest {
public JComponent makeUI() {
JTextArea cmp = new JTextArea();
String str = "1234567890abcdefghijklmnopqrstuvwxyz";
for(int i=0; i<20; i++) {
cmp.append(str+str+"\n");
}
cmp.setForeground(Color.WHITE);
cmp.setBackground(Color.BLACK);
cmp.setOpaque(true);
JScrollPane scrollPane = new JScrollPane(
cmp, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setComponentZOrder(scrollPane.getVerticalScrollBar(), 0);
scrollPane.setComponentZOrder(scrollPane.getViewport(), 1);
scrollPane.getVerticalScrollBar().setOpaque(false);
scrollPane.setLayout(new ScrollPaneLayout() {
#Override
public void layoutContainer(Container parent) {
JScrollPane scrollPane = (JScrollPane)parent;
Rectangle availR = scrollPane.getBounds();
availR.x = availR.y = 0;
Insets insets = parent.getInsets();
availR.x = insets.left;
availR.y = insets.top;
availR.width -= insets.left + insets.right;
availR.height -= insets.top + insets.bottom;
Rectangle vsbR = new Rectangle();
vsbR.width = 12;
vsbR.height = availR.height;
vsbR.x = availR.x + availR.width - vsbR.width;
vsbR.y = availR.y;
if(viewport != null) {
viewport.setBounds(availR);
}
if(vsb != null) {
vsb.setVisible(true);
vsb.setBounds(vsbR);
}
}
});
scrollPane.getVerticalScrollBar().setUI(new BasicScrollBarUI() {
private final Dimension d = new Dimension();
#Override protected JButton createDecreaseButton(int orientation) {
return new JButton() {
#Override public Dimension getPreferredSize() {
return d;
}
};
}
#Override protected JButton createIncreaseButton(int orientation) {
return new JButton() {
#Override public Dimension getPreferredSize() {
return d;
}
};
}
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle r) {}
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
Graphics2D g2 = (Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = null;
JScrollBar sb = (JScrollBar)c;
if(!sb.isEnabled() || r.width>r.height) {
return;
}else if(isDragging) {
color = new Color(200,200,100,200);
}else if(isThumbRollover()) {
color = new Color(255,255,100,200);
}else {
color = new Color(220,220,200,200);
}
g2.setPaint(color);
g2.fillRoundRect(r.x,r.y,r.width,r.height,10,10);
g2.setPaint(Color.WHITE);
g2.drawRoundRect(r.x,r.y,r.width,r.height,10,10);
g2.dispose();
}
#Override
protected void setThumbBounds(int x, int y, int width, int height) {
super.setThumbBounds(x, y, width, height);
scrollbar.repaint();
}
});
return scrollPane;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TranslucentScrollBarTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Here is an improved version I did for a private project. It also supports horizontal scrollbar.
Code:
import javax.swing.*;
import javax.swing.plaf.basic.BasicScrollBarUI;
import java.awt.*;
/**
* This is an implementation of a JScrollPane with a modern UI
*
* #author Philipp Danner
*
*/
public class ModernScrollPane extends JScrollPane {
private static final long serialVersionUID = 8607734981506765935L;
private static final int SCROLL_BAR_ALPHA_ROLLOVER = 100;
private static final int SCROLL_BAR_ALPHA = 50;
private static final int THUMB_SIZE = 8;
private static final int SB_SIZE = 10;
private static final Color THUMB_COLOR = Color.Black;
public ModernScrollPane(Component view) {
this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
public ModernScrollPane(int vsbPolicy, int hsbPolicy) {
this(null, vsbPolicy, hsbPolicy);
}
public ModernScrollPane(Component view, int vsbPolicy, int hsbPolicy) {
setBorder(null);
// Set ScrollBar UI
JScrollBar verticalScrollBar = getVerticalScrollBar();
verticalScrollBar.setOpaque(false);
verticalScrollBar.setUI(new ModernScrollBarUI(this));
JScrollBar horizontalScrollBar = getHorizontalScrollBar();
horizontalScrollBar.setOpaque(false);
horizontalScrollBar.setUI(new ModernScrollBarUI(this));
setLayout(new ScrollPaneLayout() {
private static final long serialVersionUID = 5740408979909014146L;
#Override
public void layoutContainer(Container parent) {
Rectangle availR = ((JScrollPane) parent).getBounds();
availR.x = availR.y = 0;
// viewport
Insets insets = parent.getInsets();
availR.x = insets.left;
availR.y = insets.top;
availR.width -= insets.left + insets.right;
availR.height -= insets.top + insets.bottom;
if (viewport != null) {
viewport.setBounds(availR);
}
boolean vsbNeeded = isVerticalScrollBarfNecessary();
boolean hsbNeeded = isHorizontalScrollBarNecessary();
// vertical scroll bar
Rectangle vsbR = new Rectangle();
vsbR.width = SB_SIZE;
vsbR.height = availR.height - (hsbNeeded ? vsbR.width : 0);
vsbR.x = availR.x + availR.width - vsbR.width;
vsbR.y = availR.y;
if (vsb != null) {
vsb.setBounds(vsbR);
}
// horizontal scroll bar
Rectangle hsbR = new Rectangle();
hsbR.height = SB_SIZE;
hsbR.width = availR.width - (vsbNeeded ? hsbR.height : 0);
hsbR.x = availR.x;
hsbR.y = availR.y + availR.height - hsbR.height;
if (hsb != null) {
hsb.setBounds(hsbR);
}
}
});
// Layering
setComponentZOrder(getVerticalScrollBar(), 0);
setComponentZOrder(getHorizontalScrollBar(), 1);
setComponentZOrder(getViewport(), 2);
viewport.setView(view);
}
private boolean isVerticalScrollBarfNecessary() {
Rectangle viewRect = viewport.getViewRect();
Dimension viewSize = viewport.getViewSize();
return viewSize.getHeight() > viewRect.getHeight();
}
private boolean isHorizontalScrollBarNecessary() {
Rectangle viewRect = viewport.getViewRect();
Dimension viewSize = viewport.getViewSize();
return viewSize.getWidth() > viewRect.getWidth();
}
/**
* Class extending the BasicScrollBarUI and overrides all necessary methods
*/
private static class ModernScrollBarUI extends BasicScrollBarUI {
private JScrollPane sp;
public ModernScrollBarUI(ModernScrollPane sp) {
this.sp = sp;
}
#Override
protected JButton createDecreaseButton(int orientation) {
return new InvisibleScrollBarButton();
}
#Override
protected JButton createIncreaseButton(int orientation) {
return new InvisibleScrollBarButton();
}
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
}
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
int alpha = isThumbRollover() ? SCROLL_BAR_ALPHA_ROLLOVER : SCROLL_BAR_ALPHA;
int orientation = scrollbar.getOrientation();
int x = thumbBounds.x;
int y = thumbBounds.y;
int width = orientation == JScrollBar.VERTICAL ? THUMB_SIZE : thumbBounds.width;
width = Math.max(width, THUMB_SIZE);
int height = orientation == JScrollBar.VERTICAL ? thumbBounds.height : THUMB_SIZE;
height = Math.max(height, THUMB_SIZE);
Graphics2D graphics2D = (Graphics2D) g.create();
graphics2D.setColor(new Color(THUMB_COLOR.getRed(), THUMB_COLOR.getGreen(), THUMB_COLOR.getBlue(), alpha));
graphics2D.fillRect(x, y, width, height);
graphics2D.dispose();
}
#Override
protected void setThumbBounds(int x, int y, int width, int height) {
super.setThumbBounds(x, y, width, height);
sp.repaint();
}
/**
* Invisible Buttons, to hide scroll bar buttons
*/
private static class InvisibleScrollBarButton extends JButton {
private static final long serialVersionUID = 1552427919226628689L;
private InvisibleScrollBarButton() {
setOpaque(false);
setFocusable(false);
setFocusPainted(false);
setBorderPainted(false);
setBorder(BorderFactory.createEmptyBorder());
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(400, 400));
JPanel content = new JPanel();
content.setBackground(Color.WHITE);
content.setPreferredSize(new Dimension(500, 500));
content.add(new JLabel("test"));
frame.add(new ModernScrollPane(content));
frame.pack();
frame.setVisible(true);
}
}
Custom scrollbar preview :
Custom scrollbar code :
public class CustomScrollBarUI extends BasicScrollBarUI {
private final Dimension d = new Dimension();
#Override
protected JButton createDecreaseButton(int orientation) {
return new JButton() {
private static final long serialVersionUID = -3592643796245558676L;
#Override
public Dimension getPreferredSize() {
return d;
}
};
}
#Override
protected JButton createIncreaseButton(int orientation) {
return new JButton() {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return d;
}
};
}
#Override
protected void paintTrack(Graphics g, JComponent c, Rectangle r) {
}
#Override
protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color color = null;
JScrollBar sb = (JScrollBar) c;
if (!sb.isEnabled() || r.width > r.height) {
return;
} else if (isDragging) {
color = Color.DARK_GRAY; // change color
} else if (isThumbRollover()) {
color = Color.LIGHT_GRAY; // change color
} else {
color = Color.GRAY; // change color
}
g2.setPaint(color);
g2.fillRoundRect(r.x, r.y, r.width, r.height, 10, 10);
g2.setPaint(Color.WHITE);
g2.drawRoundRect(r.x, r.y, r.width, r.height, 10, 10);
g2.dispose();
}
#Override
protected void setThumbBounds(int x, int y, int width, int height) {
super.setThumbBounds(x, y, width, height);
scrollbar.repaint();
}
}
Then use it like this:
YOUR_COMPONENT.getVerticalScrollBar().setUI(new CustomScrollBarUI());
Sadly the proposed solutions will break JTable and won't display the table header, but I found this solution that seems to work.

How to add Window's Aero effects to a JPanel?

I'm trying to have Aero Glass look in my JPanel. Is it possible do such a thing?
How to add Aero effect to JFrame - like this picture?
please read tutorials How to Create Translucent, How to Create Translucent and Shaped Windows, then by using javax.swing.Timer is possible (for example)
import java.awt.event.*;
import java.awt.Color;
import java.awt.AlphaComposite;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class ButtonTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonTest().createAndShowGUI();
}
});
}
private JFrame frame;
private JButton opaqueButton1;
private JButton opaqueButton2;
private SoftJButton softButton1;
private SoftJButton softButton2;
public void createAndShowGUI() {
opaqueButton1 = new JButton("Opaque Button");
opaqueButton2 = new JButton("Opaque Button");
softButton1 = new SoftJButton("Transparent Button");
softButton2 = new SoftJButton("Transparent Button");
opaqueButton1.setBackground(Color.GREEN);
softButton1.setBackground(Color.GREEN);
frame = new JFrame();
frame.getContentPane().setLayout(new java.awt.GridLayout(2, 2, 10, 10));
frame.add(opaqueButton1);
frame.add(softButton1);
frame.add(opaqueButton2);
frame.add(softButton2);
frame.setSize(567, 350);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Timer alphaChanger = new Timer(30, new ActionListener() {
private float incrementer = -.03f;
#Override
public void actionPerformed(ActionEvent e) {
float newAlpha = softButton1.getAlpha() + incrementer;
if (newAlpha < 0) {
newAlpha = 0;
incrementer = -incrementer;
} else if (newAlpha > 1f) {
newAlpha = 1f;
incrementer = -incrementer;
}
softButton1.setAlpha(newAlpha);
softButton2.setAlpha(newAlpha);
}
});
alphaChanger.start();
Timer uiChanger = new Timer(3500, new ActionListener() {
private LookAndFeelInfo[] laf = UIManager.getInstalledLookAndFeels();
private int index = 1;
#Override
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(laf[index].getClassName());
SwingUtilities.updateComponentTreeUI(frame);
} catch (Exception exc) {
exc.printStackTrace();
}
index = (index + 1) % laf.length;
}
});
uiChanger.start();
}
public static class SoftJButton extends JButton {
private static final JButton lafDeterminer = new JButton();
private static final long serialVersionUID = 1L;
private boolean rectangularLAF;
private float alpha = 1f;
public SoftJButton() {
this(null, null);
}
public SoftJButton(String text) {
this(text, null);
}
public SoftJButton(String text, Icon icon) {
super(text, icon);
setOpaque(false);
setFocusPainted(false);
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
repaint();
}
#Override
public void paintComponent(java.awt.Graphics g) {
java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
if (rectangularLAF && isBackgroundSet()) {
Color c = getBackground();
g2.setColor(c);
g.fillRect(0, 0, getWidth(), getHeight());
}
super.paintComponent(g2);
}
#Override
public void updateUI() {
super.updateUI();
lafDeterminer.updateUI();
rectangularLAF = lafDeterminer.isOpaque();
}
}
}
Maybe this blog post will help you more. The author describes an approach where he rebuilds the whole Windows Aero effect. Here is his working example:
(source: centigrade.de)

Categories