Ebooks

Kotlin Swing tutorial

Kotlin Swing tutorial shows how to create Swing GUI applications in Kotlin. The code examples and image are available at the author's Github Kotlin-Swing repository.

Swing is the principal GUI toolkit for the Java programming language. It is a part of the JFC (Java Foundation Classes), which is an API for providing a graphical user interface for Java programs.

Kotlin Swing JFrame

In the first example, we show a basic window on the screen. The main application window is created with JFrame.

KotlinSwingSimpleEx.kt
package com.zetcode

import java.awt.EventQueue
import javax.swing.*

class KotlinSwingSimpleEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        setTitle(title)

        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(300, 200)
        setLocationRelativeTo(null)
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingSimpleEx("Simple")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    EventQueue.invokeLater(::createAndShowGUI)
}

The window is placed in the center of the screen.

class KotlinSwingSimpleEx(title: String) : JFrame() {

KotlinSwingSimpleEx inherits from the JFrame component. JFrame is a top-level container. The purpose of containers is to hold components of the application.

setTitle(title)

The title of the application is set with setTitle().

defaultCloseOperation = JFrame.EXIT_ON_CLOSE

The defaultCloseOperation sets the operation that will happen by default when closes the frame. In our case, we exit the application.

setSize(300, 200)

We set the initial size with setSize().

setLocationRelativeTo(null)

This line is used to center the window on the screen.

val frame = KotlinSwingSimpleEx("Simple")
frame.isVisible = true

We create the application frame and show it on screen.

EventQueue.invokeLater(::createAndShowGUI)

The invokeLater() method places the application on the Swing Event Queue. It is used to ensure that all UI updates are concurrency-safe.

Simple example
Figure: Simple example

Kotlin Swing JButton

In the next example, we have a close button. A button component is created with JButton. When we click on the button, the application terminates.

KotlinSwingCloseButtonEx.kt
package com.zetcode

import java.awt.EventQueue
import javax.swing.*

class KotlinSwingCloseButtonEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        setTitle(title)

        val closeBtn = JButton("Close")

        closeBtn.addActionListener { System.exit(0) }

        createLayout(closeBtn)

        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(300, 200)
        setLocationRelativeTo(null)
    }


    private fun createLayout(vararg arg: JComponent) {

        val gl = GroupLayout(contentPane)
        contentPane.layout = gl

        gl.autoCreateContainerGaps = true

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
        )

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
        )

        pack()
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingCloseButtonEx("Close button")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    EventQueue.invokeLater(::createAndShowGUI)
}

We position a JButton on the window and add an action listener to this button.

val closeBtn = JButton("Close")

A button component is created. The constructor takes a string label as a parameter.

closeBtn.addActionListener { System.exit(0) }

An action listener is added to the button with addActionListener(). The action terminates the application by calling the System.exit() method.

createLayout(closeBtn)

The child components, in our case one button, need to be placed into containers. We delegate the task to the createLayout() method.

val gl = GroupLayout(contentPane)
contentPane.layout = gl

The content pane of a JFrame is an area where child components are placed. The children are organised by specialised non-visible components called layout managers. BorderLayout is the default layout manager of a content pane. This manager is very simple and is useful only in limited cases. We use the GroupLayout manager which is more powerful and flexible.

gl.autoCreateContainerGaps = true

The autoCreateContainerGaps attribute creates gaps between components and the edges of the container. Space or gaps are important part of the design of each application.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addComponent(arg[0])
)

gl.setVerticalGroup(gl.createSequentialGroup()
        .addComponent(arg[0])
)

GroupLayout manager defines the layout for each dimension independently. In one step, we lay out components alongside the horizontal axis; in the other step, we lay out components along the vertical axis. In both kinds of layouts we can arrange components sequentially or in parallel. In a horizontal layout, a row of components is called a sequential group and a column of components is called a parallel group. In a vertical layout, a column of components is called a sequential group and a row of components a parallel group.

In our example we have only one button, so the layout is very simple. For each dimension, we call the addComponent() method with the button component as a parameter. (Each child component must be added for both dimensions.)

Close button
Figure: Close button

Kotlin Swing JLabel

JLabel component is used to display text, colours, or images.

KotlinSwingStandardColoursEx.kt
package com.zetcode

import java.awt.Color
import java.awt.Dimension
import java.awt.EventQueue
import javax.swing.GroupLayout
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.SwingConstants.LEADING


class KotlinSwingStandardColoursEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        val stdCols = arrayOf<Color>(Color.black, Color.blue, Color.cyan, 
            Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, 
            Color.orange, Color.pink, Color.red, Color.white, Color.yellow)

        val labels = stdCols.map {
            JLabel("", null, LEADING).apply {

                minimumSize = Dimension(90, 40)
                background = it
                isOpaque = true
            }
        }

        createLayout(labels)

        setTitle(title)
        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setLocationRelativeTo(null)
    }

    private fun createLayout(labels: List<JLabel>) {

        val gl = GroupLayout(contentPane)
        contentPane.layout = gl

        gl.autoCreateContainerGaps = true
        gl.autoCreateGaps = true


        gl.setHorizontalGroup(gl.createParallelGroup()
                .addGroup(gl.createSequentialGroup()
                        .addComponent(labels[0])
                        .addComponent(labels[1])
                        .addComponent(labels[2])
                        .addComponent(labels[3]))
                .addGroup(gl.createSequentialGroup()
                        .addComponent(labels[4])
                        .addComponent(labels[5])
                        .addComponent(labels[6])
                        .addComponent(labels[7]))
                .addGroup(gl.createSequentialGroup()
                        .addComponent(labels[8])
                        .addComponent(labels[9])
                        .addComponent(labels[10])
                        .addComponent(labels[11]))
                .addComponent(labels[12])
        )

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(labels[0])
                        .addComponent(labels[1])
                        .addComponent(labels[2])
                        .addComponent(labels[3]))
                .addGroup(gl.createParallelGroup()
                        .addComponent(labels[4])
                        .addComponent(labels[5])
                        .addComponent(labels[6])
                        .addComponent(labels[7]))
                .addGroup(gl.createParallelGroup()
                        .addComponent(labels[8])
                        .addComponent(labels[9])
                        .addComponent(labels[10])
                        .addComponent(labels[11]))
                .addComponent(labels[12])
        )

        pack()
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingStandardColoursEx("Standard colours")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    EventQueue.invokeLater(::createAndShowGUI)
}

The example shows thirteen JLabel components; each of the labels has a different background colour. JLabel is usually used to display text; but it can display colours, too.

val stdCols = arrayOf<Color>(Color.black, Color.blue, Color.cyan, 
    Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, 
    Color.orange, Color.pink, Color.red, Color.white, Color.yellow)

Here we have an array of the built-in colour values.

val labels = stdCols.map {
    JLabel("", null, LEADING).apply {

        minimumSize = Dimension(90, 40)
        background = it
        isOpaque = true
    }
}

The labels are created with a certain size and background colour.

Standard colours
Figure: Standard colours

JCheckBox

JCheckBox is a box with a label that has two states: on and off. If the check box is selected, it is represented by a tick in a box. A check box can be used to show or hide a splashscreen at startup, toggle visibility of a toolbar etc.

With JCheckBox it is possible to use an ActionListener or an ItemListener. Usually the latter option is used. ItemListener is the interface for receiving item events. The class that is interested in processing an item event, e.g. the observer, implements this interface. The observer object is registered with a component using the component's addItemListener() method. When an item selection event occurs, the observer's itemStateChanged() method is invoked.

KotlinSwingCheckBoxEx.kt
package com.zetcode

import java.awt.EventQueue
import java.awt.event.ItemEvent
import javax.swing.GroupLayout
import javax.swing.JCheckBox
import javax.swing.JComponent
import javax.swing.JFrame

class KotlinSwingCheckBoxEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        setTitle(title)

        val checkBox = JCheckBox("Show title", true)

        checkBox.addItemListener { e ->
            val sel: Int = e.stateChange
            if (sel == ItemEvent.SELECTED) {
                setTitle("JCheckBox")
            } else {
                setTitle("")
            }
        }

        createLayout(checkBox)

        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(300, 200)
        setLocationRelativeTo(null)
    }

    private fun createLayout(vararg arg: JComponent) {

        val gl = GroupLayout(contentPane)
        contentPane.layout = gl

        gl.autoCreateContainerGaps = true

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
        )

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
        )

        pack()
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingCheckBoxEx("JCheckBox")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    EventQueue.invokeLater(::createAndShowGUI)
}

Our code example shows or hides the title of the window depending whether the check box is selected.

val checkBox = JCheckBox("Show title", true)

JCheckBox is created. This constructor takes a text and the state of the check box as parameters. The check box is initially selected.

checkBox.addItemListener { e ->
    val sel: Int = e.stateChange
    if (sel == ItemEvent.SELECTED) {
        setTitle("JCheckBox")
    } else {
        setTitle("")
    }
}

An item listener is added with addItemListener(). Depending on the state of the check box, we show or hide the window title.

JCheckBox
Figure: JCheckBox

Kotlin Swing icons

In the next example, we show icons in a JLabel component.

KotlinSwingLabelIconEx.kt
package com.zetcode

import javax.swing.*

class KotlinSwingLabelIconEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        val lbl1 = JLabel(ImageIcon("src/main/resources/cpu.png"))
        val lbl2 = JLabel(ImageIcon("src/main/resources/drive.png"))
        val lbl3 = JLabel(ImageIcon("src/main/resources/laptop.png"))
        val lbl4 = JLabel(ImageIcon("src/main/resources/player.png"))

        createLayout(lbl1, lbl2, lbl3, lbl4)

        setTitle(title)
        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setLocationRelativeTo(null)
    }

    private fun createLayout(vararg arg: JComponent) {

        val gl = GroupLayout(contentPane)
        contentPane.layout = gl

        gl.autoCreateContainerGaps = true
        gl.autoCreateGaps = true

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
                .addComponent(arg[1])
                .addComponent(arg[2])
                .addComponent(arg[3])
        )

        gl.setVerticalGroup(gl.createParallelGroup()
                .addComponent(arg[0])
                .addComponent(arg[1])
                .addComponent(arg[2])
                .addComponent(arg[3])
        )

        pack()
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingLabelIconEx("Icons")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    SwingUtilities.invokeLater(::createAndShowGUI)
}

The example shows four images in one row.

val lbl1 = JLabel(ImageIcon("src/main/resources/cpu.png"))

The image path is passed to the ImageIcon.

ImageIcon
Figure: ImageIcon

Kotlin Swing menu example

JMenuBar implements a menu bar. JMenu implements a menu, which is a popup window containing JMenuItems that is displayed when the user selects an item on the JMenuBar. JMenuItem implements an item in a menu. It is selected by the user to perform an action.

KotlinSwingSimpleMenuExEx.kt
package com.zetcode

import java.awt.EventQueue
import java.awt.event.ActionEvent
import java.awt.event.KeyEvent
import javax.swing.*


class KotlinSwingSimpleMenuExEx(title: String) : JFrame() {

    init {
        createUI(title)
    }

    private fun createUI(title: String) {

        setTitle(title)

        createMenuBar()

        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(300, 200)
        setLocationRelativeTo(null)
    }

    private fun createMenuBar() {

        val menubar = JMenuBar()
        val icon = ImageIcon("src/main/resources/exit.png")

        val file = JMenu("File")
        file.mnemonic = KeyEvent.VK_F

        val eMenuItem = JMenuItem("Exit", icon)
        eMenuItem.mnemonic = KeyEvent.VK_E
        eMenuItem.toolTipText = "Exit application"
        eMenuItem.addActionListener { _: ActionEvent -> System.exit(0) }

        file.add(eMenuItem)
        menubar.add(file)

        jMenuBar = menubar
    }
}

private fun createAndShowGUI() {

    val frame = KotlinSwingSimpleMenuExEx("Simple menu")
    frame.isVisible = true
}

fun main(args: Array<String>) {
    EventQueue.invokeLater(::createAndShowGUI)
}

The example creates a simple menu with one menu item. Selecting the Exit menu item we close the application.

val menubar = JMenuBar()

A menubar is created with JMenuBar.

val icon = ImageIcon("src/main/resources/exit.png")

An Exit icon is displayed in the menu.

val file = JMenu("File")
file.mnemonic = KeyEvent.VK_F

A menu object is created with the JMenu class. The menus can be accessed via keyboard as well. To bind a menu to a particular key, we use the setMnemonic() method. In our case, the menu can be opened with the Alt+F+E.

eMenuItem.toolTipText = "Exit application"

This code line creates a tooltip for the menu item.

eMenuItem.addActionListener { _: ActionEvent -> System.exit(0) }

JMenuItem is a special kind of a button component. We add an action listener to it; it terminates the application when selected.

file.add(eMenuItem)
menubar.add(file)

The menu item is added to the menu object and the menu object is inserted into the menubar.

jMenuBar = menubar

The menubar is set to the jMenubar property of the JFrame.

Simple menu
Figure: Simple menu

In this tutorial, we have introduced Swings toolkit with Kotlin language. You might also be interested in the related tutorials: Kotlin Hello World tutorial, Kotlin control flow, Kotlin read file tutorial, and Kotlin write file tutorial.