Page 1 of 1

How to Approach Swing for Beginners Rate Topic: ***** 3 Votes

#1 macosxnerd101  Icon User is online

  • Self-Trained Economist
  • member icon




Reputation: 10803
  • View blog
  • Posts: 40,262
  • Joined: 27-December 08

Posted 26 May 2012 - 02:24 PM

The purpose of this tutorial is to introduce GUI Programming in Java, as well as discuss how to develop GUI components in Java. Far too often, beginners jump into GUI Programming without a solid background in Object-Oriented Programming and get bogged down with the bulkiness of the Swing API. This leads to procedural and bulky code.

The first strategy when working with Swing is to keep things simple. There are a bunch of different JComponents in Swing, and many in AWT. Pick only those needed, and understand their basic purposes. Yes- they have many methods inherited from parent classes. However, think back to abstraction and encapsulation. It isn't important to understand the inner workings of each JComponent, only how to work with them as needed. In other words, don't get bogged down with the thousand methods the JFrame class has. Only worry about the five or six you actually need to work with.

The second strategy is to use inheritance where appropriate. Extending certain GUI Components allows for cleaner organization of code. Let's take a look at a less OO use of GUI Components to create a simple JFrame:
public class MyDemoGUI{

    private JFrame frame;
    private JPanel panel;
    private JButton button;

    public MyDemoGUI(){
        frame = new JFrame();
        frame.setSize(400,400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        panel = new JPanel();

        button = new JButton("Click me");
        button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e){
                JOptionPane.showMessageDialog(null, "You clicked the JButton");
            }
        });

        panel.add(button);
        frame.add(panel);
        frame.setVisible(true);
    }
}



On a first glance, this isn't too ugly. However, it presents some problems. First, we don't have direct access to the JFrame. So what happens when we want to hide this JFrame based on the results of another JFrame? Yes, getter and setter methods can be used. However, inheritance is useful here to provide direct access to the JFrame without use of an Object to act as a middle man. Let's look at the refactored code:
public class MyDemoGUI extends JFrame{

    private JPanel panel;
    private JButton button;

    public MyDemoGUI(){
        this.setSize(400,400);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        panel = new JPanel();

        button = new JButton("Click me");
        button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e){
                JOptionPane.showMessageDialog(null, "You clicked the JButton");
            }
        });

        panel.add(button);
        this.add(panel);
        this.setVisible(true);
    }
}



The other case I want to make for inheritance is for clean organization of the code base. A common mistake made when developing GUIs is to customize multiple Components significantly in one class. This leaves a single class of significant length that is difficult to maintain. Creating methods to create these Components and initialize them is a common way to organize them. A rule of thumb is that if a Component warrants its own method to initialize it, then it warrants its own class.

The benefit of this is twofold. First, it makes it easier to find the code for the specific Component when modifying it. The second is that it reduces the amount of code used in the other GUI classes. Consider the following class:
public class MyFrame extends JFrame{

    private JPanel mainPanel, northPanel, southPanel, eastPanel, westPanel;
    private JButton clickMe, displayDialog;
    private JLabel northRegion, southRegion, eastRegion, westRegion;

    public MyFrame(){
        this.setSize(400,400);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());

        northPanel = new JPanel();

        clickMe = new JButton("Click Me");
        clickMe.addActionListener(new ActionListener(){
           
           public void actionPerformed(ActionEvent e){
               JOptionPane.showMessageDialog(null, "I've been clicked");
           }
        });

        northRegion = new JLabel("North Region");
        northPanel.add(clickMe);
        northPanel.add(northRegion);
          
        mainPanel.add(northPanel, BorderLayout.NORTH);

        southPanel = new JPanel();
        southRegion = new JLabel("South Region");
        southPanel.add(southRegion);
        mainPanel.add(southRegion, BorderLayout.SOUTH);

        eastPanel = new JPanel();
        
        displayDialog = new JButton("Display Dialog");
        displayDialog.addActionListener(new ActionListener(){
            
            public void actionPerformed(ActionEvent e){
                JOptionPane.showMessageDialog(null, "Displaying the dialog");
            }
        });

        eastRegion = new JLabel("East region");
        eastPanel.add(displayDialog);
        eastPanel.add(eastRegion);
        mainPanel.add(eastRegion, BorderLayout.EAST);      

        westPanel = new JPanel();
        westRegion = new JLabel("West Region");
        westPanel.add(westRegion);
        mainPanel.add(westRegion, BorderLayout.WEST);
       
        this.add(mainPanel);
        this.setVisible(true);
    }
}



At a glance, this code doesn't look too troublesome to work with. As this JFrame continues to be developed, it gets more difficult to cleanly add on to each JPanel for the BorderLayout regions. While the JPanels can be compartmentalized better in the class with helper methods to initialize them, the helper methods themselves can grow quite bulky. For this reason, it is better to create subclasses for the JPanels if they will contain more than a small handful of JComponents. While a few JComponents on each JPanel isn't a pain to maintain, having a bunch of JComponents, listener code, and possibly custom painting creates a code-base more warranted for its own class. Plus, it makes each JPanel more modular and reusable.

Conclusion- This tutorial is far from comprehensive, as the Java GUI is far too bulky for any single tutorial. It also does not thoroughly cover the GUI Components used, as many other tutorials do. However, this tutorial should be used when learning GUI Programming to help when organizing one's code so as not to get overwhelmed by the Swing API.

Is This A Good Question/Topic? 4
  • +

Replies To: How to Approach Swing for Beginners

#2 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 2069
  • View blog
  • Posts: 4,307
  • Joined: 11-December 07

Posted 26 May 2012 - 04:29 PM

Interestingly, I find your first example cleaner and more OO than the refactored alternative you provide. Sure, the first example needs a getter (not a setter) but by using inheritance you are breaking encapsulation. The biggest problem I find by breaking encapsulation in this way is in lumping together all the methods from the JFrame class (and its parents) with your new methods. This can make it more difficult to use IDEs with auto-suggestion but more insidious is the possibility of mixing up concepts. For example, you might have another concept of height and width. There is a chance you will accidentally override the getters and setters from the parent, and there is the chance a user of the class will call the wrong method if you name it differently.

I'll concede that if you are designing a new component then you should extend an existing one. The line between designing a new component and using existing components is not a clear cut one. JScrollPane, for example, uses scrollbars and a viewport to achieve its functionality. In that sense, it just uses existing components but it also makes conceptual sense to consider it a new component in its own right. Making a main application form seems to me to lie clearly on the "using components" (i.e. "has a") side of the line.

However, I know that the rest of the Java community disagrees with me so maybe I have missed something.
Was This Post Helpful? 2
  • +
  • -

#3 macosxnerd101  Icon User is online

  • Self-Trained Economist
  • member icon




Reputation: 10803
  • View blog
  • Posts: 40,262
  • Joined: 27-December 08

Posted 26 May 2012 - 05:19 PM

Quote

For example, you might have another concept of height and width. There is a chance you will accidentally override the getters and setters from the parent, and there is the chance a user of the class will call the wrong method if you name it differently.

A lot of this is redundant though. Why implement width and height separately if the Component already supports it? True, there is the danger of inadvertently overriding an existing API method. However, once one develops a certain familiarity with Swing, it becomes fairly intuitive to determine if the Component has that method already. Plus there is the documentation to check against; and by this point, most people have graduated to IDEs where there is some notification when overriding a method. I also don't think that we should program in fear of people overriding methods. If someone is working with a class I wrote, and they didn't read the documentation, then that's their problem if they accidentally override a method.

I'd also argue that we're not violating encapsulation, since we have neither seen nor are reinventing the existing API methods, only using them.
Was This Post Helpful? 0
  • +
  • -

#4 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 863
  • Posts: 2,652
  • Joined: 29-July 11

Posted 26 May 2012 - 05:30 PM

I am going to say that in production code, I would prefer not to extend JFrame unless I have functionality to add to it. Composition vs inheritance. As for one jframe responding to a change in another to hide itself, that sounds like a job for the observer pattern. Maybe I am wrong.
Was This Post Helpful? 0
  • +
  • -

#5 macosxnerd101  Icon User is online

  • Self-Trained Economist
  • member icon




Reputation: 10803
  • View blog
  • Posts: 40,262
  • Joined: 27-December 08

Posted 26 May 2012 - 05:42 PM

An Observer would be another way to accomplish this as well.

Quote

I am going to say that in production code, I would prefer not to extend JFrame unless I have functionality to add to it. Composition vs inheritance.

This is my argument, though not necessarily limited to functionality. The inheritance model makes it easier to organize which Components belong to the JFrame. Whereas if the JFrame has multiple JPanels it contains, and those JPanels have other Components, it can get cumbersome to maintain. Inheritance provides a clean way to encapsulate and organize these Components, without bloating a single class with helper methods to initialize each JPanel.
Was This Post Helpful? 0
  • +
  • -

#6 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 2069
  • View blog
  • Posts: 4,307
  • Joined: 11-December 07

Posted 26 May 2012 - 06:14 PM

Quote

Why implement width and height separately if the Component already supports it?


For a different concept of height and width. The height and width represent the pixel size of the component, and they are subject to being changed by the LayoutManager of the container. If you have another kind of height and width (and admittedly, I'm struggling to come up with a good example) like maybe the size of an area in kilometres that has to be rescaled to fit then you run the risk of a collision.

Have you read Effective Java? The author makes a compelling case for composition over inheritance. I don't see why this shouldn't be the case for user interfaces too.
Was This Post Helpful? 0
  • +
  • -

#7 macosxnerd101  Icon User is online

  • Self-Trained Economist
  • member icon




Reputation: 10803
  • View blog
  • Posts: 40,262
  • Joined: 27-December 08

Posted 26 May 2012 - 06:21 PM

I have not read Effective Java. I'll try and find time this summer to read it!

I would hope in the width and height case, proper naming conventions would be used to clear up ambiguities.
Was This Post Helpful? 0
  • +
  • -

#8 cfoley  Icon User is offline

  • Cabbage
  • member icon

Reputation: 2069
  • View blog
  • Posts: 4,307
  • Joined: 11-December 07

Posted 26 May 2012 - 06:28 PM

I thoroughly recommend it! It's a bit long in the tooth, and some chapters are out of date but most of the book is still relevant.

I agree I can't think of a good example for the width and height thing. But the general principle is of putting too much in the same scope. It just gets increasingly complicated for the programmer to keep tabs on.

Then there is the fact that the entire Java world does things your way, not mine. ;)
Was This Post Helpful? 1
  • +
  • -

#9 asm-mad  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 10
  • Joined: 31-May 12

Posted 05 June 2012 - 04:10 AM

Thanks I have been looking at a lot of Java tutorials on this site lately for my java course, this helped a lot!
Was This Post Helpful? 0
  • +
  • -

#10 bigmatt267  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: -1
  • View blog
  • Posts: 55
  • Joined: 02-December 09

Posted 05 February 2014 - 10:50 AM

View Postmacosxnerd101, on 26 May 2012 - 02:24 PM, said:

Spoiler


This is a great tutorial. It does give me ideas to make my class work look neater. The simple codes are boring to me and I like a challenge.
Thanks!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1