Scala Swing Components and Basic Layout example

After having completed a basic Scala Swing example in this post, we continue by adding a basic reactive functionality similar to its Java Swing counterpart presented in this post:

object HelloWorld extends SimpleSwingApplication {

  def top = new MainFrame {

    val button = new Button("Click me!")
    val textArea = new TextArea {
      listenTo(button)
      reactions += { case ButtonClicked(_)  => text = text.concat("Button Clicked!\n") }
    }

    contents = new BorderPanel{
      layout(button) = BorderPanel.Position.North
      layout(textArea) = BorderPanel.Position.South
    }

    title = "Hello, World!"
    this.peer.setSize(200,100)
    this.peer.setLocationRelativeTo(null)

  }
}

Notes:
– The TextArea component listensTo the Button as well as defines the reactive functionality by adding case function to the reactions.
– The BorderPanel represents the BorderLayout and is added under the contents variable.
– Lastly, as also mentioned in the previous article, by calling peer the MainFrame has access to the methods of the underlying JFrame.

Scala Swing smallest possible application

In comparison to the smallest possible Java Swing application posted before here, this is the Scala way of doing it. Scala is a modular language that is not shipping with one big jdk like Java does but with rather independent jars per functionality like scala-swing, scala-actors. The scala-swing is a light wrapper on Java Swing providing Scala-esque feel to it.

import swing._

object HelloWorld extends SimpleSwingApplication {
  def top = new MainFrame {
    title = "Hello, World!"
    this.peer.setSize(200,100)
    this.peer.setLocationRelativeTo(null)
  }
}

Things to note:
– We don’t need to start on independent thread or start with main method. The provided SimpleSwingApplication is doing all these for us.
MainFrame is a wrapper around JFrame
– By calling peer we are getting access on the methods of the underlying JFrame.
– Notice that we don’t have to set visible the frame or shutdown the application on close because it is done for us from the underlying MainFrame.

Java Swing Scrollbar

This is a quick demo on how to add a Java Swing scrollbar following the footpaths of this previous example if you need detailed explanation on the Swing construct so far.

Without further ado the code looks like so:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SwingScrollBarDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MyFrame("Hello Swing World!");
            }
        });
    }
}

//Branching out of a JPanel to create our more complicated
//component which for now will only serve as a wrapper around
//JTextArea, with its own layout and its scrollable functionality
class MyTextComponent extends JPanel {

    //state of our custom text
    private final JTextArea text = new JTextArea();

    MyTextComponent(){
       //this component has a layout in its own right
       setLayout(new BorderLayout());
       add(getScrollable(), BorderLayout.CENTER);
    }

    //A fancy way to confine in a method the sole purpose
    //of this whole exercise: to wrap JTextArea around a
    //JScrollPane, serving when we are adding the
    //component into the panel
    private JScrollPane getScrollable(){
        //this is where the magic happens wrapping JScrollPane
        //around JTextArea
        return new JScrollPane(this.text);
    }

    void append(String text){
        this.text.append(text);
        this.text.append("\n");
    }
}

//Pay attention on that we are implementing ActionListener
//More comments to follow below on that
class MyFrame extends JFrame implements ActionListener {

    //state of the frame: a button and some text
    private final JButton button = new JButton("Click me!");
    private final MyTextComponent text = new MyTextComponent();

    MyFrame(String title){
        //boilerplate begins
        super(title);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200,300);
        setLocationRelativeTo(null);
        //boilerplate ends

        //Setting an easy Layout that is positioning components based
        //on intuitive compass signs
        setLayout(new BorderLayout());

        //Pay attention that the class is implementing ActionListener
        //therefore the button is adding this to its action listener.
        //This practically means every time the button is clicked
        //the overriden actionPerformed method of this class will be
        //called and will append some text to the text component
        button.addActionListener(this);

        //Last but not least adding the two components into the
        //frame passing as well as a second parameter their
        //position inside the layout
        add(text, BorderLayout.CENTER);
        add(button, BorderLayout.SOUTH);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        //If we are here it's because the button was clicked
        //therefore it's time to do something exciting with the
        //text component
        text.append("Button Clicked!");
    }
}
  • All we are doing in this example following incrementally this previous example so far (you can follow the comments if you want to skip reading the article) is to add a separate class to wrap around a JTextArea component.
  • The sole state of the new class is the JTextArea and in its constructor includes a separate BorderLayout. Before adding the JTextArea into its own layout it is wrapping it around JScrollPane which gives the effect of horizontal/vertical side scrolls effect into our text area inside of the broader frame.

Java Swing Components and Layout basic example

This is a basic example demonstrating a Swing frame with two components and a basic interaction between them.

You might also want to take a look at the bare minimum Swing app that is demonstrating all the boilerplate needed code demonstrated in this article.

The source code looks like this:


import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class BasicComponentsLayouts {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new InnerFrame("Hello Swing World!");
            }
        });
    }
}

//Pay attention on that we are implementing ActionListener
//More comments to follow below on that
class InnerFrame extends JFrame implements ActionListener {

    //state of the frame: a button and some text
    private JButton button = new JButton("Click me!");
    private JTextArea text = new JTextArea();

    InnerFrame(String title){
        //boilerplate begins
        super(title);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200,300);
        setLocationRelativeTo(null);
        //boilerplate ends

        //Setting an easy Layout that is positioning components based
        //on intuitive compass signs
        setLayout(new BorderLayout());

        //Pay attention that the class is implementing ActionListener
        //therefore the button is adding this to its action listener.
        //This practically means every time the button is clicked
        //the overriden actionPerformed method of this class will be
        //called and will append some text to the text component
        button.addActionListener(this);

        //Last but not least adding the two components into the
        //frame passing as well as a second parameter their
        //position inside the layout
        add(text, BorderLayout.NORTH);
        add(button, BorderLayout.SOUTH);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
       //If we are here it's because the button was clicked
       //therefore it's time to do something exciting with the
       //text component
       text.append("Button Clicked!");
       text.append("\n");
    }
}

Some comments:

  • This time we have a state in our frame, namely a button and a text area
  • Pay attention that our JFrame child is implementing ActionListener. This interface brings us the actionPerformed() overriden method that fires when this frame is used as a listener somewhere.
  • Other than the ceremonial code of frame visibility etc we have the setLayout() method where we define a nice basic BorderLayout(). For more information on layouts please have a look at this useful link.
  • Next stop is where we are adding our frame using its identity as ActionListener (we are implementing this interface remember?) to the button component. This effectively means that when the button gets clicked the ActionListener overriden method will fire.
  • Last piece of the puzzle before the end of the frame constructor and effectively completing the initialisation it’s the addition of both components to the frame using BorderLayout compass points depicted through static intuitive fields.
  • Finally we have the sole method of ActionListener which gets overriden in the Frame child class where we are not using its parameter but rather grabbing the text field (part of the frame state remember?) and appending a basic message that will reflect in a text string every time the listener gets invoked.

Java Swing basic example

This is the bare minimum that we need in order to have a Java Swing application up and running.

The code looks like this:

import javax.swing.*;

public class BasicFirstExample {

    public static void  main(String ... args){
        //Avoiding threading issues by invoking the frame through an anonymous
        //inner class via the static Swing Utilities method as follows
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                //Initialization the frame with a title as parameter
                //in the constructor
                JFrame myFirstFrame = new JFrame("Hello Frame World!");
                
                //Here comes the boilerplate code:
                
                //Setting the frame as visible. If it was not there
                //we wouldn't see the frame
                myFirstFrame.setVisible(true);
                //Setting the close-down of the application upon clicking the
                //close button in the swing frame. If it was not there, the JVM
                //would be holding on there even after closing down the swing through the button
                myFirstFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //Setting the size of the frame according to our needs. If this
                //wasn't there the default width is non-existant and the length barely visible
                //for a couple of chars of title
                myFirstFrame.setSize(200,100);
                //Setting the default position of the frame in the center of the screen.
                //If that wasn't there it will be initialized at the top left of the screen
                myFirstFrame.setLocationRelativeTo(null);
            }
        });
    }
}

Things to note:

  • We are first creating a JFrame instance call the constructor with a String as a parameter representing it’s title bar inscription.
  • We are are then calling setVisible(true) on the instance because surprisingly enough by default the visibility is set to false then non-visible.
  • We are then setting setDefaultCloseOperation() method because if we are not then after we close the little swing app from the close button the java application will still remain alive and running although counter-intuitive.
  • We are then setting setSize() because the default values are barely living any space for the title and almost non width space.
  • We are then calling setLocalisationRelativeTo() because otherwise our little Swing app will reside on the top left corner of our screen by default. Calling this method with null parameter will initialise it in the center of the screen.
  • All of the above are found in the body of an anonymous inner Runnable#run() call that is passed as a parameter inside the static SwingUtilities#invokeLater() method as per the suggestions of Sun in order for us not to run into unwanted exceptions following the rule of thumb of not setting state to components from within threads we are instantiating.

Performing a bit of housekeeping, namely including all the JFrame specific actions on a separate child class we could deduce the clutter inside the Runnable#run() method and extract it on the constructor of the child JFrame class. Besides all these actions we’ve seen (setting visibility, setting size, setting exit on close, setting relative position) are integral parts of the initialization of the frame and this makes total sense to include them within the constructor:

import javax.swing.*;

public class BasicFirstExample2 {

    public static void main(String ... args) {
         SwingUtilities.invokeLater(new Runnable() {
             @Override
             public void run() {
                 new MyFrame("Hello Frame World!");
             }
         });
    }

}

class MyFrame extends JFrame{

    MyFrame(String title) {
        super(title);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200,100);
        setLocationRelativeTo(null);
    }
}