6

My ultimate goal is to have a JTextArea with a background image. I found code online that showed me how to do this, but now I'm having an issue with the text being on top of the image.

This Is what I mean:

Text overlapping image

Is there any way I can add a sort of inward indent so that the text is not overlapping the edges of the image?


Here is the raw comment bubble image.

Comment bubble


Here is the code:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.GrayFilter;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class myBackgroundSample {

    String file;

    public myBackgroundSample(String i) {
        file = i;
        setItUp();
    }

    public void setItUp() {
        final ImageIcon imageIcon = new ImageIcon(file);
        JTextArea textArea = new JTextArea() {
            Image image = imageIcon.getImage();

            public void paint(Graphics g) {
                setOpaque(false);
                g.drawImage(image, 0, 0, this);
                super.paint(g);
            }
        };
        JFrame frame = new JFrame("Background Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JScrollPane scrollPane = new JScrollPane(textArea);
        Container content = frame.getContentPane();
        content.add(scrollPane, BorderLayout.CENTER);
        frame.setSize(400, 400);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String right = "chat1.jpg";
        myBackgroundSample temp = new myBackgroundSample(right);

    }
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
user1091968
  • 87
  • 1
  • 3
  • Here is the image for chat1: https://lh4.googleusercontent.com/-uhDR71VGYfA/TuRNiM2DfOI/AAAAAAAAADU/DqOuEi8c3lg/s334/chat1.jpg – user1091968 Dec 11 '11 at 06:48
  • 1
    1) Why a `JTextArea` (as opposed to a `JLabel` or other syled component)? 2) I think this would best be done with a **custom** border, a `CommentBubbleBorder`. 3) Swing components should override `paintComponent(Graphics)`, rather than `paint(Graphics)`. – Andrew Thompson Dec 11 '11 at 07:26
  • Is it not about time you select an answer to some of your [earlier questions](http://stackoverflow.com/users/1091968/user1091968?tab=questions)? – Andrew Thompson Nov 12 '13 at 00:03

4 Answers4

12

Use a custom border that extends AbstractBorder. Something like this:

Text Bubble Border

Getting the exact shape & color is left as an exercise for the reader. :)

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.AbstractBorder;

class TextBubbleBorder extends AbstractBorder {

    private Color color;
    private int thickness = 4;
    private int radii = 8;
    private int pointerSize = 7;
    private Insets insets = null;
    private BasicStroke stroke = null;
    private int strokePad;
    private int pointerPad = 4;
    RenderingHints hints;

    TextBubbleBorder(
        Color color) {
            new TextBubbleBorder(color, 4, 8, 7);
    }

    TextBubbleBorder(
        Color color, int thickness, int radii, int pointerSize) {
            this.thickness = thickness;
            this.radii = radii;
            this.pointerSize = pointerSize;
        this.color = color;

        stroke = new BasicStroke(thickness);
        strokePad = thickness/2;

        hints = new RenderingHints(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

        int pad = radii + strokePad;
        int bottomPad = pad + pointerSize + strokePad;
        insets = new Insets(pad,pad,bottomPad,pad);
    }

    @Override
    public Insets getBorderInsets(Component c) {
        return insets;
    }

    @Override
    public Insets getBorderInsets(Component c, Insets insets) {
        return getBorderInsets(c);
    }

    @Override
    public void paintBorder(
        Component c,
        Graphics g,
        int x, int y,
        int width, int height) {

        Graphics2D g2 = (Graphics2D)g;

        int bottomLineY = height-thickness-pointerSize;

        RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(
            0+strokePad,
            0+strokePad,
            width-thickness,
            bottomLineY,
            radii,
            radii
            );

        Polygon pointer = new Polygon();

        // left point
        pointer.addPoint(
            strokePad+radii+pointerPad,
            bottomLineY);
        // right point
        pointer.addPoint(
            strokePad+radii+pointerPad+pointerSize,
            bottomLineY);
        // bottom point
        pointer.addPoint(
            strokePad+radii+pointerPad+(pointerSize/2),
            height-strokePad);

        Area area = new Area(bubble);
        area.add(new Area(pointer));

        g2.setRenderingHints(hints);

        Area spareSpace = new Area(new Rectangle(0,0,width,height));
        spareSpace.subtract(area);
        g2.setClip(spareSpace);
        g2.clearRect(0,0,width,height);
        g2.setClip(null);

        g2.setColor(color);
        g2.setStroke(stroke);
        g2.draw(area);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JLabel l = new JLabel(
                    "The quick brown fox jumped over the lazy dog!");

                l.setBorder(new TextBubbleBorder(Color.MAGENTA.darker(),2,4,0));
                l.setOpaque(true);
                l.setBackground(Color.BLACK);
                JOptionPane.showMessageDialog(null, l);
            }
        });
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Thank you! A couple questions: What is the little "i" bubble to left? What portion of this code can be edited so that the words will wrap around instead of stretching the text area? " so that the conversation bubble is stemming from the right? My overall goal is to simulate conversation view for chat. – user1091968 Dec 12 '11 at 01:58
  • See further info. (and a repeated question) in edit to answer. – Andrew Thompson Dec 12 '11 at 02:18
6

You should use an Border for that, more specifictly you should use BorderFactory.createEmptyBorder(int top, int left, int bottom, int right):

textArea.setBorder(BorderFactory.createEmptyBorder(10,10,15,10));

You should also override paintComponent instead of paint. Also, use setRows() and setColumns() to set the size of the textArea, then you can use pack() instead of setSize(400,400) which is not recommended. See this example:

enter image description here

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;


public class Test extends JFrame {

    class MyTextArea extends JTextArea {

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            g2.setColor(Color.PINK);
            g2.setStroke(new BasicStroke(4));
            g2.drawRoundRect(3, 3, getWidth()-7, getHeight()-7, 5, 5);
        }

    }

    public Test() {
        JPanel panel = new JPanel(new BorderLayout());
        JTextArea textArea = new MyTextArea();
        textArea.setRows(3);
        textArea.setColumns(25);
        textArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        panel.add(textArea, BorderLayout.NORTH);

        add(panel);
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

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

}
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • 1
    Nice example, but I still think a [custom border](http://stackoverflow.com/a/8463742/418556) is the way to go on this one. ;) – Andrew Thompson Dec 11 '11 at 12:54
  • @AndrewThompson: You are right. It depends how much changes he wants to do. This single line fix may help, but not be the best solution. +1 for your solution. – Jonas Dec 11 '11 at 13:07
  • +1. easiest solution for those of use that are Graphics challenged :) – camickr Dec 11 '11 at 16:06
  • Thank you! I'm working with this particular line of code: textArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); ...to create a border so it looks like the text is confined within the bubble image. – user1091968 Dec 12 '11 at 02:04
1

To post code, indent every line by four spaces.

I assume you are overriding paintComponent()* for your JTextArea. If you are, make sure that it is transparent by adding

setOpaque(false);

*This also works if you override paint(), but as trashgod correctly states, that would interfere with paintBorder().

Community
  • 1
  • 1
Allen Z.
  • 1,560
  • 1
  • 11
  • 19
  • "Swing programs should override `paintComponent()` instead of overriding `paint()`."—[Painting in AWT and Swing: The Paint Methods](http://java.sun.com/products/jfc/tsc/articles/painting/index.html#callbacks). – trashgod Dec 11 '11 at 08:47
0

A better version of TextBubbleBorder.

https://gist.github.com/wenerme/6940534

  • pointer padding control
  • pointer side control
  • dynamic change
wener
  • 7,191
  • 6
  • 54
  • 78