11
\$\begingroup\$

I just finished a fairly easy homework assignment. Basically all I had to do was add six JPanels that were just text boxes with color names as the text. However, the color of the font had to match the text, so for instance, black had to be black, blue had to be blue, etc. It works find but it just seems like I could have shortened this code up a little.

import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.border.*;


public class SixLabels extends JFrame
{
    public SixLabels()
    {
        // sets layout of square
        setLayout(new GridLayout(2,3));

        //set JLabel properties such as border, text, font, etc.
        Border line =  new LineBorder(Color.yellow, 4);
        Font TNR = new Font("Times New Roman", Font.BOLD, 20);
        JLabel black = new JLabel("Black", JLabel.CENTER);
        JLabel blue = new JLabel("Blue", JLabel.CENTER);
        JLabel cyan = new JLabel("Cyan", JLabel.CENTER);
        JLabel green = new JLabel("Green", JLabel.CENTER);
        JLabel magenta = new JLabel("Magenta", JLabel.CENTER);
        JLabel orange = new JLabel("Orange", JLabel.CENTER);

        // add set properties and add panel to JFrame
        black.setOpaque(true);
        black.setForeground(Color.black);
        black.setBackground(Color.white);
        black.setBorder(line);
        black.setFont(TNR);
        black.setToolTipText("This is Black");
        add(black);

        // add set properties and add panel to JFrame
        blue.setOpaque(true);
        blue.setForeground(Color.blue);
        blue.setBackground(Color.white);
        blue.setBorder(line);
        blue.setFont(TNR);
        blue.setToolTipText("This is Blue");
        add(blue);

        // add set properties and add panel to JFrame
        cyan.setOpaque(true);
        cyan.setForeground(Color.cyan);
        cyan.setBackground(Color.white);
        cyan.setBorder(line);
        cyan.setFont(TNR);
        cyan.setToolTipText("This is Cyan");
        add(cyan);

        // add set properties and add panel to JFrame
        green.setOpaque(true);
        green.setForeground(Color.green);
        green.setBackground(Color.white);
        green.setBorder(line);
        green.setFont(TNR);
        green.setToolTipText("This is Green");
        add(green);

        // add set properties and add panel to JFrame
        magenta.setOpaque(true);
        magenta.setForeground(Color.magenta);
        magenta.setBackground(Color.white);
        magenta.setBorder(line);
        magenta.setFont(TNR);
        magenta.setToolTipText("This is Magenta");
        add(magenta);

        // add set properties and add panel to JFrame
        orange.setOpaque(true);
        orange.setForeground(Color.orange);
        orange.setBackground(Color.white);
        orange.setBorder(line);
        orange.setFont(TNR);
        orange.setToolTipText("This is Orange");
        add(orange);





    }


    public static void main(String[] args) 
    {
        SixLabels frame = new SixLabels(); // creates frame
        frame.setTitle("Chapter 12 Exercise 12.8"); // sets name on title bar
        frame.setSize(600, 400); // sets size of GUI frame
        frame.setLocationRelativeTo(null); // Centers GUI frame
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Stop/Close program on exit 
        frame.setVisible(true); // Shows the frame, must be true

    }

}
\$\endgroup\$
10
\$\begingroup\$

I would recommend:

  • You're using a GridLayout(2, 3) -- good.
  • I'd make it actually a GridLayout(2, 0, GAP, GAP) where GAP is a constant that allows a gap between JLabels. The first and second parameters mean 2 rows with variable number of columns.
  • Then if you set the background color of the JPanel to yellow, make the JLabels opaque and white, give the background JPanel an empty border of GAP width, you've got your yellow borders
  • Use an enum to combine a Color with a String. An alternative to this could be to use a Map<String, Color>
  • The enum method values() would then return an array of all the items of that enum. If using the Map such as a HashMap, you'd get the key set and iterate through this.
  • You could then use a simple for loop to iterate through this array, creating JLabels that have the desired text and the desired foreground colors.
  • You risk painting yourself in a corner by having your class extend JFrame, forcing you to create and display JFrames, when often more flexibility is often called for. In fact, I would venture that most of the Swing GUI code that I've created and that I've seen does not extend JFrame, and in fact it is rare that you'll ever want to do this. More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This can greatly increase the flexibility of your GUI coding.

For example,

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.*;

@SuppressWarnings("serial")
public class SixLabels2 extends JPanel {
   private static final int GAP = 8;
   private static final int LABEL_WIDTH = 160;
   private static final Dimension LABEL_SIZE = new Dimension(LABEL_WIDTH, LABEL_WIDTH);
   private static final int ROWS = 2;
   private static final Font LABEL_FONT = new Font("Times New Roman", Font.BOLD, 20);

   public SixLabels2() {
      setBackground(Color.yellow);
      setLayout(new GridLayout(ROWS, 0, GAP, GAP)); 
      setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));

      // iterate through all the items in the MyColor enum
      for (MyColor myColor : MyColor.values()) {
         JLabel label = new JLabel(myColor.getText(), SwingConstants.CENTER);
         label.setForeground(myColor.getColor());
         label.setPreferredSize(LABEL_SIZE);
         label.setFont(LABEL_FONT);
         label.setOpaque(true);
         label.setBackground(Color.white);
         add(label);
      }
   }

   private static void createAndShowGui() {
      SixLabels2 mainPanel = new SixLabels2();

      JFrame frame = new JFrame("SixLabels2");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

enum MyColor {
   BLACK("Black", Color.black), BLUE("Blue", Color.blue), 
   CYAN("Cyan", Color.cyan), GREEN("Green", Color.green), 
   MAGENTA("Magenta", Color.magenta), ORANGE("Orange", Color.orange);

   private String text;
   private Color color;

   private MyColor(String text, Color color) {
      this.text = text;
      this.color = color;
   }

   public String getText() {
      return text;
   }

   public Color getColor() {
      return color;
   }

   @Override
   public String toString() {
      return text;
   }

}

Note, my code may not be actually shorter in length, but I'll wager that it's easier to debug and enhance. Consider adding a few more colors to your code and mine and see how many variables and statements need to be changed in both. For instance, if you wanted to add two more colors to mine, all you'd need to do would be to change the enum to include two more colors. The program would take care of the rest for you.

For example, if you changed the enum from this:

enum MyColor {
   BLACK("Black", Color.black), BLUE("Blue", Color.blue), 
   CYAN("Cyan", Color.cyan), GREEN("Green", Color.green), 
   MAGENTA("Magenta", Color.magenta), ORANGE("Orange", Color.orange);

to this:

enum MyColor {
   BLACK("Black", Color.black), BLUE("Blue", Color.blue), 
   CYAN("Cyan", Color.cyan), GREEN("Green", Color.green), 
   MAGENTA("Magenta", Color.magenta), ORANGE("Orange", Color.orange),
   GRAY("Gray", Color.gray), FOO("Foo", new Color(100, 250, 255));

The GUI would then automatcially accommodate the additional colors.

\$\endgroup\$
  • 2
    \$\begingroup\$ +1. One tip for the OP: when using the enum solution shown here, iterating through values() is ordered by the declaration order, e.g. BLACK, then BLUE, then CYAN etc. as shown. Useful if you require ordering and need to swap the color arrangements. \$\endgroup\$ – h.j.k. Jan 26 '15 at 2:50
4
\$\begingroup\$

The answer using enums is powerful but slightly overkill in my opinion. I would have preferred to keep it simple by extracting the label creation code into a separate method, so this:

{
    ...
    // add set properties and add panel to JFrame
    magenta.setOpaque(true);
    magenta.setForeground(Color.magenta);
    magenta.setBackground(Color.white);
    magenta.setBorder(line);
    magenta.setFont(TNR);
    magenta.setToolTipText("This is Magenta");
    add(magenta);

    // add set properties and add panel to JFrame
    orange.setOpaque(true);
    orange.setForeground(Color.orange);
    orange.setBackground(Color.white);
    orange.setBorder(line);
    orange.setFont(TNR);
    orange.setToolTipText("This is Orange");
    add(orange);
}

Becomes something like this:

{
    magenta = createLabel("This is Magenta", Color.magenta);
    orange = createLabel("This is Orange", Color.orange);
    ...

    add(magenta);
    add(orange);
    add(...);
}

private static JLabel createLabel(String text, Color color) {
    JLabel label = new JLabel();
    label.setOpaque(true);
    label.setForeground(color);
    label.setBackground(Color.white);
    label.setBorder(line);
    label.setFont(TNR);
    label.setToolTipText(text);
    return label;
}

That way it's still clear what's going on, and if you decide to change the way you create your labels, there's an obvious place to do that -- the createLabel method.

My first port of call when refactoring for clarity and brevity is to consider extracting a short and simple method and calling that several times, rather than using a loop. However, if there was a large number of JLabels to be added, and you didn't need to hold onto a reference (you usually don't), a loop might be more appropriate.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.