How to change default style of Java Swing application in NetBeans? - java

There are few styles in the preview mode but can't find where I can set the preferred design. Is there any option?
Check screen

Examples:
Programmatically:
UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
Command line:
java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel MyApp
You can find more info about this on the Oracle docs.
A complete demo (from the docs):
public class LookAndFeelDemo implements ActionListener {
private static String labelPrefix = "Number of button clicks: ";
private int numClicks = 0;
final JLabel label = new JLabel(labelPrefix + "0 ");
// Specify the look and feel to use by defining the LOOKANDFEEL constant
// Valid values are: null (use the default), "Metal", "System", "Motif",
// and "GTK"
final static String LOOKANDFEEL = "Metal";
// If you choose the Metal L&F, you can also choose a theme.
// Specify the theme to use by defining the THEME constant
// Valid values are: "DefaultMetal", "Ocean", and "Test"
final static String THEME = "Test";
public Component createComponents() {
JButton button = new JButton("I'm a Swing button!");
button.setMnemonic(KeyEvent.VK_I);
button.addActionListener(this);
label.setLabelFor(button);
JPanel pane = new JPanel(new GridLayout(0, 1));
pane.add(button);
pane.add(label);
pane.setBorder(BorderFactory.createEmptyBorder(
30, //top
30, //left
10, //bottom
30) //right
);
return pane;
}
public void actionPerformed(ActionEvent e) {
numClicks++;
label.setText(labelPrefix + numClicks);
}
private static void initLookAndFeel() {
String lookAndFeel = null;
if (LOOKANDFEEL != null) {
if (LOOKANDFEEL.equals("Metal")) {
lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
// an alternative way to set the Metal L&F is to replace the
// previous line with:
// lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel";
}
else if (LOOKANDFEEL.equals("System")) {
lookAndFeel = UIManager.getSystemLookAndFeelClassName();
}
else if (LOOKANDFEEL.equals("Motif")) {
lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
}
else if (LOOKANDFEEL.equals("GTK")) {
lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
}
else {
System.err.println("Unexpected value of LOOKANDFEEL specified: "
+ LOOKANDFEEL);
lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
}
try {
UIManager.setLookAndFeel(lookAndFeel);
// If L&F = "Metal", set the theme
if (LOOKANDFEEL.equals("Metal")) {
if (THEME.equals("DefaultMetal"))
MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
else if (THEME.equals("Ocean"))
MetalLookAndFeel.setCurrentTheme(new OceanTheme());
else
MetalLookAndFeel.setCurrentTheme(new TestTheme());
UIManager.setLookAndFeel(new MetalLookAndFeel());
}
}
catch (ClassNotFoundException e) {
System.err.println("Couldn't find class for specified look and feel:"
+ lookAndFeel);
System.err.println("Did you include the library in the class path?");
System.err.println("Using the default look and feel.");
}
catch (UnsupportedLookAndFeelException e) {
System.err.println("Can't use the specified look and feel ("
+ lookAndFeel
+ ") on this platform.");
System.err.println("Using the default look and feel.");
}
catch (Exception e) {
System.err.println("Couldn't get specified look and feel ("
+ lookAndFeel
+ "), for some reason.");
System.err.println("Using the default look and feel.");
e.printStackTrace();
}
}
}
private static void createAndShowGUI() {
//Set the look and feel.
initLookAndFeel();
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
JFrame frame = new JFrame("SwingApplication");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
LookAndFeelDemo app = new LookAndFeelDemo();
Component contents = app.createComponents();
frame.getContentPane().add(contents, BorderLayout.CENTER);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

Related

An UI that slides through images

I am trying to change what image is shown in the JLabels of my User Interfaces.
The following class is a very simple test of the concept. The UI takes a folder full of images (the field imageFolderPath) and displays the first image, resized, in the only JLabel; clicking on the image prompts the UI to display the following image in the folder.
At least, it should. In reality, no image is shown. The fault is obviously of the method reloadImage(), either while rescaling the image or while repainting the JLabel, but I have not managed to find or correct the problem. Any idea?
public class Test extends JFrame {
private JPanel contentPane;
private JLabel boardImage;
private ImageIcon icon;
public String imageFolderPath = "C:\\modify\\it\\to\\suit\\your\\needs\\";
public File[] files;
public int indexImage = 0;
public int imageResolution = 450;
// =========================================================
// TODO | Constructors
/**
* Create the frame.
*/
public Test() {
if( !new File(imageFolderPath).exists() )
new File(imageFolderPath).mkdir();
this.files = new File(imageFolderPath).listFiles();
System.out.println("Can show:");
for( File file : files )
System.out.println("\t"+file.getName());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, imageResolution, imageResolution);
contentPane = new JPanel();
contentPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
// Every time the image is clicked, another one is shown
indexImage++;
reloadImage();
}
});
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
boardImage = new JLabel();
boardImage.setBounds(0, 0, imageResolution, imageResolution);
reloadImage();
contentPane.add(boardImage);
}
// =========================================================
// TODO | Support methods
/** Reloads the image of the {#link ChessBoard} at its current state. */
public void reloadImage() {
if( files[indexImage % files.length].exists() )
System.out.println("The file " + files[indexImage % files.length].getName() + " exists");
else System.out.println("The file " + files[indexImage % files.length].getName() + " doesnt exists");
// Reload the image - resized
BufferedImage buffer = null;
try {
buffer = ImageIO.read( files[indexImage % files.length] );
} catch (IOException e) { e.printStackTrace(); }
Image rescaledImage = buffer.getScaledInstance(imageResolution, imageResolution, Image.SCALE_SMOOTH);
icon = new ImageIcon(rescaledImage);
boardImage = new JLabel(icon);
// boardImage.setText( files[indexImage % files.length].getName() );
System.out.println("Is now showing " + files[indexImage % files.length].getName() );
boardImage.repaint();
}
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Test frame = new Test();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
In reality, no image is shown.
boardImage = new JLabel(icon);
Don't create a new JLabel. The instance of that JLabel hasn't been added to the frame.
Instead just change the Icon of the existing JLabel:
//boardImage = new JLabel(icon);
boardImage.setIcon( icon );
The label will automatically repaint itself with the new Icon.

How to update look and feel clean seperate in a class?

I want to update my look and feel by JRadioButtonMenuItem. And I searching in Stackoverflow but what I find was a big bunch of code in 1 class. For me as a beginner its easier to seperate function in a special class.
That is my Frame-Class.
public class CalenderFrame extends JFrame {
public CalenderFrame() throws HeadlessException {
createFrame();
}
public void createFrame() {
setJMenuBar(CalenderMenuBar.getInstance().createMenu());
setTitle("Calender");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(400, 300));
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
And that is my MenueBar Class. I just give a short one of Code that is specific for this question. This class is an Singleton.
public JMenuBar createMenu() {
JMenu lookAndFeelMenu = new JMenu("Look & Feel");
JRadioButtonMenuItem lAndFWindowsItem = new JRadioButtonMenuItem("Windows",true);
lAndFWindowsItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == lAndFWindowsItem) {
lAndFAction(1);
}
}
});
JRadioButtonMenuItem lAndFMetalItem = new JRadioButtonMenuItem("Metal",false);
lAndFMetalItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == lAndFMetalItem) {
lAndFAction(2);
}
}
});
JRadioButtonMenuItem lAndFMotifItem = new JRadioButtonMenuItem("Motif", false);
lAndFMotifItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == lAndFMotifItem) {
lAndFAction(3);
}
}
});
ButtonGroup group = new ButtonGroup();
group.add(lAndFWindowsItem);
group.add(lAndFMetalItem);
group.add(lAndFMotifItem);
lookAndFeelMenu.add(lAndFWindowsItem);
lookAndFeelMenu.add(lAndFMetalItem);
lookAndFeelMenu.add(lAndFMotifItem);
}
public void lAndFAction(int counter) {
try {
String plaf = "";
if (counter == 1) {
plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
} else if (counter == 2) {
plaf = "javax.swing.plaf.metal.MetalLookAndFeel";
} else if (counter == 3) {
plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
}
UIManager.setLookAndFeel(plaf);
//SwingUtilities.updateComponentTreeUI(this);
} catch (UnsupportedLookAndFeelException ue) {
System.err.println(ue.toString());
} catch (ClassNotFoundException ce) {
System.err.println(ce.toString());
} catch (InstantiationException ie) {
System.err.println(ie.toString());
} catch (IllegalAccessException iae) {
System.err.println(iae.toString());
}
}
}
I hope you guys can help me.
I'm not sure what your problem actually is. But, you must update your components after changing the LaF. According to the Look and Feel Documentation:
Changing the Look and Feel After Startup
You can change the L&F with setLookAndFeel even after the program's
GUI is visible. To make existing components reflect the new L&F,
invoke the SwingUtilities updateComponentTreeUI method once per
top-level container. Then you might wish to resize each top-level
container to reflect the new sizes of its contained components. For
example:
UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();
Therefore, you would need a reference to the frame holding the components in your UI. An idea would be doing something like:
public class CalendarMenuBar {
// Add this field to tour factory
private static JFrame frameThatWillBeUpdated;
// ... (Your code goes here)
// update this method to receive the reference of the frame which will
// need to be refreshed (update the GUI)
public JMenuBar createMenu(JFrame frame) {
// sets the reference for the frame
frameThatWillBeUpdated = frame;
// ... (the rest of your code for this method)
}
// ...
// Update this method to refresh the frame
public void lAndFAction(int counter) {
try{
// ... (your code)
// Set the LaF
UIManager.setLookAndFeel(plaf);
// Update the component tree (frame and its children)
SwingUtilities.updateComponentTreeUI(frameThatWillBeUpdated);
// repack to resize
frame.pack();
} catch(Exception ex){
// Your catches
}
}
}
And here is how you use it when creating your frame (inside your CalenderFrame class):
public void createFrame() {
// use this frame as reference
setJMenuBar(CalenderMenuBar.getInstance().createMenu(this));
// ... (your code goes here)
}

how to prevent jscrollpane from scrolling to the bottom?

I have JTextPane (log) inside JScrollPane (logScrollPane) element. Log's content is set to "text/html".
I created a method that appends this log which looks like this:
public void appendLog(String someHTMLText)
{
HTMLDocument doc = (HTMLDocument) log.getDocument();
HTMLEditorKit editorKit = (HTMLEditorKit) log.getEditorKit();
try
{
editorKit.insertHTML(doc, doc.getLength(), someHTMLText, 0, 0, null);
}
catch (BadLocationException | IOException ex)
{
// handle exceptions
}
}
I want to improve this method and force logScrollPane's VerticalScrollBar to move_to_the_bottom/stay_at_it's_position depending on additional boolean argument.
Final method should look like this:
public void appendLog(String someHTMLText, boolean scroll)
{
if(scroll)
{
/*
* append log and set VerticalScrollBar to the bottom by
* log.setCaretPosition(log.getDocument().getLength());
*/
}
else
{
// append log BUT make VerticalScrollBar stay at it's previous position
}
}
Any suggestions? :)
Here is something similar to what I did when I wanted to achieve this. The key is to alter the behavior of scrollRectToVisible.
public class Docker extends JFrame {
boolean dockScrollbar = true;
MYTextPane textPane = new MYTextPane();
JScrollPane sp = new JScrollPane(textPane);
Docker() {
JCheckBox scrollbarDockCB = new JCheckBox("Dock scrollbar");
scrollbarDockCB.addItemListener(new DockScrollbarListener());
scrollbarDockCB.setSelected(true);
JButton insertText = new JButton("Insert text");
insertText.addActionListener(new TextInserter());
getContentPane().add(insertText, BorderLayout.PAGE_START);
getContentPane().add(sp);
getContentPane().add(scrollbarDockCB, BorderLayout.PAGE_END);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
class MYTextPane extends JTextPane {
MYTextPane() {
setEditorKit(new HTMLEditorKit());
}
#Override
public void scrollRectToVisible(Rectangle aRect) {
if (dockScrollbar)
super.scrollRectToVisible(aRect);
}
void insertText(String msg) {
HTMLEditorKit kit = (HTMLEditorKit) getEditorKit();
HTMLDocument doc = (HTMLDocument) getDocument();
try {
kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null);
} catch (BadLocationException | IOException e1) {
e1.printStackTrace();
}
setCaretPosition(doc.getLength());
}
}
class TextInserter implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textPane.insertText("AAA\n");
}
}
class DockScrollbarListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
dockScrollbar = true;
JScrollBar sb = sp.getVerticalScrollBar();
sb.setValue(sb.getMaximum());
}
else if (e.getStateChange() == ItemEvent.DESELECTED)
dockScrollbar = false;
}
}
public static void main(String[] args) {
new Docker();
}
}
Notes:
I made the docking set to false when you manually scroll in my code, you can add it here too. by adding a mouse listener to the vertical scrollbar.
I have boolean dockScrollbar as a field of the text pane because I have more than one.
I don't have a field for the JScrollPane, I get it through the text pane.
I've never tried it on a JEditorPane but you should be able to use the caret update policy to control this.
Check out Text Area Scrolling for more information.

Controlling LookAndFeel of Java Swing [duplicate]

I'm writing a script for a larger GUI application. The main application window uses the system's LookAndFeel, but I want my script's GUI to use the Nimbus LookAndFeel. After GUI creation, I want to set the LookAndFeel back to the original. I feel the below SSCCE should work, but I'm getting a NullPointerException when using my Component objects.
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class GUI extends JFrame {
private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel();
static {
System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName());
try {
setNimbusLookAndFeel();
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName()
+ " before component creation");
}
private GridBagLayout gridBag = new GridBagLayout();
private JTabbedPane tabs = new JTabbedPane();
private JPanel selectionPanel = new JPanel(gridBag);
private JPanel infoPanel = new JPanel(gridBag);
private JPanel settingsPanel = new JPanel(gridBag);
public GUI() {
setWindowProperties();
setUpComponents();
addComponents();
try {
System.out.println("Setting to original, which is " + originalLookAndFeel.getName());
UIManager.setLookAndFeel(originalLookAndFeel);
System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName());
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
private void setWindowProperties() {
setLayout(gridBag);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(700, 600));
setTitle("fAmos Quester");
setResizable(false);
setLocationRelativeTo(null);
}
private static void setNimbusLookAndFeel() {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
}
}
} catch (Exception e) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e2) {
}
}
}
public void setUpComponents() {
tabs.addTab("Quest selection", selectionPanel);
tabs.addTab("Quest info", infoPanel);
tabs.addTab("Settings", settingsPanel);
selectionPanel.setPreferredSize(new Dimension(650, 500));
infoPanel.setPreferredSize(new Dimension(650, 500));
settingsPanel.setPreferredSize(new Dimension(650, 500));
}
private void addComponents() {
add(tabs);
}
public static void main(String[] args) {
new GUI().setVisible(true);
}
}
As a general rule it is not a good idea to mix LAF's. This problem is an example of why.
There is something in the Nimbus LAF that may not allow you to do this. Run the code as is. It will set the LAF to the System LAF and then reset the LAF. In my case the system is Windows and it appear to work fine. Then change the code to use the Nimbus LAF. It appears to work initially, but try resizing the frame and you get the errors. So it would appear that the Nimbus frame does not work completely independent of the current LAF.
import java.awt.*;
import java.awt.event.*;
import java.awt.GridBagLayout;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class GUI2 extends JFrame {
private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel();
/*
private GridBagLayout gridBag = new GridBagLayout();
private JTabbedPane tabs = new JTabbedPane();
private JPanel selectionPanel = new JPanel(gridBag);
private JPanel infoPanel = new JPanel(gridBag);
private JPanel settingsPanel = new JPanel(gridBag);
*/
private GridBagLayout gridBag;
private JTabbedPane tabs;
private JPanel selectionPanel;
private JPanel infoPanel;
private JPanel settingsPanel;
public GUI2() {
System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName());
try {
// setNimbusLookAndFeel();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName()
+ " before component creation");
gridBag = new GridBagLayout();
setLayout(gridBag);
tabs = new JTabbedPane();
selectionPanel = new JPanel(gridBag);
infoPanel = new JPanel(gridBag);
settingsPanel = new JPanel(gridBag);
setUpComponents();
addComponents();
setWindowProperties();
Action reset = new AbstractAction()
{
public void actionPerformed(ActionEvent ae)
{
try {
System.out.println("Setting to original, which is " + originalLookAndFeel.getName());
UIManager.setLookAndFeel(originalLookAndFeel);
System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName());
} catch (UnsupportedLookAndFeelException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
};
Timer timer = new Timer(500, reset);
timer.setRepeats(false);
timer.start();
}
private void setWindowProperties() {
// setLayout(gridBag);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("fAmos Quester");
// setResizable(false);
pack();
setLocationRelativeTo(null);
}
private void setNimbusLookAndFeel() {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
}
}
} catch (Exception e) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e2) {
}
}
}
public void setUpComponents() {
tabs.addTab("Quest selection", selectionPanel);
tabs.addTab("Quest info", infoPanel);
tabs.addTab("Settings", settingsPanel);
selectionPanel.setPreferredSize(new Dimension(650, 500));
infoPanel.setPreferredSize(new Dimension(650, 500));
settingsPanel.setPreferredSize(new Dimension(650, 500));
}
private void addComponents() {
add(tabs);
}
public static void main(String[] args) {
new GUI2().setVisible(true);
}
}
Maybe a solution would be to create the component using the Nimbus LAF as is done above. However, don't reset the LAF until the frame is deactivated. Then you could try resetting the LAF every time the frame is activated. You would use a WindowListener to handle the activated/deactivated events.
The problem originates from attempting to do the PLAF change in a static block. Move it to the constructor and it works.
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class GUI extends JFrame {
private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel();
private GridBagLayout gridBag = new GridBagLayout();
private JTabbedPane tabs = new JTabbedPane();
private JPanel selectionPanel = new JPanel(gridBag);
private JPanel infoPanel = new JPanel(gridBag);
private JPanel settingsPanel = new JPanel(gridBag);
public GUI() {
System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName());
try {
setNimbusLookAndFeel();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName()
+ " before component creation");
setWindowProperties();
setUpComponents();
addComponents();
try {
System.out.println("Setting to original, which is " + originalLookAndFeel.getName());
UIManager.setLookAndFeel(originalLookAndFeel);
System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName());
} catch (UnsupportedLookAndFeelException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
private void setWindowProperties() {
setLayout(gridBag);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(700, 600));
setTitle("fAmos Quester");
setResizable(false);
setLocationRelativeTo(null);
}
private void setNimbusLookAndFeel() {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
}
}
} catch (Exception e) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e2) {
}
}
}
public void setUpComponents() {
tabs.addTab("Quest selection", selectionPanel);
tabs.addTab("Quest info", infoPanel);
tabs.addTab("Settings", settingsPanel);
selectionPanel.setPreferredSize(new Dimension(650, 500));
infoPanel.setPreferredSize(new Dimension(650, 500));
settingsPanel.setPreferredSize(new Dimension(650, 500));
}
private void addComponents() {
add(tabs);
}
public static void main(String[] args) {
new GUI().setVisible(true);
}
}
LAFs can't be mixed in the general case, they are designed such there is exactly one for all components in any application at any given time. So the outcome of mixing is simply undefined - you might or might not get away with it in a concrete context, but be prepared for unexpected visual and feel artifacts.
An example for visual artefacts (it's done in SwingX testing infrastructure, simple enough to write it out - but I'm too lazy ;-) - open the optionPane, than move it around: you'll see Nimbus striping colors appear more or less unpredictably
setLAF("Metal");
final JTable table = new JTable(new AncientSwingTeam());
JXFrame frame = wrapWithScrollingInFrame(table, "Metal-base");
Action action = new AbstractAction("show dialog") {
#Override
public void actionPerformed(ActionEvent e) {
setLAF("Nimbus");
JOptionPane.showMessageDialog(table, "dummy - we are Nimbus!");
setLAF("Metal");
}
};
addAction(frame, action);
show(frame);
The technical reason is that the ui-delegates may access properties stored in the UIManager at any time: mostly, they configure the component's properties from those stored in the UIManager at instantiation time and after that access those properties from the component. Occasionally though, they access the UIManager directly .. thus leading to unpredictable artefacts.
This solution assumes that you are going to change the Look and Feel setting for "this" particular window (a little private helper method). I used a dialog window to get input from the user (you could re engineer this yourself to hide it from the user and make the change happen when you need it). Of course this is a little long for clarity, and can be easily shortened.
private void changeLookAndFeel() {
final LookAndFeelInfo[] list = UIManager.getInstalledLookAndFeels();
//Look And Feels available
final List<String> lookAndFeelsDisplay = new ArrayList<>();
final List<String> lookAndFeelsRealNames = new ArrayList<>();
for (LookAndFeelInfo each : list) {
lookAndFeelsDisplay.add(each.getName()); //simplified name of each available look and feel
lookAndFeelsRealNames.add(each.getClassName()); //class name
}
if (lookAndFeelsDisplay.size() != lookAndFeelsRealNames.size()) {
throw new InternalError(); //should never happen, redundant
}
String changeSpeed = (String) JOptionPane.showInputDialog(this, "Choose Look and Feel Here\n(these are all available on your system):", "Choose Look And Feel", JOptionPane.QUESTION_MESSAGE, null, lookAndFeelsDisplay.toArray(), null);
boolean update = false;
if (changeSpeed != null && changeSpeed.length() > 0) {
for (int a = 0; a < lookAndFeelsDisplay.size(); a++) {
if (changeSpeed.equals(lookAndFeelsDisplay.get(a))) {
try {
UIManager.setLookAndFeel(lookAndFeelsRealNames.get(a)); //reads the identical class name at the corresponding index position.
this.whichLookAndFeel = changeSpeed;
update = true;
break;
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
err.println(ex);
ex.printStackTrace();
Logger.getLogger(MyClass.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
if (update) {
// make updates here...
}
}

Smart JScrollPane autoscrolling

I am attempting to implmement smart autoscrolling on a JScrollPane containing a JTextPane. The JTextPane is used for logging my app in color. However I'm running into a wall trying to do smart autoscrolling. By smart autoscrolling I don't mean blindly autoscrolling every time something changes, I mean checking to see if your scrolled all the way down, then autoscroll. However no matter what I do it either always autoscrolls or doesn't at all
As a test script, here's the setup (the JFrame has been left out)
final JTextPane textPane = new JTextPane();
textPane.setEditable(false);
final JScrollPane contentPane = new JScrollPane(textPane);
contentPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
And here's the ugly auto add test loop
while (true)
try {
Thread.sleep(1000);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
JScrollBar scrollBar = scroll;
boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
System.out.println("Value: " + scroll.getValue()
+ " | Visible: " + scrollBar.getVisibleAmount()
+ " | Maximum: " + scrollBar.getMaximum()
+ " | Combined: " + (scrollBar.getValue() + scrollBar.getVisibleAmount())
+ " | Vis!=Max : " + (scrollBar.getVisibleAmount() != scrollBar.getMaximum())
+ " | Comb=Max: " + (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum())
+ " | Eval: " + preCheck);
StyledDocument doc = textPane.getStyledDocument();
doc.insertString(doc.getLength(), "FAGAHSIDFNJASDKFJSD\n", doc.getStyle(""));
if (!preCheck)
textPane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
Its not pretty, but it gets the job done.
Here's though the relevant check
boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
if (preCheck)
textPane.setCaretPosition(doc.getLength());
Thats the part thats been giving me trouble. There is first a check to see if the bar is visible but unusable (not enough text, making the bar the full length), then if the bottom of the bar is equal to the maximum. In theory, that should work. However nothing, including moving the check around, has gotten the results I would like.
Any suggestions?
NOT A DUPLICATE of this or this, as they are wanting it to always scroll, not just sometimes.
Edit:
I replaced the following code with a more flexible version that will work on any component in a JScrollPane. Check out: Smart Scrolling.
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.text.*;
public class ScrollControl implements AdjustmentListener
{
private JScrollBar scrollBar;
private JTextComponent textComponent;
private int previousExtent = -1;
public ScrollControl(JScrollPane scrollPane)
{
Component view = scrollPane.getViewport().getView();
if (! (view instanceof JTextComponent))
throw new IllegalArgumentException("Scrollpane must contain a JTextComponent");
textComponent = (JTextComponent)view;
scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.addAdjustmentListener( this );
}
#Override
public void adjustmentValueChanged(final AdjustmentEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
checkScrollBar(e);
}
});
}
private void checkScrollBar(AdjustmentEvent e)
{
// The scroll bar model contains information needed to determine the
// caret update policy.
JScrollBar scrollBar = (JScrollBar)e.getSource();
BoundedRangeModel model = scrollBar.getModel();
int value = model.getValue();
int extent = model.getExtent();
int maximum = model.getMaximum();
DefaultCaret caret = (DefaultCaret)textComponent.getCaret();
// When the size of the viewport changes there is no need to change the
// caret update policy.
if (previousExtent != extent)
{
// When the height of a scrollpane is decreased the scrollbar is
// moved up from the bottom for some reason. Reposition the
// scrollbar at the bottom
if (extent < previousExtent
&& caret.getUpdatePolicy() == DefaultCaret.UPDATE_WHEN_ON_EDT)
{
scrollBar.setValue( maximum );
}
previousExtent = extent;
return;
}
// Text components will not scroll to the bottom of a scroll pane when
// a bottom inset is used. Therefore the location of the scrollbar,
// the height of the viewport, and the bottom inset value must be
// considered when determining if the scrollbar is at the bottom.
int bottom = textComponent.getInsets().bottom;
if (value + extent + bottom < maximum)
{
if (caret.getUpdatePolicy() != DefaultCaret.NEVER_UPDATE)
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
}
else
{
if (caret.getUpdatePolicy() != DefaultCaret.UPDATE_WHEN_ON_EDT)
{
caret.setDot(textComponent.getDocument().getLength());
caret.setUpdatePolicy(DefaultCaret.UPDATE_WHEN_ON_EDT);
}
}
}
private static void createAndShowUI()
{
JPanel center = new JPanel( new GridLayout(1, 2) );
String text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n";
final JTextArea textArea = new JTextArea();
textArea.setText( text );
textArea.setEditable( false );
center.add( createScrollPane( textArea ) );
System.out.println(textArea.getInsets());
final JTextPane textPane = new JTextPane();
textPane.setText( text );
textPane.setEditable( false );
center.add( createScrollPane( textPane ) );
textPane.setMargin( new Insets(5, 3, 7, 3) );
System.out.println(textPane.getInsets());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(center, BorderLayout.CENTER);
frame.setSize(500, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(2000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
Date now = new Date();
textArea.getDocument().insertString(textArea.getDocument().getLength(), "\n" + now.toString(), null);
textPane.getDocument().insertString(textPane.getDocument().getLength(), "\n" + now.toString(), null);
}
catch (BadLocationException e1) {}
}
});
timer.start();
}
private static JComponent createScrollPane(JComponent component)
{
JScrollPane scrollPane = new JScrollPane(component);
new ScrollControl( scrollPane );
return scrollPane;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

Categories