The process of developing a simple GUI program in the Java language. A quick tour of GUI frameworks for Java and my first simple GUI application in Swing How to implement a GUI using java

Historically, I have had very little to do with UI. Apparently, that is why Qt and wxWidgets are so interesting to me - everything seems new, interesting, unusual. However, since I started learning Java, today we will not talk about Qt or wxWidgets, but about Swing. Today, by joint efforts, we will write a simple GUI application in Java, with buttons, lists, and even able to change skins!

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

  • AWT(Abstract Window Toolkit) was the first GUI framework. The idea was correct - AWT uses native controls, that is, they look and physically are native, no matter where you launch your application. Unfortunately, it turned out that (1) there are few common controls for different environments, and (2) it is very difficult to write cross-platform native interfaces so that nothing crawls and does not disperse;
  • Therefore, AWT was replaced by Swing... Swing uses shapes created by AWT, on which he draws controls himself. This economy works, of course, slower, but the UI becomes much more portable. Swing offers a variety of Look & Feels for the programmer to choose from, thanks to which you can either make your application look like and behaved the same under Windows as under Linux, or so that the application is very similar to the native one regardless of where it is launched. 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 by Eclipse. Like AWT, native controls are used. SWT is not included in the JDK and uses JNI, so it does not really correspond to the Java ideology "written once, works everywhere". 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 is actively sawing in Oracle and is positioned as a quick replacement for Swing. Ideologically, JavaFX is similar to Swing, that is, controls are not native. Among the interesting features of JavaFX are hardware acceleration, GUI creation using CSS and XML (FXML), the ability to use JavaFX controls in Swing, as well as a bunch of beautiful new controls, including those for drawing diagrams and 3D. Videos with more detailed overview JavaFX is possible. Since Java 7, JavaFX is part of the JRE / JDK;
  • NetBeans Platform(not to be confused with NetBeans IDE!) - this is the kind of thing that, as I understand it, works on top of Swing and JavaFX, provides, as it were, a more convenient interface for working with them, as well as all sorts of additional controls. In one application using the NetBeans Platform, I've seen the ability to drag & drop tabs to position panels in the window in a similar way to tile window managers. Apparently, Swing itself cannot do this. Read more about the NetBeans Platform;

It is possible that there are other frameworks as well. Swing is by far the most canonical, so that's what we'll do.

Above, something was said about some Look & Feel. To better understand what in question, 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 the program.

Our application will look like this under Ubuntu:

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

As you can see, the 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 using KDE?), And under Windows - L&F Windows. It’s probably a good idea to include in your program the ability to switch between different L&Fs. On the other hand, you will have to test the operation of the application with all these L&Fs.

Let's take a look at the source code of the application. Colleagues of UI-specialists assured me that they do not use any WYSIWYG editors, and if they do, then only for rapid prototyping. Of the good WYSIWYG editors, it was called JFormDesigner. They say the code it generates even looks like human-written code, not a 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 () (
createGUI ();
}
} ) ;
}

In Swing and AWT, if we want to change something in the UI, we have to do it from the event dispatching thread. The static invokeLater method takes a class that implements the Runnable interface and calls its run () method inside the event dispatching thread. If you are not familiar with the above syntax, this is the Java way to declare a class without giving it a name. Classes without a name are said to be anonymous. Often, anonymous classes in Java serve the same role as lambda functions in functional programming languages. 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 verifies that the run () method will indeed override the Runnable interface method. Without it, when overriding a method, we can accidentally make a typo and define a new method instead of overriding an existing one. However, in this particular case, the annotation is apparently not very useful, and, perhaps, even superfluous.

As a result, 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 btnPannel = 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 (btnPannel);

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 are created, a list is 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 the same as that used by wxWidgets.

To respond to various events, for example, 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 handling clicks on the left button "Update list", the second - on right button"Update Look & Feel". ActionListeners are attached to buttons using the addActionListener method. Since immediately after launching the application, we want to see a list of available Look & Feel, 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);
}
} ) ;
}
}

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

The actionPerformed method will be called when the button is clicked. The code for this method is pretty trivial - we just use the static methods of the UIManager class to get a list of available Look & Feel, 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, these are two different things. We must show the names to the user, and use the class name when switching Look & Feel. Second, notice how the final variables newListData and newSelectedIndex are created, which are then used in the anonymous class. This is the very analogue of closures, which was discussed earlier. Obviously, using non-final variables in closures would have dire consequences.

Finally, let's take a look at 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 the L&F with a name equal to the name selected in the list, (2) change the L&F using the static setLookAndFeel method of the UIManager class, and (3) redraw the main frame of our UI, 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 if everything went well.

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

buttonsPanel.setBackground (Color .BLUE);

Advance notice

Unlike the previous lessons, where we mainly acted according to the principle of "do as I do" and talked about concepts and technical details, which is called "on the fingers", the style of presentation, starting from this lesson, will change slightly and will be more technical.

This, alas, cannot be avoided. sooner or later we will have to come to a point after which the fingertips approach becomes untenable. Now that moment is coming. So let's brace ourselves, roll up our sleeves and get started.

In the previous lesson (), we formed and displayed a graphic window on the monitor screen and along the way solved some problems related to its appearance and location. Now we will discuss what remains behind the scenes.

You probably noticed that there are two lines at the beginning of the source code:

import java.awt. *;

import javax.swing. *;

We need to linger here a little. Recall that we mentioned earlier that the Java programming environment includes many libraries for networking, graphics, database, messaging, and more. It is the libraries that deliver all the power and versatility to Java.

In Java, the term “package” is used instead of the term “library”. The above lines just connect the necessary packages to form the graphical interface. You will see later that we will use other packages as well, but for now the two are enough.

Packages include the necessary classes and interfaces (we will talk about interfaces in due time) that provide one or another functionality of the future application. The presence of an asterisk ("*") indicates that the programmer is importing the entire contents of the package, without specifying the classes or interfaces it contains. It might seem that in the case of large packages, the resulting object code might be overwhelming, but don't worry: the Java compiler is smart enough to only use what your program really needs; everything that the program does not need, the compiler will simply not include in the object code. If you want, you can use a slightly different form of connecting packages, for example,

import java.awt.Window;

import javax.swing.JFrame;

but this assumes that the programmer is already well aware of the structure and capabilities of these packages. We will use a simpler package connection form, indicated earlier.

Importing (connecting) a package to the program under development is performed by the import keyword, followed by the name of the package. Each package must be imported separately (i.e. you cannot write import java.awt. *, Javax.swing. *;). Of course, the volume of the source code increases somewhat, but only slightly. All classes and interfaces that make up your program must be placed strictly after import statements, otherwise the compiler will generate a compilation error message.

The first package (starting with java.awt) provides primarily the interaction of Java programs with the graphics subsystem operating system... Remember that Java is a cross-platform programming language and generates a single object code regardless of the operating system on which the code will run. Therefore, Java is "forced" to access the resources provided by the operating system installed on the user's computer. One of these resources is graphics (in addition to graphics, Java refers to the "services" of the operating system to access file system and other resources). So, the java.awt. * Package allows GUIs written in Java to use graphics capabilities operating system and displayed graphic objects created in Java programs on a monitor screen. We'll linger here for a bit.

All graphical components in Java fall into two categories: lightweight and heavyweight. Overwhelming majority graphic components(buttons, lists, trees, labels, tables, etc.) are lightweight. This means that the operating system knows absolutely nothing about them and does not even "suspect" about their existence. Lightweight components belong to windows (similar to what we displayed in the previous lesson) and are displayed in these windows.

If we want to display a button in the window, then we just need to define it with a code similar to the following

JButton buttonPressMe = new JButton ("Press me");

and place it in the indicated place of the window (the corresponding details will be described later). A button is a lightweight object and the operating system has no information about the button (for the operating system, the button does not exist at all). The button "knows" only the window in which it is located. The window renders the button, intercepts the events occurring with the button, redraws the button if it was obscured by something, etc. But the window itself as such - the operating system is aware of it, and precisely because the window is a heavyweight component. Only the window has access to the resources of the operating system (in particular - graphical).

Take a look at the source code. When we calculated the coordinates of the upper left corner of the window before displaying it on the screen, we needed to know the screen resolution

Dimension sSize = Toolkit.getDefaultToolkit () .getScreenSize ()

What can provide such information? The operating system and only it! Next, we used the code

try (UIManager.setLookAndFeel

(UIManager.getSystemLookAndFeelClassName ());

catch (Exception lfe) ()

so that the appearance of the window conforms to the standard accepted in a particular operating system. What can provide such information? Again - the operating system! So we can't do without the java.awt. * Package to create GUIs.

When a window is displayed on the screen, the operating system allocates to the window necessary resources(memory first of all) and the window becomes visible. Then lightweight components come into play and all further work is carried out almost exclusively with them.

The second import package (starting with javax.swing) is responsible for generating lightweight graphical interfaces (there are other packages, but this one is the most important and almost always used). We will study and master this package gradually, because it is quite large and quite complex. We will start doing this in the next lesson, but for now we will turn to next line source code:

public class MoneyForNothing extends JFrame (

Two elements are new here: the extends keyword and the JFrame word.

Keyword extends (translated as "extend", but close in meaning to the word "inherits" or to the phrase "borrow properties and behavior") expresses the fundamental concept of object-oriented programming (or more simply, programming based on classes). This concept is called inheritance. This must be dealt with thoroughly.

Remember, in one of the first lessons () we discussed the concept of classes and used a car engine as an example. With all the variety of engines and their designs (of course, we are talking about internal combustion engines), almost all of these engines are similar to each other: each of them has cylinders, pistons, crankshafts, valves, etc.

Of course, a huge diesel engine for a tanker cannot be compared with a tiny alcohol engine for an aircraft model, but they (so to speak) - albeit distant, but relatives. They have a common ancestor - some abstract engine, and the engines themselves are its descendants (even if very, very distant).

In programming, such an ancestor is usually called "parent" or "superclass", i.e. the class from which other classes descended. The superclass is named immediately after extends. Thus, translated into ordinary language, the above code snippet can be read like this: "class ... extends the JFrame class", "class ... inherits the JFrame class" or "the class ... borrows the properties and behavior of the JFrame class". The JFrame class defines the basic properties and behavior of "standard" graphics windows. The JFrame class itself is in the javax.swing. * Package, and that's what we imported at the beginning of the program.

JFrame is the parent (in Java terminology - a superclass) of graphic windows (there are also other windows, for example, dialog windows, but we'll talk about them in due time). If you turn to the Java documentation, you will find in it that the JFrame class has several constructors, fields and about two dozen methods that define the behavior of a certain "standard" window. Thus, our class named MoneyForNothing inherits from the JFrame class (usually we talk not about heirs, but about descendants or child classes).

Please note that the JFrame class itself, in turn, inherits from several classes (most of which belong to the already familiar java.awt. * Package):

In order not to return to this question, we draw your attention to the fact that at the top of the Java inheritance hierarchy are classes from the java.lang. * Package. This is the only package from the JDK that does not need to be imported explicitly — it is always imported automatically. Judging by this "ladder", the JFrame class is the great-great-great-grandson of java.lang.Object (the picture above is scientifically called the class hierarchy).

All components of the graphical user interface (abbreviated as GUI, from Graphical User Interface), which, recall, are lightweight elements, must be placed inside the main window - the JFrame inheritor. Now we have no components, but soon they will appear - we promise.

And now let's "go over" at the end of our source code which looks like this:

// Constructor

public MoneyForNothing () (

setTitle ("Welcome to Money for Nothing");

setSize (new Dimension (600, 400));

Dimension sSize = Toolkit.getDefaultToolkit () .getScreenSize (),

fSize = getSize ();

if (fSize.height> sSize.height) (fSize.height = sSize.height;)

if (fSize.width> sSize.width) (fSize.width = sSize.width;)

setLocation ((sSize.width - fSize.width) / 2,

(sSize.height - fSize.height) / 2);

setDefaultCloseOperation (EXIT_ON_CLOSE);

setVisible (true);

First of all, we set the title of the window (setTitle ("...") method). Then the horizontal and vertical dimensions of the window are set (method setSize (...)). After determining the screen resolution of the monitor, the coordinates of the upper left corner of our window are calculated and the window is displayed (setLocation (...) method) at the specified location on the screen.

After that, we define what to do when closing the window in the system menu; v this case you can see that the application should terminate its work (EXIT_ON_CLOSE). This short and simple line actually hides a surprisingly interesting and intriguing world of event handling. Let's say right away: Understanding the event handling mechanism in Java is key to developing graphical applications, and starting in the next lesson, we will do just that.

Finally, the last line (the setVisible (true) method) makes the window visible.

Of course, we have not omitted some details, but our goal is not to thoroughly tell everything and about everything - there are documentation, reference books and tutorials for this. Our goal is to give a general direction, a starting point, starting from which you (if you wish and with sufficient persistence) will be able to develop your programs in the necessary directions and fill them with the necessary functionality.

This concludes our lesson. The most important thing you have to learn from it is the concept of inheritance (borrowing). This is indeed a fundamental concept. Without it, you cannot create any complex and useful program in Java, so do not rush to move on, but once again carefully review and think about everything that we have learned so far.

Good luck and see you soon!

In this short article I want to describe the process of creating a small program that supports the GUI in the language Java... The reader is assumed to be 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 in the screenshot, the folder src does not contain anything, so we create our main class in it, containing the function main.

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

See the main class support above. We can already create a project ( Build project ) and run it ( Run ). At the bottom in the terminal of your IDE you will see a message “Hello, Govzalla!“... But as you yourself understood - it does not support 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 it to the form on the right side. JTextField, JPasswordField and JButton:

It remains to add the code and associate our form with it. When we added the shape MainWindow, the class was automatically created MainWindow, this class is the class of the generated form, i.e. it is this class that will serve all events of this form.

Although the class of our window contains the necessary elements, even now it has nothing to do with the GUI, so we will extend it with JFrame and inherit all the basic and necessary functionality of the GUI .

V this moment we have a form MainWindow and class MainWindow extended with JFrame... Now we need to define all added GUI elements as the content of the class MainWindow this.getContentPane (). add (panel); After that, the content 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 fact is that we have created a class and a form for it, but we have not created 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);))

Run the code

By clicking on the Button you will notice that the program does not react in any way. The point is that we have not yet added a listener ( Listener) for events ( Events) of the Button.

Event listener ( Event listener) JButton must be an implantation of the adapter ActionListener, so let's add the following code to the class body MainWindow:

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

Method actionPerformed() will process all the events of button1, but first, you also need to tell button1 which class to handle, so add the following code to the constructor of the MainWIndow class: 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 react 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 no event handler for the main window has been added.

Davydov Anton Valerievich
Student of TSU, Russia, Togliatti
Supervisor: Erofeeva E.A.

The user interface in Java has gone through a very thorny path of formation and development. It has long been accused of greed for system resources, slow operation and limited functionality. The advent of .NET with faster graphics components has further shaken Java's position. But such competition only spurred Java developers to develop and improve graphical libraries. And in this article we will see what came of it.

Abstract Window Toolkit

The Abstract Window Toolkit (AWT for short) was first released in 1995 by Sun Microsystems. This was the first attempt at creating a graphical interface for Java. AWT acted as a layer calling methods from libraries written in C. And these methods, in turn, used the graphical components of the operating system. On the one hand, a program built in this way looked similar to all other programs in the operating system used, but on the other hand, the same program may look completely different on different operating systems, which complicated development. In addition, for the sake of multiplatformity, it was necessary to unify the interfaces for calling components, which led to a somewhat stripped-down functionality. The set of components is also quite modest. For example, tables are missing, and icons cannot be placed in buttons. AWT tries to automatically release used resources. This affects performance and complicates the architecture. AWT is easy to learn, but writing something complex is difficult. AWT is now used primarily for applets. Oracle is currently encouraging developers to switch to Swing as more secure.

Fig. 1 - A sample program written using AWT in a Windows environment

After AWT, in 1998, Sun released Swing. It is completely written in Java and uses 2D for rendering. Swing has a lot more components than AWT. The components themselves have become much easier to create by inheriting them from existing ones. The ability to use different styles and skins was also introduced. However, the speed of the early versions of Swing was rather slow, and mistakes in the program writing could even lead to the operating system freezing.

However, due to its ease of use and abundance of documentation, Swing has become the most popular GUI in Java. Many extensions have emerged from it, such as SwingX and JGoodies, that make it even easier to create visually complex applications. All modern Java programming environments include graphic editor Swing. Even though more modern frameworks exist now, Swing remains the most popular.


Figure 2 - Sample Swing Program

Standard Widget Toolkit

SWT was released by IBM at a time when Swing was still slow, and mainly to promote the Eclipse programming environment. Like AWT, SWT uses OS components, but different interfaces are used for different platforms. Therefore, a separate JAR library must be shipped for each operating system. This allows you to more fully use the functions corresponding to different operating systems. And the missing components were implemented using 2D. However, SWT is more difficult to master than Swing. In addition, the programmer must implement the release of resources by the application himself.

Figure 3 - Sample Swing Program

JavaFX was released in 2008 by Oracle. It is positioned as a platform for creating a rich Internet application. For rendering, a graphics pipeline is used, which significantly speeds up the application. There is a large set of built-in components. There are also separate components for charting. Support for multimedia content, animation and even multi-touch has been implemented. The appearance of the components is customizable using CSS styles. In addition, the set of JavaFX utilities includes the ability to make a native installer for the most popular platforms: exe or msi for Windows, deb or rpm for Linux, dmg for Mac. The Oracle website has detailed documentation and big number ready-made examples.

Thus, having described the main features and disadvantages of the above graphical user interfaces, we can decide for which tasks they are better suited. The Abstract Window Toolkit is more suitable for creating applets. For a beginner, we can recommend Swing in view of the fact that you can find a huge amount of documentation for it on the Internet, including in Russian. JavaFX is perfect for building rich Internet applications.

List of sources used

    Ryzhenko AV Object-oriented programming: Educational-methodical complex for the discipline for the specialty 010501 - "Applied Mathematics and Informatics". - 2007.

    Khabibullin I. Sh. Java 7 (4th ed.). - BHV-Petersburg, 2012.

    Clarke J., Connors J., Bruno E. J. JavaFX: Developing Rich Internet Applications. - Pearson Education, 2009.

    Northover S., Wilson M. Swt: the standard widget toolkit, volume 1. - Addison Wesley Professional, 2004.

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 graphics components has further shaken Java's position. But there is 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 creating a graphical interface for Java. They took the easy route and just made a Java layer that calls methods from libraries written in C. Library methods create and use graphical components of the operating environment. On the one hand, this is good, since a Java program is similar to other programs in 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 multiplatformity, it was necessary to unify the interfaces of the component calls, which is why their functionality turned out to be a little curtailed. And the set of components turned out to be quite small. For example, AWT does not have tables, and buttons do not support displaying icons.

AWT tries to free used resources automatically. This complicates the architecture a bit and affects performance. It's quite easy to master AWT, but writing something complex will be somewhat difficult. Nowadays it is used only for applets.

Advantages:

  • part of the JDK;
  • speed of work;
  • the graphic 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, are absent 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 has hidden the tutorials and is strongly encouraging the move to Swing. This is understandable, direct access to axle components can be a serious security hole.

Swing


Following AWT, Sun has developed a set of graphical components called Swing. Swing components are written entirely in Java. 2D is used for rendering, which brought with it several advantages at once. The set of standard components significantly surpasses AWT in terms of variety and functionality. It became easy to create new components by inheriting from existing ones and drawing whatever your heart desires. Support for various styles and skins is now possible. At the same time, 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 the flexibility of its 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 answers on Swing on the forums. All problems, especially for beginners, are well known to Google;
  • built-in form editor in almost all development environments;
  • there are many SwingX extensions based on swing;
  • 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 lived, Swing lived, Swing lived. While Oracle strives to promote JavaFX, Swing remains the most popular framework for building user interfaces in Java today.

Standard Widget Toolkit


How
looks like
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 everyone new system you will have to ship a separate JAR library with the correct SWT version. This allowed for more complete use of existing component functions on each axis. The missing features and components were implemented in 2D, just like in Swing. SWT has many adherents, but, in all honesty, one cannot but agree that everything turned out not as simple as we would like. A beginner will have to spend much more time learning SWT than acquaintance with Swing. In addition, SWT assigns the task of freeing resources to 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 - higher speed;
  • Eclipse provides visual editor forms;
  • extensive documentation and many examples;
  • it is possible to use AWT and Swing components.

Disadvantages:

  • a separate library must be supplied for each platform;
  • you need to monitor the use of resources all the time and release them in time;
  • a complex architecture that evokes suicidal thoughts after futile attempts to implement a custom interface.

Conclusion:

It can be seen that IBM tried hard. But it turned out very much for an amateur ...

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. Support for multimedia content, a variety of display effects, animation and even multitouch has been implemented. 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 an easy and enjoyable experience.

Advantages:

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

Disadvantages:

  • the framework is still being developed, so there are crashes and some glitches;
  • JavaFX is not yet widely adopted.

Conclusion:

Good job Oracle. The framework leaves only positive impressions. It's easy to figure out, the methods and interfaces look logical. I want to use it 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 displaying various information.

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

Any SWT program starts by creating 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 an equally important Shell object. Shell is a normal operating system window. Display is passed to the shell constructor to create the 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 specified the NO_TRIM flag. For the background we will use a picture - a rectangle with rounded corners. Basically, the SWT window can take any shape. To achieve this effect, we use the Region class. All you need is to add to this class all visible points from the background image, skipping transparent ones.

Load the picture:

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

In images of different formats, transparency is set in different ways, therefore, information about transparent areas is also not extracted in the same way. Create a background area and add all visible points 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 the 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 keystrokes 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 window area, remember the coordinates of the click. When moving the mouse with the left key pressed, move the window on the screen according to the movement. On a redraw event, we draw a background image using the GC graphics context.

Let's 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 equal to the image size:

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

Open the window and start the event loop:

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

Do not 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 by 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 graphic components in the window in the desired view. The layout manager is concerned with not only positioning components, but also resizing them when the window is resized. For our widget, we will use a GridLayout. This manager arranges components in the cells of an imaginary table. Create a GridBagLayout for two columns with different column widths (flag false in the constructor), set it as the window layout manager:

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

For the status picture, we use the Label component. We pass the window object as a parent. The second parameter can be set the style of the component. For each component, the set of possible style flags is different; they can be viewed 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));

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 transparent background components, and behind the status picture there will be a white background, which, of course, we would not want. Therefore, 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 also needs to be cleaned up by calling the dispose method. We set the background color and the status image, which can be loaded from the 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 with the service characters \ u is used.

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, must be freed. To do this, we will 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 ("Partly cloudy, light rain"); descriptionLabel.setBackground (bgColor); descriptionLabel.setForeground (display.getSystemColor (SWT.COLOR_WHITE));

The text can be quite long, so when creating a label, we specify the WRAP flag so that the text is automatically split into several lines if there is not enough space. Center the component and let it fill all the horizontal space. We also indicate that the component occupies two columns of the layout table. Launch and get a window from the "Weather Widget" picture.

Now you can add some weather service, create a timer for automatic update- 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 Swing's standard window functionality is called JFrame. By default, closing an application window in Swing does not stop the program, so it's better to write 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, a model provides data, a view is responsible for displaying data (e.g. text, input fields), and a controller provides interaction between the model and the view. The table demonstrates this approach well. To represent data, a class is used that implements the TableModel interface.

To store information about available news, let's create a FeedMessage class with fields for the title of the article and the 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 the 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 informs the view that the data model has changed and needs to be redrawn.

Create a table and slightly change its appearance 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 the column names:

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

Now let's get down to appearance cells. Swing allows you to assign separate view classes for different types data. A 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 for data of type String. Let's change the default font color and make the background 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, 0x ); 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, you 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 the design of the widget:

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

Add a scrolling component to the main window pane. The second argument can be passed the location of the component. By default, the main window pane uses the BorderLayout layout manager, which arranges components to cardinal points. Place the scrolling table in the center.

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

Just like last time, let's 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, color and font objects are freed 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, as if the window is resized, the window's shape will be recalculated correctly:

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

Set the window size, remove the border 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 finish loading data in a separate stream, and we will get such a widget with the latest news from your favorite magazine :).


JavaFX: Listen to the Music

Finally, JavaFX is the highlight of the season. Let's take advantage of its multimedia capabilities and its 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 life cycle applications. Form components are created in the start method, which takes the Stage class as an argument. Stage is a program window. Change the window style to TRANSPARENT to remove the border and buttons. The Stage contains the Scene class, which sets the window size and background color. In Scene, in turn, we pass the Group class, into which we will place the 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"));

We fill 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 rounded rectangle to give the widget a proper 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);

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

Load the song into the player:

File file = new File ("release me from 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 start the song:

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

Launch the application:

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

And we enjoy this beauty.