Java JScrollBar Design - java

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.

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).

java- repaint() method is misbehaving?

I am working on a Music Player
I am using a JSlider as seek bar and using a JLabel to draw text on screen, such as song name.
I am new to Graphics2D
Here's the minimized code:
public class JSliderDemo extends JFrame
{
JLabel label;
JSlider seek = new JSlider();
int y = 10;
public JSliderDemo()
{
setSize(400, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
createWindow();
setVisible(true);
startThread();
}
public void createWindow()
{
JPanel panel = new JPanel(null);
panel.setOpaque(true);
panel.setBackground(Color.BLUE);
label = new Component();
label.setSize(400, 400);
label.setLocation(0, 0);
createSlider();
panel.add(seek);
panel.add(label);
add(panel);
}
protected void createSlider()
{
seek.setUI(new SeekBar(seek, 300, 10, new Dimension(20, 20), 5,
Color.DARK_GRAY, Color.RED, Color.RED));
seek.setOrientation(JProgressBar.HORIZONTAL);
seek.setOpaque(false);
seek.setLocation(10, 50);
seek.setSize(300, 20);
seek.setMajorTickSpacing(0);
seek.setMinorTickSpacing(0);
seek.setMinimum(0);
seek.setMaximum(1000);
}
protected void startThread()
{
Thread thread = new Thread(new Runnable(){
#Override
public void run()
{
try
{
while(true)
{
if(y == label.getHeight()){y = 1;}
label.repaint();
y += 1;
Thread.sleep(100);
}
}
catch(Exception ex){}
}
});
thread.start();
}
protected class Component extends JLabel
{
#Override
public void paintComponent(Graphics g)
{
Graphics2D gr = (Graphics2D) g;
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setColor(Color.RED);
gr.setFont(new Font("Calibri", Font.PLAIN, 16));
gr.drawString("Slider", 50, y);
}
}
public static void main(String[] args)
{
new JSliderDemo();
}
}
The Custom Slider UI class prints a line each time the JSlider is repainted.
Through this, I was able to find out that, when I call repaint() for JLabel it automatically repaints JSlider with it even though JSlider is not included in JLabel.
Output :
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted.........
Now if I remove label.repaint() from the Thread, then the JSlider is not re-painted.
Output:
Slider re-painted
Slider re-painted
Is the repaint() method supposed to work like this?
Here's the Custom Slider UI class :
package jsliderdemo;
import java.awt.*;
import javax.swing.*;
import java.awt.geom.RoundRectangle2D;
import javax.swing.plaf.basic.BasicSliderUI;
public class SeekBar extends BasicSliderUI
{
private int TRACK_ARC = 5;
private int TRACK_WIDTH = 8;
private int TRACK_HEIGHT = 8;
private Color backGround = Color.GRAY;
private Color trackColor = Color.RED;
private Color thumbColor = Color.WHITE;
private Dimension THUMB_SIZE = new Dimension(20, 20);
private final RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float();
public SeekBar(final JSlider b, int width, int height, Dimension thumbSize, int arc,
Color backGround, Color trackColor, Color thumbColor)
{
super(b);
this.TRACK_ARC = arc;
this.TRACK_WIDTH = width;
this.TRACK_HEIGHT = height;
this.THUMB_SIZE = thumbSize;
this.backGround = backGround;
this.trackColor = trackColor;
this.thumbColor = thumbColor;
}
#Override
protected void calculateTrackRect()
{
super.calculateTrackRect();
if (isHorizontal())
{
trackRect.y = trackRect.y + (trackRect.height - TRACK_HEIGHT) / 2;
trackRect.height = TRACK_HEIGHT;
}
else
{
trackRect.x = trackRect.x + (trackRect.width - TRACK_WIDTH) / 2;
trackRect.width = TRACK_WIDTH;
}
trackShape.setRoundRect(trackRect.x, trackRect.y, trackRect.width, trackRect.height, TRACK_ARC, TRACK_ARC);
}
#Override
protected void calculateThumbLocation()
{
super.calculateThumbLocation();
if (isHorizontal())
{
thumbRect.y = trackRect.y + (trackRect.height - thumbRect.height) / 2;
}
else
{
thumbRect.x = trackRect.x + (trackRect.width - thumbRect.width) / 2;
}
}
#Override
protected Dimension getThumbSize()
{
return THUMB_SIZE;
}
private boolean isHorizontal()
{
return slider.getOrientation() == JSlider.HORIZONTAL;
}
#Override
public void paint(final Graphics g, final JComponent c)
{
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g, c);
}
#Override
public void paintTrack(final Graphics g)
{
System.out.println("Slider re-painted");
Graphics2D g2 = (Graphics2D) g;
Shape clip = g2.getClip();
boolean horizontal = isHorizontal();
boolean inverted = slider.getInverted();
// Paint shadow.
//g2.setColor(new Color(170, 170 ,170));
//g2.fill(trackShape);
// Paint track background.
g2.setColor(backGround);
g2.setClip(trackShape);
trackShape.y += 1;
g2.fill(trackShape);
trackShape.y = trackRect.y;
g2.setClip(clip);
// Paint selected track.
if (horizontal)
{
boolean ltr = slider.getComponentOrientation().isLeftToRight();
if (ltr) inverted = !inverted;
int thumbPos = thumbRect.x + thumbRect.width / 2;
if (inverted)
{
g2.clipRect(0, 0, thumbPos, slider.getHeight());
}
else
{
g2.clipRect(thumbPos, 0, slider.getWidth() - thumbPos, slider.getHeight());
}
}
else
{
int thumbPos = thumbRect.y + thumbRect.height / 2;
if (inverted)
{
g2.clipRect(0, 0, slider.getHeight(), thumbPos);
}
else
{
g2.clipRect(0, thumbPos, slider.getWidth(), slider.getHeight() - thumbPos);
}
}
g2.setColor(trackColor);
g2.fill(trackShape);
g2.setClip(clip);
}
#Override
public void paintThumb(final Graphics g)
{
g.setColor(thumbColor);
g.fillOval(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height);
}
#Override
public void paintFocus(final Graphics g) {}
}

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.

Creating custom JComponent with JLayer over JProgressBar

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);
}
}
}

Scrolling programmatically

I would like the cell of my JTable to be aligned horizontally with the selected panels.Here is the a SSCCE to illustrate my problem. Thanks for any help.
public class TableCellAlignment {
private final static int MAX = 50;
private static SelectablePanel[] selectablePanels = new SelectablePanel[MAX];
private static JScrollPane slaveScrollPane = new JScrollPane();
private static JScrollPane masterScrollPane = new JScrollPane();
private static JTable slaveTable = new JTable();
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new TableCellAlignment().createGUI();
}
});
}
private static void createGUI() {
JFrame f = new JFrame("TableCellAlignment");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel masterPanel = new JPanel(new GridLayout(MAX, 1));
Integer[][] objs = new Integer[MAX][1];
for (int i = 0; i < MAX; i++) {
objs[i][0] = new Integer(i);
SelectablePanel masterSelectablePanel = new SelectablePanel();
masterSelectablePanel.setNum(i);
selectablePanels[i] = masterSelectablePanel;
masterPanel.add(masterSelectablePanel);
}
DefaultTableModel model = new DefaultTableModel(objs, new Object[]{"Column1"});
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
slaveTable.setRowHeight(20);
}
});
}
});
model.addRow(objs);
slaveTable.setModel(model);
final JPanel p = new JPanel(new GridLayout(1, 2));
masterScrollPane.setViewportView(masterPanel);
slaveScrollPane.setViewportView(slaveTable);
p.add(masterScrollPane);
p.add(slaveScrollPane);
f.add(p);
f.setSize(400, 200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class SelectablePanel extends JPanel {
private PropertyChangeSupport cs;
private int num;
private boolean selected = false;
public SelectablePanel() {
cs = new PropertyChangeSupport(this);
cs.addPropertyChangeListener(new SelectedPropertyChangeListener());
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
setSelected(true);
}
});
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public boolean isSelected() {
return selected;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (selected) {
Color c = g.getColor();
g.setColor(Color.blue);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
FontMetrics fm = g.getFontMetrics();
g.drawString("" + getNum(), getWidth() / 2, (getHeight() + (fm.getAscent() - fm.getDescent())) / 2);
g.setColor(c);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 20);
}
public void setSelected(boolean selected) {
boolean oldVal = isSelected();
this.selected = selected;
cs.firePropertyChange("selected", oldVal, selected);
repaint();
}
private class SelectedPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("selected")) {
boolean selected = (boolean) evt.getNewValue();
if (selected) {
for (int i = 0; i < MAX; i++) {
SelectablePanel masterSelectablePanel = selectablePanels[i];
if (i != getNum() && masterSelectablePanel.isSelected()) {
masterSelectablePanel.setSelected(false);
}
}
slaveTable.setRowSelectionInterval(getNum(), getNum());
final JViewport viewport = slaveScrollPane.getViewport();
Rectangle rect = new Rectangle(getBounds().x, getBounds().y, 1, 1);
Rectangle r2 = viewport.getVisibleRect();
slaveTable.scrollRectToVisible(new Rectangle(rect.x, rect.y, (int)r2.getWidth(), (int)r2.getHeight()));
}
}
}
}
}
}
viewport.setViewPosition( pt ); as shown here.
It's basic math and it does not require access to the viewport:
// in the isSelected block of the propertyChangeListener:
JComponent current = (JComponent) evt.getSource();
slaveTable.setRowSelectionInterval(getNum(), getNum());
// get the cellRect of the selected cell
Rectangle cellRect = slaveTable.getCellRect(getNum(), 0, false);
// get the bounds of the selected panel
Rectangle panelRect = current.getBounds();
// get the visible rect of the selected panel's parent
Rectangle parentVisibleRect = ((JComponent) current.getParent()).getVisibleRect();
// the diff above the current (to the parent's visible rect)
int aboveCurrent = panelRect.y - parentVisibleRect.y;
// translate the cell rect
cellRect.y = Math.max(cellRect.y - aboveCurrent, 0);
// adjust size to slaveTable's visible height
cellRect.height = slaveTable.getVisibleRect().height;
slaveTable.scrollRectToVisible(cellRect);
Note that this snippet assumes that the view's viewport of both the panel's parent and the table have the same size, so either remove the header from the table, or add a header to the panel's scrollPane, or use a LayoutManager which can align the viewports of the two scrollPanes.

Categories