3

I'm attempting to create an IM program as a sort of hobby project. I've been experimenting with UI designs and have been trying to get this prototype for the actual message display working.

Currently, the frame is set up like so;

  • The JFrame content pane is set to a JPanel which utilizes a BoxLayout using Y_AXIS.
  • The content panel contains the TextMessage objects which I would like to display, added to the content pane individually.

The TextMessage object is setup like so;

  • The message and sender string are stored inside of a TextMessage object extending JTextPane and utilizing a StyledDocument for formatting.
  • The TextMessage is placed inside of a JPanel in order to allow proper placement of the object within the BoxLayout. The JPanel is set to a FlowLayout which pins the TextMessage object against either edge of the JPanel, depending on a boolean value.

Thus far, everything is working as I would like it to, with one notable exception. When I resize the JFrame, the TextMessage objects do not resize and instead simply disappear off the edge of the screen.

Expected Result:

http://oi57.tinypic.com/nnl3bn.jpg

The actual Result:

http://oi62.tinypic.com/2re7dbc.jpg

The JFrame Class:

public class NewMain extends JFrame {

    public NewMain() {
        setTitle("SparrowIM Chat Bubble");
        setPreferredSize(new Dimension(880, 550));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setResizable(true);

        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        setContentPane(panel);

        add(new TextMessage("Hey, man. What's up?", "Jnk1296", true));
        add(new TextMessage("Eh, nothing much.", "KeepJ96", false));
        add(new TextMessage("Wbu? Anything new going on with you?", "KeepJ96", false));

        add(new TextMessage("Nah, not really. Got a job interview coming up in a few " +
                                    "days, though. Sorta excited for that, but sorta not. " +
                                    "Other than that, life as usual. xD", "Jnk1296", true));

        add(new TextMessage("lol Sounds good.", "KeepJ96", false));
        add(new TextMessage("Yeah. How's the wife and kids, though?", "Jnk1296", true));
        add(new TextMessage("Sarah still griping at you to get the roof patched up?", "Jnk1296", true));
        add(new TextMessage("lol you could say that...", "KeepJ96", false));

        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new NewMain();
    }
}

TextMessage Object (JPanel Container for TextMessageContent):

public class TextMessage extends JPanel {

    private TextMessageContent content;

    public TextMessage(String sender, String message, boolean localMessage) {
        setBorder(new EmptyBorder(5, 5, 5, 5));

        if (localMessage) {
            setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0));
        } else {
            setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        }

        this.content = new TextMessageContent(sender, message, localMessage);
        add(content);
    }

    public TextMessageContent getContent() {
        return this.content;
    }
}

TextMessageContent Class: (JTextPane)

public class TextMessageContent extends JTextPane {

private static final long serialVersionUID = -8296129524381168509L;
    private String sender;
    private String message;
    private boolean isClient;

    private Image background;

    public TextMessageContent(String message, String sender, boolean isClient) {
        // Define Data Points
        this.message = message;
        this.sender = sender;
        this.isClient = isClient;

        this.setEditable(false);
        this.setOpaque(false);
        this.setBorder(new EmptyBorder(7, 7, 7, 7));

        // Fetch Background Image (Hard Location Temporary)
        try {
            if (this.isClient) {
                background = ImageIO.read(
                        new File("/home/jacob/Desktop/orange.png"));
            } else {
                background = ImageIO.read(
                        new File("/home/jacob/Desktop/Green.png"));
            }
        } catch (Exception e) { return; }

        // Create Text Styles
        StyledDocument doc = getStyledDocument();

        // Create Default Base Style
        Style def = StyleContext.getDefaultStyleContext()
                .getStyle(StyleContext.DEFAULT_STYLE);
        Style regular = doc.addStyle("regular", def);

        // Create Body Style
        Style body = doc.addStyle("message", regular);
        StyleConstants.setFontFamily(body, "Verdana");
        StyleConstants.setFontSize(body, 12);

        // Create Sender Style
        Style foot = doc.addStyle("sender", body);
        StyleConstants.setFontSize(foot, 9);

        // Build Document
        try {
            doc.insertString(0, this.message + "\n", body);
            doc.insertString(doc.getLength(), this.sender + " - " +
                    getSystemTime(), foot);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        background = ImageUtil.stretch(background, new Insets(5, 5, 5, 5),
                new Dimension(getWidth(), getHeight()),
                BufferedImage.TYPE_INT_ARGB);

        g.drawImage(background, 0, 0, null);
        super.paintComponent(g);
    }

    private String getSystemTime() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("h:mm a");
        return sdf.format(cal.getTime());
    }
}

I've read about simply adding the JTextPane directly to the containers and whatnot, but with this set up, the JPanel wrapping around the text message content is necessary to keep the BoxLayout of the content pane from making the Text Messages fill the entire panel.

Any suggestions?

camickr
  • 321,443
  • 19
  • 166
  • 288
user288467
  • 31
  • 2
  • 3
    First, I'd advise you to think about using JList with a custom item renderer rather than this complicated design. Second, your try { } catch (Exception e) { is one of the basic really bad no nos... Be more specfic with the Exceptions you are catching, and at least print out the stacktrace if an Exception occurs – ControlAltDel Dec 22 '14 at 20:55
  • See also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html) and [`TextPanePerfectSize`](http://stackoverflow.com/a/3318949/230513). – trashgod Dec 22 '14 at 20:56
  • This is happening due to the use of FlowLayout in the TextMessage class. FlowLayout does not attempt to restrain its children to fit inside the container. You can use BorderLayout or GridBagLayout instead. You will then find that JTextPane's preferred size for long content isn't pleasant, since it is really designed to go inside a JScrollPane which would force the JTextPane's width to match the viewport's width. – VGR Dec 23 '14 at 00:08
  • @ControlAltDel The problem with a JList is that every cell has to be the same size (I think). In a chat application such as this one, messages can have different heights. – VGR Dec 23 '14 at 00:26
  • Try to place the JTextPanes in JScrollPanes rather than direct adding – StanislavL Dec 23 '14 at 05:44
  • I see 2 ways to solve this. The easiest if it works is to force the reSize() and revalidate() of the ContentPane. If all other propositions failed, another way is to write your own LayoutManager. It's quite easy and the most efficient way to obtain what you want when the case is simple like yours. – Sharcoux Dec 24 '14 at 15:50

1 Answers1

0

I've read about simply adding the JTextPane directly to the containers and whatnot, but with this set up, the JPanel wrapping around the text message content is necessary to keep the BoxLayout of the content pane from making the Text Messages fill the entire panel

Not sure if it will help or not but the BoxLayout respects the maximum size of the components added to it. Maybe you can override the getMaximumSize() method to control the width. The width should be the lesser of the preferred width or the width of the parent container.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • It's possible, but I've been trying to avoid making calls for set*Size() wherever possible. I attempted a setup earlier that did something roughly similar to this and it ended up being extremely unreliable. – user288467 Dec 22 '14 at 21:55
  • `I've been trying to avoid making calls for set*Size() wherever possible.` - I did not suggest to invoke set???Size(). You should not be doing that. If you want custom behaviour then you override the get???Size() method. Layout managers can only do their job when they have enough information to make an informed decision. – camickr Dec 22 '14 at 22:31