Graphic application in java. The process of developing a simple GUI program in Java

Graphic application in java. The process of developing a simple GUI program in Java

03.03.2022

The graphical interface in Java has gone through a very thorny path of development and formation. For a long time he was accused of slow work, greed for system resources and limited functionality.

Java AWT

Sun's first attempt at a GUI for Java was the library A.W.T.(Abstract Window Toolkit) - a toolkit for working with various window environments. Sun has made a Java layer that calls methods from libraries written in C. The AWT library methods create and use the graphical components of the operating environment. On the one hand, this is good, since a Java program is similar to other programs within the same OS. But when you run it on a different platform, there may be differences in the sizes of components and fonts that will spoil the appearance of the program.

To ensure multi-platform A.W.T. component call interfaces have been unified, resulting in a slightly reduced functionality. And the set of components turned out to be quite small. For example, there are no tables in AWT, and icons are not supported in buttons. However, the package java.awt included with Java from the very first release and can be used to create GUIs.

So the components A.W.T. don't do any "work". It's just a "Java wrapper" for the controls of the operating system they run on. All requests to these components are redirected to the operating system, which does all the work.

Used resources A.W.T. tries to release automatically. This slightly complicates the architecture and affects performance. Writing something serious using AWT will be somewhat difficult. Now it is used only for applets.

Basic SWING Concepts

After A.W.T. Sun has developed a graphics component library Swing, written entirely in Java. 2D is used for rendering, which brings with it several advantages at once. The set of standard components far exceeds AWT in terms of variety and functionality. Swing makes it easy to create new components by inheriting from existing ones, and supports a variety of styles and skins.

Creators of the new UI library Swing did not "reinvent the wheel" and chose AWT as the basis for their library. Of course, we were not talking about the use of specific heavyweight AWT components (represented by the Button, Label and the like classes). Only lightweight components provided the required degree of flexibility and controllability. The inheritance diagram shows the relationship between AWT and Swing.

The most important difference Swing from AWT is that Swing components are not connected to the operating system at all and are therefore much more stable and faster. Such components are called lightweight in Java, and understanding the basic principles of how they work will go a long way to explain how Swing works.

Swing Top Level Containers

To create an application's graphical interface, you need to use special components of the Swing library, called top-level containers. They are operating system windows that host user interface components. Top-level containers include the JFrame and JWindow windows, the JDialog dialog box, and the JApplet applet (which is not a window, but is also intended to display an interface in the browser running this applet). Top-level Swing containers are heavyweight components and are an exception to the general rule. All other Swing components are lightweight.

Simple Swing window interface example jFrame.

Import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JLabel; public class JFrameTest ( public static void createGUI() ( JFrame frame = new JFrame("Test frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JLabel label = new JLabel("Test label"); frame.getContentPane(). add(label); frame.setPreferredSize(new Dimension(200, 100)); frame.pack(); frame.setVisible(true); ) public static void main(String args) ( JFrame.setDefaultLookAndFeelDecorated(true); javax. swing.SwingUtilities.invokeLater(new Runnable() ( public void run() ( createGUI(); ) )); ) )

Constructor JFrame() without parameters creates an empty window. Constructor JFrame(String title) creates an empty window with title title. To create a simple program with an empty window, you need to use the following methods:

  • setSize(int width, int height) - defining the size of the window;
  • setDefaultCloseOperation(int operation) - definition of action on program termination;
  • setVisible(boolean visible) - make the window visible.

If you don't define the size of the window, then it will have a zero height regardless of what is in it. The dimensions of the window include not only the "working" area, but also the borders and the title bar.

The setDefaultCloseOperation method defines the action to be taken when "exiting the program". To do this, pass the EXIT_ON_CLOSE constant, described in the JFrame class, as the operation parameter.

By default, the window is created invisible. To display the window on the screen, the setVisible method is called with the parameter true. If you call it with the false parameter, the window will become invisible.

GUI java swing window creation example jFrame shown in the following figure.

To connect the library Swing the application needs to import the library javax.swing.

Every time a top-level container is created, be it a normal window, dialog box, or applet, the container's constructor creates JRootPane root panel. Top-level Swing containers make sure that other components can't "crawl" outside of the JRootPane.

Root Pale JRootPane adds a "depth" property to containers, providing the ability not only to place components one above the other, but also, if necessary, to swap them, increase or decrease the depth of the components. This feature is necessary when creating a multi-document application. Swing, whose windows represent lightweight components stacked on top of each other, as well as drop-down (context) menus and tooltips.

The following figure clearly shows the structure of the root panel JRootPane.

Root panel JRootPane is a container that inherits from the base Swing JComponent class. In this container, a special layout manager, implemented in the internal RootPaneLayout class, is responsible for the layout of the components. This layout manager is responsible for ensuring that all component parts of the root panel are placed as they should be: the layered panel occupies the entire space of the window; its FRAME_CONTENT_LAYER contains the menu bar and content panel, and above it all is a transparent panel.

All components of the root panel JRootPane can be obtained or changed. It has a set of get/set methods for this. Programmatically JRootPane can be obtained using the getRootPane() method.

In addition to top-level containers, the root pane is used in internal JInternalFrame windows created in multi-document applications and located on the "desktop" JDesktopPane. This allows you to forget that these windows are ordinary lightweight components, and work with them as if they were real top-level containers.

Layered Panel JLayeredPane

At the base of the root panel (container) lies the so-called layered panel JLayeredPane A that takes up all the available space in the container. It is in this panel that all other parts of the root panel are located, including all user interface components.

JLayeredPane used to add a depth property to the container. That is, the multilayer panel allows you to organize a third dimension in the container, along which the layers (layers) of the component are located. In a normal container, the location of a component is determined by a rectangle that shows how much of the container the component occupies. When adding a component to a multilayer panel, you must specify not only the rectangle occupied by the component, but also the layer in which it will be located. A layer in a layered panel is defined by an integer. The larger the number defining the layer, the higher the layer is.

The first component added to the container is higher than the components added later. Most often, the developer does not deal with the positions of the components. When you add components, their position changes automatically. However, the layered panel allows you to change the position of the components dynamically, after they are added to the container.

Layered panel capabilities are widely used by some components Swing. They are especially important for multi-document applications, tooltips and menus. Multidocument Swing applications use a special container JDesktopPane("desktop") inherited from JLayeredPane A that holds inner Swing windows. The most important functions of a multi-document application - positioning the "active" window above others, minimizing windows, dragging them - are provided by the mechanisms of the layered panel. The main advantage of using a layered panel for tooltips and menus is their speed. Instead of creating a new heavyweight window for each tooltip or menu, located above the component in which the tooltip or menu was requested, Swing creates a fast lightweight component. This component is placed in a sufficiently high layer of the layered panel above in the stack of all other components and is used to display a tooltip or menu.

The multi-layer panel allows you to organize an unlimited number of layers. Structure JLayeredPane includes several standard layers, which are used by all Swing components, which ensures that all the mechanisms of the layered panel work correctly. Standard JLayeredPane layers are shown in the following figure.

The Default layer is used to host all the normal components that are added to the container. This layer contains the internal windows of multi-document applications.

The Palette layer is designed to host windows with a set of tools, which usually overlap the rest of the interface. The JDesktopPane panel allows you to create such windows, which places them on this layer.

The Modal layer was intended to host lightweight modal dialog boxes. However, such dialog boxes are not yet implemented, so this layer is not currently used in Swing.

The most commonly used layer for popup menus and tooltips.

The topmost layer. Designed for drag and drop operations, which should be clearly visible in the program interface.

A small example of a JLayeredPane with a layered panel shows how to add components to different layers and how the layers stack on top of each other:

Import javax.swing.*; import java.awt.*; // class for drawing two types of figures with text class Figure extends JComponent ( private static final long serialVersionUID = 1L; private Color color; private int type; private String text; // parameters: color and figure type Figure(Color color, int type, String text) ( this.color = color; this.type = type; this.text = text; setOpaque(false); ) public void paintComponent(Graphics g) ( // draw the figure g.setColor(color); switch (type ) ( case 0: g.fillOval(0, 0, 90, 90); break; case 1: g.fillRect(0, 0, 130, 80); break; ) g.setColor(Color.yellow); g. drawString(text, 10, 35); ) ) public class JLayeredPaneTest extends JFrame ( private static final long serialVersionUID = 1L; public JLayeredPaneTest() ( // create window super("Example LayeredTest"); // exit when window is closed setDefaultCloseOperation( EXIT_ON_CLOSE); // define layered panel JLayeredPane lp = getLayeredPane(); // create three shapes Figure figure1 = new Figure(Color.red , 0, "Figure po pup"); Figure figure2 = new Figure(Color.blue, 0, "Figure 1"); Figure figure3 = new Figure(Color.cyan, 1, "Figure 2"); // determine the location of the figures in the window figure1.setBounds(10, 40, 120, 120); figure2.setBounds(60, 120, 160, 180); figure3.setBounds(90, 55, 250, 180); // adding shapes to different layers lp.add(figure1, JLayeredPane.POPUP_LAYER); lp.add(figure2, JLayeredPane.PALETTE_LAYER); lp.add(figure3, JLayeredPane.PALETTE_LAYER); // change the position of one of the figures lp.setPosition(figure3, 0); // determining the size and opening the window setSize(280, 250); setVisible(true); ) public static void main(String args) ( JFrame.setDefaultLookAndFeelDecorated(true); new JLayeredPaneTest(); ) )

The example creates a small window jFrame and multiple Figure components are added to the layered panel. To get a layered panel in any top-level Swing container, just call the method getLayeredPane().

The Figure helper class inherits the properties of the JComponent base class and allows you to draw two types of shapes (circles and rectangles) with different colors. Parameters for drawing shapes are set in the class constructor.

When defining an interface, three shapes of different colors (two circles and a rectangle) are created. The circle is placed in the POPUP_LAYER layer, and the rectangles are placed in the PALETTE_LAYER layer. When placing components, their absolute screen coordinates are specified, because the usual location managers do not work in a layered panel.

At the end, the position of one of the rectangles is changed so that it is the first in the layer, although it was originally added the second. When you run the application, you will see that the layered panel works and neatly arranges the components according to their layers and positions.

In conventional applications, the layered panel is rarely used directly, in which it performs its functions invisibly. However, sometimes it helps to create amazing effects and unusual interfaces, allowing, for example, to place animation or video on top of ordinary components without requiring inhuman efforts and tricks from the developer.

ContentPane

The ContentPane is the next part of the root pane and is used to host the program's user interface components. ContentPane occupies most of the space of the layered panel (except for the space occupied by the menu bar). To prevent the content panel from covering components that are subsequently added to the window, the layered panel places it on a special very low layer called FRAME_CONTENT_LAYER, numbered -30000.

You can access the content panel using getContentPane() JFrame class. Using the add(Component component) method, you can add any control to it. Replace ContentPane any other panel of JPanel type, you can use the method setContentPane()

An example of adding a button to the content panel:

JButton newButton = new JButton(); getContentPane().add(newButton);

As a result, we get a window with a button. The button occupies the entire available area of ​​the window. This effect is not useful in all programs, so you need to use different ways to arrange elements on the panel.

The content panel can be completely replaced. Consider the following Swing content panel example ContentPane.

Import javax.swing.*; public class ContentPaneReplace extends JFrame ( private static final long serialVersionUID = 1L; public ContentPaneReplace() ( super("Test ContentPane"); setDefaultCloseOperation(EXIT_ON_CLOSE); // Create a panel with two buttons JPanel contents = new JPanel(); contents.add (new JButton("Family")); contents.add(new JButton("School")); // Replacing the content pane setContentPane(contents); // Determining the size of the window setSize(200, 100); // Opening the window setVisible (true); ) public static void main(String args) ( JFrame.setDefaultLookAndFeelDecorated(true); new ContentPaneAdd(); ) )

The example creates a small window and a panel with two buttons, which is then setContentPane() replaces the window's content pane. Thus, a replacement was used instead of a simpler addition - calling the add () method. The window interface is shown in the following screenshot.

Content panel ContentPane by itself is nothing special. You just need to remember that the components are added to it.

Transparent JOptionPane

Transparent panel JOptionPane placed as the root panel above all elements of the layered panel. JOptionPane placement is handled by the root pane, which places the transparent pane above the layered pane so that it completely covers the entire window area, including the area occupied by the menu bar.

JOptionPane is rarely used in applications, so by default the root panel makes it invisible, which reduces the load on the drawing system. Keep in mind that if you make a transparent panel visible, you need to make sure that it is transparent (its opaque property is false), because otherwise it will cover all other elements of the root panel and the rest of the interface will be invisible.

In what cases can a transparent panel be used? JOptionPane? It can be used to define application features that would require significant effort to implement from scratch. The transparent panel can be adapted for automated user interface testing. The events synthesized in it allow you to track intermediate debugging results. Sometimes this approach is much more effective than manual testing.

Transparent panel JOptionPane can be used for a fancy animation that "floats" on top of all components, including the menu bar, or for catching events if some of them need to be handled before being sent to the main UI.

An example of using a transparent Swing JOptionPane:

// Using a transparent JOptionPane import java.awt.Dimension; import java.awt.Font; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.UIManager; public class JOptionPaneTest extends JFrame ( private static final long serialVersionUID = 1L; public static final Font FONT = new Font("Verdana", Font.PLAIN, 11); public static void createGUI() ( JFrame frame = new JFrame("Test JOptionPane "); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowListener() ( public void windowActivated(WindowEvent event) () public void windowClosed(WindowEvent event) () public void windowDeactivated(WindowEvent event) () public void windowDeiconified(WindowEvent event) () public void windowIconified(WindowEvent event) () public void windowOpened(WindowEvent event) () public void windowClosing(WindowEvent event) ( Object options = ( "Yes", "No!" ); int rc = JOptionPane.showOptionDialog(event.getWindow(), "Close window?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options); (false);System.exit(0); ) ) )); JLabel label = new JLabel("Use transparent panel when closing window"); frame.getContentPane().add(label); frame.setPreferredSize(new Dimension(350, 80)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); ) public static void main(String args) ( javax.swing.SwingUtilities.invokeLater(new Runnable() ( public void run() ( UIManager.put("Button.font", FONT); UIManager.put("Label.font ", FONT); JFrame.setDefaultLookAndFeelDecorated(true); JDialog.setDefaultLookAndFeelDecorated(true); createGUI(); ) )); ) )

If the setDefaultCloseOperation method is passed a constant JFrame.EXIT_ON_CLOSE, then when the window is closed, the application will stop working. In the example, a constant is passed to this method JFrame.DO_NOTHING_ON_CLOSE so that nothing happens when the window is closed. Exiting the application in the example is done from the JFrame listener WindowListener in the method windowClosing. When the window is closed, the windowClosing method is called with a WindowEvent event parameter, which in a transparent Swing JOptionPane opens a confirmation dialog.

The following screenshot shows two application windows. Top main window. Closing this window opens the lower intent confirmation dialog box.

JMenuBar Menu Bar

One of the important features of using the JRootPane root pane in Swing is the need to place a menu bar in the window. JMenuBar. A serious application cannot be built without some kind of menu for accessing the program's functions. The Swing library provides excellent facilities for creating convenient JMenuBars that are also lightweight components.

Menu bar JMenuBar placed in a layered panel in a special layer FRAME_CONTENT_LAYER and occupies a small space at the top of the window. The length of the menu bar is equal to the size of the window. The width of the menu bar depends on the components it contains.

The root panel makes sure that the content panel and menu bar JMenuBar did not overlap. If the menu bar is not required, then the root panel uses all the space to accommodate the content panel.

Swing Examples

The source codes of the examples discussed in the text of the page can be downloaded.

The user interface in Java has gone through a very thorny path of formation and development. For a long time he was accused of slow work, greed for system resources, limited functionality. The advent of .NET with faster graphical components has further shaken the position of Java. But every cloud has a silver lining - all this movement only spurred Java developers to develop and improve graphics libraries. Let's see what came of it.

Abstract Window Toolkit

AWT was Sun's first attempt at a GUI for Java. They took the easy route and simply made a Java layer that calls methods from libraries written in C. The library methods create and use the graphical components of the operating environment. On the one hand, this is good, since a Java program is similar to other programs within this OS. But on the other hand, there is no guarantee that differences in component sizes and fonts will not spoil the appearance of the program when it is run on a different platform. In addition, in order to ensure multiplatform, it was necessary to unify the call interfaces of components, which is why their functionality turned out to be a little truncated. And the set of components turned out to be quite small. For example, AWT doesn't have tables, and buttons don't support displaying icons.

Used resources AWT tries to release automatically. This slightly complicates the architecture and affects performance. Learning AWT is pretty easy, but writing something complex can be a little tricky. Now it is used only for applets.

Advantages:

  • part of the JDK;
  • work speed;
  • graphical components are similar to the standard ones.

Disadvantages:

  • the use of native components imposes restrictions on the use of their properties. Some components may not work at all on "non-native" platforms;
  • some properties, such as icons and tooltips, do not exist in AWT at all;
  • There are very few standard AWT components, the programmer has to implement many custom ones;
  • the program looks different on different platforms (may be crooked).

conclusion:

Currently, AWT is used extremely rarely - mainly in old projects and applets. Oracle hid the tutorials and strongly encourages the transition to Swing. This is understandable, direct access to the components of the axis can be a serious security hole.

Swing


Following AWT, Sun developed a set of graphical components called Swing. Swing components are written entirely in Java. 2D is used for rendering, which brings with it several advantages at once. The set of standard components far exceeds AWT in terms of variety and functionality. It has become easy to create new components, inheriting from existing ones and drawing whatever your heart desires. Support for various styles and skins has become possible. However, the speed of the first versions of Swing left much to be desired. An incorrectly written program could even hang Windows tightly.

Nevertheless, due to its ease of use, rich documentation, and flexible components, Swing has become perhaps the most popular graphics framework in Java. Many extensions have appeared on its basis, such as SwingX, JGoodies, which greatly simplify the creation of complex user interfaces. Almost all popular Java programming environments include graphical editors for Swing forms. Therefore, it will not be difficult to understand and start using Swing.

Advantages:

  • part of the JDK, no need to install additional libraries;
  • there are many more books and forum answers on Swing. All problems, especially for beginners, are thoroughly known to Google;
  • built-in form editor in almost all development environments;
  • there are many extensions based on swing like SwingX;
  • support for various styles (Look and feel).

Disadvantages:

  • a window with many components starts to slow down;
  • working with layout managers can be a real nightmare in complex interfaces.

Conclusion:

Swing has lived, Swing is alive, Swing will live. Although Oracle is trying to promote JavaFX, Swing remains the most popular Java UI framework today.

Standard Widget Toolkit


How
looks
SWT

SWT was developed at IBM in the days when Swing was still slow, and it was done mainly to promote the Eclipse programming environment. SWT, like AWT, uses operating system components, but it has its own interaction interfaces for each platform. So for each new system, you will have to ship a separate JAR library with the correct version of SWT. This allowed more complete use of the existing features of the components on each axis. The missing features and components were implemented using 2D, like in Swing. SWT has many adherents, but, in all honesty, one cannot but agree that everything turned out to be not as simple as we would like. A beginner will have to spend much more time learning SWT than getting acquainted with the same Swing. In addition, SWT puts the task of releasing resources on the programmer, and therefore he needs to be especially careful when writing code so that an accidental exception does not lead to memory leaks.

Advantages:

  • uses operating system components - speed is higher;
  • Eclipse provides a visual form editor;
  • extensive documentation and many examples;
  • it is possible to use AWT and Swing components.

Disadvantages:

  • for each platform it is necessary to supply a separate library;
  • you need to constantly monitor the use of resources and release them in time;
  • complex architecture, evoking suicidal thoughts after futile attempts to implement a custom interface.

Conclusion:

It can be seen that IBM tried. But it turned out very amateurish ...

JavaFX


JavaFX can be called a breakthrough without exaggeration. For rendering, a graphics pipeline is used, which significantly speeds up the application. The set of built-in components is extensive, there are even separate components for drawing graphs. Implemented support for multimedia content, many display effects, animations and even multi-touch. The appearance of all components can be easily changed using CSS styles. And the best thing is that JavaFX includes a set of utilities that allow you to make a native installer for the most popular platforms: exe or msi for Windows, deb or rpm for Linux, dmg for Mac. On the Oracle website, you can find detailed documentation and a huge number of ready-made examples. This makes programming with JavaFX easy and enjoyable.

Advantages:

  • fast work due to the graphics pipeline;
  • many different components;
  • style support;
  • utilities for creating a program installer;
  • the application can be launched as a desktop and in the browser as part of the page.

Disadvantages:

  • the framework is still being developed, so crashes and some glitches happen;
  • JavaFX is not widely used yet.

Conclusion:

Good job, Oracle. The framework leaves only positive impressions. It is easy to understand, methods and interfaces look logical. I want to use again and again!

Visual libraries in practice

SWT: weather widget

To demonstrate the capabilities of the most popular graphic libraries and the basic principles of working with them, we will make several small widgets that display various information.

And let's start, perhaps, with the most popular widget - displaying the current weather, for the implementation of which we will choose SWT.

Any SWT program starts with the creation of a Display object. It serves as a kind of application context that contains the necessary methods for accessing system resources and provides an event loop. The next step is to create the equally important Shell object. Shell is a normal operating system window. Display is passed to the shell constructor to create a top-level window.

Display display = new Display(); shell = new Shell(display, SWT.NO_TRIM);

Since we are creating a widget, we do not need to display the standard window frame and control buttons, for this we have specified the NO_TRIM flag. For the background, we will use a picture - a rectangle with rounded corners. In principle, a SWT window can take on any shape. To achieve this effect, we use the Region class. All that is needed is to add to this class all the visible points from the background image, skipping the transparent ones.

Uploading an image:

Image image = new Image(display, "images/bg.png#26759185");

In images of different formats, transparency is set in different ways, so information about transparent areas is also not retrieved in the same way. Create a background area and add all the visible dots there:

Region region = new Region(); ImageData imageData = image.getImageData(); if (imageData.alphaData != null) ( Rectangle pixel = new Rectangle(0, 0, 1, 1); for (int y = 0; y< imageData.height; y++) { for (int x = 0; x < imageData.width; x++) { if (imageData.getAlpha(x, y) == 255) { pixel.x = imageData.x + x; pixel.y = imageData.y + y; region.add(pixel); } } } } else { ImageData mask = imageData.getTransparencyMask(); Rectangle pixel = new Rectangle(0, 0, 1, 1); for (int y = 0; y < mask.height; y++) { for (int x = 0; x < mask.width; x++) { if (mask.getPixel(x, y) != 0) { pixel.x = imageData.x + x; pixel.y = imageData.y + y; region.add(pixel); } } } }

Set window shape:

Shell.setRegion(region);

Now we need to create an event listener for the window. We will be interested in window drawing events, mouse events, and key presses so that the window can be moved around the screen.

Listener listener = new Listener() ( int startX, startY; public void handleEvent(Event e) ( if (e.type == SWT.KeyDown && e.character == SWT.ESC) ( shell.dispose(); ) if (e.type == SWT.MouseDown && e.button == 1) ( startX = ex; startY = ey; ) if (e.type == SWT.MouseMove && (e.stateMask & SWT.BUTTON1) != 0 ) ( Point p = shell.toDisplay(ex, ey); px -= startX; py -= startY; shell.setLocation(p); ) if (e.type == SWT.Paint) ( e.gc.drawImage( image, imageData.x, imageData.y); ) ) );

So, by pressing the Esc key, the window will close. When you press the left mouse button on the area of ​​the window, remember the coordinates of the click. When moving the mouse with the left button pressed, we move the window on the screen according to the movement. On redraw event - draw background image using GC graphics context.

Assign a listener to the corresponding window events:

Shell.addListener(SWT.KeyDown, listener); shell.addListener(SWT.MouseDown, listener); shell.addListener(SWT.MouseMove, listener); shell.addListener(SWT.Paint, listener);

Set the window size to the size of the image:

Shell.setSize(imageData.x + imageData.width, imageData.y + imageData.height);

Open the window and run the event loop:

Shell.open(); while (!shell.isDisposed ()) ( if (!display.readAndDispatch ()) display.sleep (); )

Don't forget to release the used resources at the end:

region.dispose(); image.dispose(); display.dispose();

By running the program at this stage, we will get a rectangle that can be moved with the mouse and closed with Esc.

It's time to add content. We will display the current weather in the form of a status icon (sunny, rain, snow ...), temperature readings and the time of the last update.

Layout managers are used to arrange graphical components in the window in the desired form. The layout manager is concerned not only with the arrangement of components, but also with their resizing when the window is resized. We will use GridLayout for our widget. This manager arranges the components in the cells of an imaginary table. Create a GridBagLayout for two columns with different column widths (false flag in the constructor), set it as the window layout manager:

GridLayout layout = new GridLayout(2, false); shell.setLayout(layout);

For the status image, we use the Label component. We pass the window object as the parent. The second parameter is to set the style of the component. For each component, the set of possible style flags is different, they can be found in the documentation or directly in the source code of the component.

//draw status image Label imageLabel = new Label(shell, SWT.NONE); imageLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1));

The flags in the GridData class mean that the label will be positioned at the top left, will stretch horizontally and vertically (flags set to true) when there is free space, and occupy one row and one column of the layout table.

SWT does not have a transparent background for components, and a white background will show off behind the status image, which, of course, would not be desirable. So let's create a Color object with the background color of the window:

Color bgColor = new Color(display, 0x2b, 0x2b, 0x2b);

At the end of the program, this object must also be cleaned up by calling the dispose method. Set the background color and status image, which can be loaded from a file in the same way as we loaded the background image at the beginning:

ImageLabel.setBackground(bgColor); Image statusImage = new Image(display, "images/1.png#26759185"); imageLabel.setImage(statusImage);

Now let's add a Label with the current temperature and place it in the upper right part of the window:

Label temperatureLabel = new Label(shell, SWT.NONE); temperatureLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));

Let's set some temperature:

TemperatureLabel.setText("+1 \u2103");

To record the temperature in Celsius, the Unicode number of the corresponding character is used with the service characters \u.

The default font for text labels is too small. So let's create a new, bigger one:

FontData fD = temperatureLabel.getFont().getFontData(); fD.setHeight(30); fD.setStyle(SWT.BOLD); Font newFont = new Font(display, fD); temperatureLabel.setFont(newFont); The font, like other resource objects, needs to be freed. To do this, we use the label destruction event listener:

TemperatureLabel.addDisposeListener(new DisposeListener() ( public void widgetDisposed(DisposeEvent e) ( newFont.dispose(); ) ));

Finally, let's add a label describing the weather conditions:

Label descriptionLabel = new Label(shell, SWT.WRAP); descriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 2, 1)); descriptionLabel.setText("Cloudy with clearings, light rain"); descriptionLabel.setBackground(bgColor); descriptionLabel.setForeground(display.getSystemColor(SWT.COLOR_WHITE));

The text can be quite long, so when creating the label, we specify the WRAP flag so that the text automatically breaks into several lines when there is not enough space. Let's center the component and let it fill the entire horizontal space. We also specify that the component occupies two columns of the layout table. We launch and get a window from the picture "Weather Widget".

Now you can hook up some kind of weather service, create a timer for automatic updates - and the widget is ready.

Swing: always fresh news

In Swing, we will write a widget to display RSS feeds. We start, like last time, by creating a window. The class that implements the standard window functionality in Swing is called JFrame. By default, closing an application window in Swing does not cause the program to stop, so it's better to specify how the window should behave when closed:

JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

A table is best for presenting news. Swing is built on the Model-View-Controller (MVC) pattern. In the MVC architecture, the Model provides the data, the View is responsible for displaying the data (eg text, input fields), and the Controller provides the interaction between the Model and the View. The table demonstrates this approach well. To represent data, a class that implements the TableModel interface is used.

To store information about available news, let's create a FeedMessage class with fields for the article title and release date:

Public class FeedMessage ( public String title; public Date publicationDate; )

To simplify and speed up development, we inherit our data model from the AbstractTableModel class, which offers a ready-made implementation of almost all methods of the TableModel interface.

Public class RssFeedTableModel extends AbstractTableModel ( private List entries = new ArrayList<>(); public void updateData(List entries) ( this.entries = entries; fireTableDataChanged(); ) public int getRowCount() ( return entries.size(); ) public int getColumnCount() ( return 2; ) public Object getValueAt(int rowIndex, int columnIndex) ( switch (columnIndex) ( case 0: return entries.get(rowIndex).title; case 1: return entries.get(rowIndex).publicationDate; ) return null; ) )

The fireTableDataChanged method tells the view that the data model has changed and needs to be rerendered.

We create a table and change its appearance a bit so that it looks more like a widget. We remove the lines between rows and columns, increase the row height and remove the table header with column names:

JTable table = new JTable(new RssFeedTableModel()); tablesetShowGrid(false); table.setIntercellSpacing(new Dimension(0, 0)); tablesetRowHeight(30); table.setTableHeader(null);

Now let's take a look at the appearance of the cells. Swing allows you to assign separate view classes to different data types. The class that inherits the TableCellRenderer interface is responsible for rendering individual table cells. The default is DefaultTableCellRenderer which is a text label.

Let's assign our cell renderer to String data. Let's change the default font color and make the background color alternate to improve readability.

Table.setDefaultRenderer(String.class, new DefaultTableCellRenderer() ( Color oddColor = new Color(0x25, 0x25, 0x25); Color evenColor = new Color(0x1a, 0x1a, 0x1a); Color titleColor = new Color(0x3a, 0xa2, 0xd7 ); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) ( super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setBackground(row % 2 == 0 ?oddColor: evenColor); setForeground(titleColor); setFont(font); return this; ) ));

In order for the table to start using our renderer, we need to add a method that returns the data type for each cell to the data model:

public classgetColumnClass(int columnIndex) ( switch (columnIndex) ( case 0: return String.class; case 1: return Date.class; ) return Object.class; )

There can be a lot of news, so let's put the table on the scroll bar and make the scroll bar invisible so that it doesn't spoil our widget design:

JScrollPane scrollPane = new JScrollPane(table); table.setFillsViewportHeight(true); scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(0,0));

Adding a scroll component to the main pane of the window. The second argument can be the location of the component. By default, the window's main panel uses the BorderLayout layout manager, which lays out components by cardinal directions. Let's place the table with scrolling in the center.

Frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

Like last time, we will remove the standard window frame. And as the title of the window, we will use a stylized text label, which we will place at the top of the window.

JLabel titleLabel = new JLabel("Xakep RSS"); Font titleFont = new Font("Arial", Font.BOLD, 20); titleLabel.setFont(titleFont); titleLabel.setHorizontalAlignment(SwingConstants.CENTER); titleLabel.setForeground(Color.WHITE); titleLabel.setPreferredSize(new Dimension(0, 40)); frame.getContentPane().add(titleLabel, BorderLayout.NORTH);

Unlike SWT, the "color" and "font" objects are deallocated automatically, so you don't have to worry about memory leaks anymore.

We add mouse listeners so that the window can be moved around the screen.

MouseAdapter listener = new MouseAdapter() ( int startX; int startY; public void mousePressed(MouseEvent e) ( if (e.getButton() == MouseEvent.BUTTON1) ( startX = e.getX(); startY = e.getY( ); ) ) public void mouseDragged(MouseEvent e) ( Point currCoords = e.getLocationOnScreen(); frame.setLocation(currCoords.x - startX, currCoords.y - startY); ) ); titleLabel.addMouseListener(listener); titleLabel.addMouseMotionListener(listener);

Now change the shape of the window to a rectangle with rounded corners. This is best done in a component listener, because if the window size changes, the window shape will be correctly recalculated:

Frame.addComponentListener(new ComponentAdapter() ( public void componentResized(ComponentEvent e) ( frame.setShape(new RoundRectangle2D.Double(0, 0, frame.getWidth(), frame.getHeight(), 20, 20)); ) ) );

Set the size of the window, remove the frame and make the window semi-transparent.

Frame.setSize(520, 300); frame.setUndecorated(true); frame.setOpacity(0.85f);

Finally, we open a window in the graphics thread. SwingUtilities.invokeLater(new Runnable() ( public void run() ( frame.setVisible(true); ) ));

It remains to add data loading in a separate thread, and we will get such a widget with the latest news from your favorite magazine :).


JavaFX: Let's listen to the music

And finally, the highlight of the season is JavaFX. Let's use its multimedia capabilities and graphing component and make a simple equalizer.

First, we inherit the widget class from Application. This is the main application class in JavaFX. Application contains the main methods of the application life cycle. Form components are created in the start method, whose argument is the Stage class. Stage represents the window of the program. Change the window style to TRANSPARENT to remove the border and buttons. The Stage class contains the Scene class, which sets the size of the window and the background color. In Scene, in turn, we pass the Group class, in which we will place child components:

Public void start(Stage primaryStage) ( primaryStage.initStyle(StageStyle.TRANSPARENT); Group root = new Group(); Scene scene = new Scene(root, 400, 200, Color.TRANSPARENT); primaryStage.setScene(scene);

To display the equalizer, we use a bar chart, along the axes of which we will display the frequency and sound power:

CategoryAxis xAxis = new CategoryAxis(); NumberAxis yAxis = new NumberAxis(0,50,10); BarChart bc = new BarChart (xAxis,yAxis); bc.setPrefSize(400, 200); bc.setLegendVisible(false); bc.setAnimated(false); bc.setBarGap(0); bc.setCategoryGap(1); bc.setVerticalGridLinesVisible(false); bc.setHorizontalGridLinesVisible(false); xAxis.setLabel("Frequency"); yAxis.setLabel("Power"); yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis, null, "dB"));

Fill in the diagram with initial data:

XYChart.Series series1 = new XYChart.Series (); series1Data = new XYChart.Data; String categories = new String; for (int i=0; i (categories[i], 50); series1.getData().add(series1Data[i]); ) bc.getData().add(series1);

Create a rectangle with rounded corners to give the widget the appropriate shape:

Rectangle rectangle = new Rectangle(0, 0, 400, 200); Stop stops = new Stop ( new Stop(0, new Color(0, 0, 0, 0.8)), null); LinearGradient lg2 = new LinearGradient(0, 0, 0, 0, false, CycleMethod.NO_CYCLE, stops); rectangle.setFill(lg2); rectangle.setArcHeight(20); rectangle.setArcWidth(20);

Add both components to the group:

Root.getChildren().addAll(rectangle, bc);

Assign mouse listeners to the group to move the window around the screen:

Root.setOnMousePressed(new EventHandler () ( public void handle(MouseEvent me) ( initX = me.getScreenX() - primaryStage.getX(); initY = me.getScreenY() - primaryStage.getY(); ) )); root.setOnMouseDragged(new EventHandler () ( public void handle(MouseEvent me) ( primaryStage.setX(me.getScreenX() - initX); primaryStage.setY(me.getScreenY() - initY); ) ));

Download the song to the player:

File file = new File("let me out of here.mp3"); media audioMedia = null; audioMedia = new Media(file.toURI().toURL().toString()); audioMediaPlayer = new MediaPlayer(audioMedia);

Add a listener that will update the bar chart:

AudioMediaPlayer.setAudioSpectrumListener(new AudioSpectrumListener() ( public void spectrumDataUpdate(double timestamp, double duration, float magnitudes, float phases) ( for (int i = 0; i< series1Data.length; i++) { series1Data[i].setYValue(magnitudes[i] + 60); } } });

Make the scene visible and play the song:

PrimaryStage.show(); audioMediaPlayer.play();

We start the application:

Public static void main(String args) ( launch(args); )

And enjoy this beauty.

In this short article I want to describe the process of creating a small program that supports GUI in the language Java. It is assumed that the reader is familiar with the basics of the language. Java.

And so, what tools do we need:

  • Java Virtual Machine (OpenJDK or Oracle JDK)
  • Intellij IDEA (or another IDE for Java)

After installing the necessary software, open IntelliJ IDEA and create a new project: File -> New Project…

I named the project guiBase. As you can see on the screenshot, the folder src does not contain anything, so we create in it our main class containing the function main.

Public class Main ( public static void main(String args) ( System.out.println("Hello, Govzalla!"); ) )

See the contents of the main class above. We can now create a project ( build project ) and run it ( Run ). Down in the terminal of your IDE you will see a message Hello, Govzalla!. But as you yourself understood - it does not support the GUI.

At this stage, we already have a working program, but without GUI support. And now in the same folder src create GUI Form: New -> GUI Form

Open the created GUI form, click on jPanel and set its identifier in the field field name, I asked panel.

Then drag and drop onto the form on the right side JTextField, JPasswordField And jButton:

It remains to add the code and link our form to it. When we added the form mainwindow, the class was automatically created mainwindow, this class is the class of the generated form, i.e. this class will serve all events of the given form.

Although our window class contains the necessary elements, even now it has nothing to do with the GUI, so let's extend it with jFrame and inherit all the basic and necessary functionality of the GUI .

We currently have the form mainwindow and class mainwindow extended with jFrame. Now we need to define all added GUI elements as class content mainwindow this.getContentPane().add(panel); After that, the contents of the MainWindow.java file will be changed as follows:

Import javax.swing.*; public class MainWindow extends JFrame ( private JTextField textField1; private JPasswordField passwordField1; private JButton button1; private JPanel panel; public MainWindow() ( this.getContentPane().add(panel); ) )

If you try to run the code, you will see the same “Hello, Govzalla!” message again. The matter is that we created a class and the form to it, but did not create an instance of this class.

It's time to change the Main.java file and add the code for creating our GUI there:

Import java.awt.*; public class Main ( public static void main(String args) ( // Create an instance of the MainWindow class MainWindow mainWindow = new MainWindow(); // Pack all the elements from our form mainWindow.pack(); // Resize the window mainWindow.setSize( new Dimension(200, 200)); // Display the created window mainWindow. setVisible(true); ) )

Running the code

By clicking on the Button you will notice that the program does not react in any way. The thing is, we haven't added the listener yet ( Listener) for events ( Events) of the Button.

event listener ( event listener) jButton must be an adapter implementation ActionListener, so add the following code to the body of the class mainwindow:

Private class MyButtonListener implements ActionListener ( @Override public void actionPerformed(ActionEvent actionEvent) ( ) )

Method actionPerformed() will handle all button1 button events, but first you still need to tell button1 what class it will handle, so add the following code to the MainWIndow class constructor: this.button1.addActionListener(new MyButtonListener()); So that our handler is not meaningless, add the following code to the method actionPerformed():

@Override public void actionPerformed(ActionEvent actionEvent) ( if (textField1.getText().equals(passwordField1.getText())) ( JOptionPane.showMessageDialog(null, "Success"); ) else ( JOptionPane.showMessageDialog(null, "Failure "); ) )

Now the program will respond correctly to events, not to all events, of course. For example, if you try to disable the program by clicking on the cross, the window will disappear, but the program will still work, because. main window event handler not added.

It so happened historically that I had to work very little with the UI. Apparently, that's why I'm so interested in all sorts of Qt and wxWidgets - everything seems new, interesting, unusual. However, as soon as I took up the study of Java, today we will talk not about Qt and not about wxWidgets, but about Swing. Today, together we will write a simple GUI application in Java, with buttons, lists, and even the ability to change skins!

The situation with GUI frameworks in the Java world is somewhat confusing. As far as I could figure out, things are as follows.

  • A.W.T.(Abstract Window Toolkit) was the first GUI framework. The idea was correct - AWT uses native controls, that is, they look and are physically native, no matter where you run your application. Unfortunately, it turned out that (1) there are few controls common to different environments and (2) it is very difficult to write cross-platform native interfaces so that nothing crawls and disperses;
  • Therefore, AWT was replaced by Swing. Swing uses molds created by AWT on which it draws controls on its own. This economy works, of course, more slowly, but the UI becomes much more portable. Swing offers the programmer a variety of Look&Feels to choose from, thanks to which you can either make the application look and behaved the same for both Windows and Linux, or for the application to be very similar to the native one, regardless of where it is run. In the first case, the application is easier to debug, in the second, users become happier. By the way, Swing was originally made by the guys at Netscape;
  • SWT(Standard Widget Toolkit) is a framework written by IBM and used in Eclipse. As in AWT, native controls are used. SWT is not part of the JDK and uses JNI, so it doesn't really fit with Java's "write once, run everywhere" ideology. It seems like with a very strong desire, you can pack the SWT implementation for all-all-all platforms into a package, and then the application seems to even become portable, but only until some new operating system or processor architecture appears;
  • JavaFX actively sawn in Oracle and is positioned as an ambulance replacement for Swing. Ideologically, JavaFX is similar to Swing, that is, controls are not native. Interesting features of JavaFX include hardware acceleration, GUI creation with CSS and XML (FXML), the ability to use JavaFX controls in Swing, as well as a bunch of new beautiful controls, including those for drawing charts and 3D. A video with a more detailed overview of JavaFX is available. Since Java 7, JavaFX has been part of the JRE/JDK;
  • NetBeans Platform(not to be confused with NetBeans IDE!) - this is such a thing that, as I understand it, works on top of Swing and JavaFX, provides a more convenient interface for working with them, as well as all sorts of additional controls. In one application using the NetBeans Platform, I saw the ability to drag and drop tabs with drug&drop, positioning panels in the window similar to how tiled window managers do. Apparently, Swing itself can't do that. Read more about NetBeans Platform;

It is possible that there are other frameworks. The most canonical today is Swing, so we'll deal with it.

Above, something was said about some Look & Feel there. To better understand what is at stake, let's write a program that displays a list of these same Look & Feel and allows you to switch between them right in the process of running the program.

Our application will look like this under Ubuntu:

And this is how it will look when running under Windows:

As you can see, JREs for Windows and Linux include a different set of L&Fs. In addition, you can connect a third-party Look&Feel or even write your own. By default, L&F Metal is used, which looks more or less the same in all operating systems and window managers. If you prefer round buttons, you can use Look&Feel Nimbus instead of Metal. If you want the application to look like a native one, then under Linux you should choose L&F GTK + (I wonder if the user is running KDE?), And under Windows - L&F Windows. It might be a good idea to make your program switch between different L&Fs. On the other hand, you will have to test the application with all these L&Fs.

Let's look at the application's source code. Colleagues of UI specialists assured me that they do not use any WYSIWYG editors, and if they do, it is only for rapid prototyping. Of the good WYSIWYG editors, the name was JFormDesigner . They say that the code it generates even looks like human-written code, and not hellish().sequence().calls().methods(). In general, all the code was written with paws in IntelliJ IDEA.

public static void main(String args) (

@Override
public void run() (
create GUI() ;
}
} ) ;
}

In Swing and AWT, if we want to change something in the UI, we have to do it from the event dispatching thread. The invokeLater static method takes a class that implements the Runnable interface and calls its run() method inside the event dispatching thread. If you're not familiar with the above syntax, this is Java's way of declaring a class without giving it a name. Classes without a name are called anonymous. Often anonymous classes in Java perform the same role that lambda functions play in functional programming languages. Among other things, closures are also supported. Interestingly, unlike lambdas, anonymous classes in Java allow you to pass a bunch of methods at once. Moreover, with the help of inheritance and abstract classes, for all or part of the methods, you can take their default implementation.

The @Override annotation checks that the run() method will actually override the method of the Runnable interface. Without it, when overriding a method, we might accidentally make a typo and define a new method instead of overriding an existing one. However, in this particular case, the annotation, apparently, is not very useful, and, probably, is even superfluous.

As a result, the event dispatching thread will call the createGUI () method, the full code of which is as follows:

private static void createGUI() (
jlist< String>list = new JList<> () ;
list.setSelectionMode (ListSelectionModel .SINGLE_SELECTION ) ;

JScrollPane listScrollPane = new JScrollPane (list) ;

JPanel topPanel = new JPanel();
topPanel.setLayout(new BorderLayout()) ;
topPanel.add (listScrollPane, BorderLayout .CENTER ) ;

ActionListener updateButtonListener = new UpdateListAction(list) ;
updateButtonListener.actionPerformed(
new ActionEvent (list, ActionEvent .ACTION_PERFORMED , null )
) ;

JButton updateListButton = new JButton ("Update list" ) ;
JButton updateLookAndFeelButton = new JButton ("Update Look&Feel" ) ;

JPanel btnPanel = new JPanel();
btnPannel.setLayout (new BoxLayout (btnPannel, BoxLayout .LINE_AXIS ) ) ;
btnPannel.add(updateListButton) ;
btnPannel.add (Box .createHorizontalStrut (5) ) ;
btnPannel.add(updateLookAndFeelButton) ;

JPanel bottomPanel = new JPanel();
bottomPanel.add (btnPanel) ;

JPanel panel = new JPanel();
panel.setBorder (BorderFactory .createEmptyBorder (5 ,5 ,5 ,5 ) ) ;
panel.setLayout(new BorderLayout());
panel.add (topPanel, BorderLayout .CENTER ) ;
panel.add (bottomPanel, BorderLayout .SOUTH ) ;

JFrame frame = new JFrame ("Look&Feel Switcher" ) ;
frame.setMinimumSize (new Dimension (300 , 200 ) ) ;
frame.setDefaultCloseOperation (WindowConstants .EXIT_ON_CLOSE ) ;
frame.add (panel) ;
frame.pack();
frame.setVisible(true) ;

UpdateListButton.addActionListener(updateButtonListener) ;
updateLookAndFeelButton.addActionListener(
new UpdateLookAndFeelAction(frame, list)
) ;
}

Here, in general, there is nothing super complicated. Buttons, a list are created, the list is wrapped in a JScrollPane so that the list has scrolling. Controls are arranged in a frame using panels. Panels can have different layouts, here we used BorderLayout and BoxLayout . The principle is similar to that used in wxWidgets .

To respond to various events, such as button presses, classes that implement the ActionListener interface are used. The above code uses two such classes, UpdateListAction and UpdateLookAndFeelAction. As you might guess from the name, the first class is responsible for processing clicks on the left button "Update list", the second - on the right button "Update Look&Feel". ActionListeners are attached to buttons using the addActionListener method. Since we want to see the list of available Look&Feels right after the application starts, we emulate clicking on the “Update list” button. To do this, we create an instance of the ActionEvent class and pass it as an argument to the actionPerformed method of the UpdateListAction class.

The implementation of the UpdateListAction class is as follows:

static class UpdateListAction implements ActionListener(
private JList< String>list;

public UpdateListAction(JList< String>list) (
this.list = list;
}

@Override
public void actionPerformed(ActionEvent event) (
ArrayList< String>lookAndFeelList = new ArrayList<> () ;
UIManager.LookAndFeelInfo infoArray =

int lookAndFeelIndex = 0 ;
int currentLookAndFeelIndex = 0 ;
String currentLookAndFeelClassName =
UIManager .getLookAndFeel() .getClass() .getName() ;

for( UIManager.LookAndFeelInfo info : infoArray) (
if (info.getClassName() .equals(currentLookAndFeelClassName) ) (
currentLookAndFeelIndex = lookAndFeelIndex;
}
lookAndFeelList.add(info.getName()) ;
lookAndFeelIndex++;
}

String listDataArray = new String [ lookAndFeelList.size () ] ;
final String newListData =
lookAndFeelList.toArray (listDataArray) ;
final int newSelectedIndex = currentLookAndFeelIndex;

SwingUtilities .invokeLater(new Runnable()(
@Override
public void run() (
list.setListData(newListData);
list.setSelectedIndex(newSelectedIndex) ;
}
} ) ;
}
}

In the constructor, a pointer to a list is passed in which we will display the available Look & Feel. In fact, since UpdateListAction is a nested class of our main LookAndFeelSwitcher class, it has the ability to directly access the fields of the LookAndFeelSwitcher instance that created it. But the functionalist inside me resists this approach, so I decided to pass a reference to the list explicitly through the constructor.

The actionPerformed method will be called when the button is clicked. The code for this method is quite trivial - we just use the static methods of the UIManager class to get a list of available Look&Feels, as well as determine the current Look&Feel. Then the contents of the list and the item selected in it are updated. Here you need to pay attention to two points. First, every Look&Feel has name And class name are different things. We must show the user names, and when switching Look & Feel, use the class name. Second, notice how the final variables newListData and newSelectedIndex are created and then used in the anonymous class. This is the same analogue of closures, which was discussed earlier. Obviously, the use of non-final variables in closures would lead to sad consequences.

Finally, consider the UpdateLookAndFeelAction class:

static class UpdateLookAndFeelAction implements ActionListener(
private JList< String>list;
private JFrame rootFrame;

public UpdateLookAndFeelAction(JFrame frame, JList< String>list) (
this .rootFrame = frame;
this.list = list;
}

@Override
public void actionPerformed(ActionEvent e) (
String lookAndFeelName = list.getSelectedValue();
UIManager.LookAndFeelInfo infoArray =
UIManager .getInstalledLookAndFeels();

for( UIManager.LookAndFeelInfo info : infoArray) (
if (info.getName() .equals(lookAndFeelName) ) (
String message = "Look&feel was changed to " + lookAndFeelName;
try(
UIManager .setLookAndFeel (info.getClassName () ) ;
SwingUtilities .updateComponentTreeUI(rootFrame) ;
) catch (ClassNotFoundException e1) (
message = "Error: " + info.getClassName () + " not found" ;
) catch (InstantiationException e1) (
message = "Error: instantiation exception";
) catch (IllegalAccessException e1) (
message = "Error: illegal access" ;
) catch ( UnsupportedLookAndFeelException e1) (
message = "Error: unsupported look and feel";
}
JOptionPane .showMessageDialog (null , message) ;
break ;
}
}
}
}

Here we simply (1) find an L&F with the same name as the name selected in the list, (2) change the L&F using the UIManager class's static setLookAndFeel method, and (3) redraw our UI's main frame, as well as, recursively, the elements located on it, using the static updateComponentTreeUI method of the SwingUtilities class. Finally, we notify the user with a message whether everything was successful.

I would also like to say a few words about debugging GUI applications in Java, and not only GUI. First, Swing has this magic keyboard shortcut Ctr + Shift + F1, which prints to stdout information about how the controls are located. Very useful if you want to steal the UI of your competitors. Secondly, there is such an interesting hotkey Ctr + \. If you click it in the console of a running Java application, all threads and their stack traces will be displayed. Convenient if you caught a deadlock. Finally, thirdly, during the development of the GUI, it can be useful to decorate the panels in different colors. You can do it like this:

buttonsPanel.setBackground (Color .BLUE ) ;

© 2022 hecc.ru - Computer technology news