How to make an interactive world map in Java Swing? - java

I want to write a program in Java Swing which will show the world map, where a user can click on each country(i.e each country is a button). I thought that the most convenient way to do it is to split a world map image into buttons, but I couldn't figure out how can I do this.
I looked at this question: Split image into clickable regions , the given answer there shows how to split the image into rectangles, but I have to split it into arbitrary shapes.
This is the image i want to work on:

A 'little' bit of number crunching can define areas or shapes of particular colors in an image. E.G. Starting with this:
Original image, cropped and reduced to a binary (2 color) image, then the oceans flood filled. All but the flood filling done in Java code not shown.
Then running the shape algorithm based on colors near white (be patient - it takes a while), will produce a series of areas used to render this over the top. A mouse motion listener has been added to color the area under the pointer to dark green.
This is the code that produces that image. The mouse pointer is currently pointing at China.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
/*
Outline code from:
https://stackoverflow.com/q/7218309/418556
*/
public class WorldMapArea {
private JComponent ui = null;
JLabel output = new JLabel();
public static final int SIZE = 750;
BufferedImage image;
Area area;
ArrayList<Shape> shapeList;
public WorldMapArea() {
try {
initUI();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void initUI() throws Exception {
if (ui != null) {
return;
}
URL url = new URL("https://i.stack.imgur.com/N4eOn.png");
image = ImageIO.read(url);
long then = System.currentTimeMillis();
System.out.println("" + then);
area = getOutline(Color.WHITE, image, 12);
long now = System.currentTimeMillis();
System.out.println("Time in mins: " + (now - then) / 60000d);
shapeList = separateShapeIntoRegions(area);
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
output.addMouseMotionListener(new MousePositionListener());
ui.add(output);
refresh();
}
public Area getOutline(Color target, BufferedImage bi, int tolerance) {
// construct the GeneralPath
GeneralPath gp = new GeneralPath();
boolean cont = false;
for (int xx = 0; xx < bi.getWidth(); xx++) {
for (int yy = 0; yy < bi.getHeight(); yy++) {
if (isIncluded(new Color(bi.getRGB(xx, yy)), target, tolerance)) {
//if (bi.getRGB(xx,yy)==targetRGB) {
if (cont) {
gp.lineTo(xx, yy);
gp.lineTo(xx, yy + 1);
gp.lineTo(xx + 1, yy + 1);
gp.lineTo(xx + 1, yy);
gp.lineTo(xx, yy);
} else {
gp.moveTo(xx, yy);
}
cont = true;
} else {
cont = false;
}
}
cont = false;
}
gp.closePath();
// construct the Area from the GP & return it
return new Area(gp);
}
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<>();
PathIterator pi = shape.getPathIterator(null);
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
class MousePositionListener implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
// do nothing
}
#Override
public void mouseMoved(MouseEvent e) {
refresh();
}
}
public static boolean isIncluded(Color target, Color pixel, int tolerance) {
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return ((rP - tolerance <= rT) && (rT <= rP + tolerance)
&& (gP - tolerance <= gT) && (gT <= gP + tolerance)
&& (bP - tolerance <= bT) && (bT <= bP + tolerance));
}
private void refresh() {
output.setIcon(new ImageIcon(getImage()));
}
private BufferedImage getImage() {
BufferedImage bi = new BufferedImage(
2 * SIZE, SIZE, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.drawImage(image, 0, 0, output);
g.setColor(Color.ORANGE.darker());
g.fill(area);
g.setColor(Color.RED);
g.draw(area);
try {
Point p = MouseInfo.getPointerInfo().getLocation();
Point p1 = output.getLocationOnScreen();
int x = p.x - p1.x;
int y = p.y - p1.y;
Point pointOnImage = new Point(x, y);
for (Shape shape : shapeList) {
if (shape.contains(pointOnImage)) {
g.setColor(Color.GREEN.darker());
g.fill(shape);
break;
}
}
} catch (Exception doNothing) {
}
g.dispose();
return bi;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
WorldMapArea o = new WorldMapArea();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.setResizable(false);
f.pack();
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}

Related

How to Obtain the Chinese character stroke order from the ttf file? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
i find getGlyphOutline() can show font out line from JAVA API.
and i have not found out any API for show one chinese stroke order.
but this is true: .ttf contain the stroke order.
i just don't know how to get it by JAVA.
May be some important APIs i forget?
shape = gv.getGlyphOutline(i, 200, 200);
((Graphics2D) g).draw(shape);
now, i found PathIterator
Shape shape = gv.getGlyphOutline(0, 200, 200);
PathIterator pi = shape.getPathIterator(new AffineTransform());
double[] coords = new double[6];
int count = 0;
while (!pi.isDone()) {
int kind = pi.currentSegment(coords);
int[] path = new int[4];
switch (kind) {
case PathIterator.SEG_MOVETO:
System.out.println("SEG_MOVETO");
break;
case PathIterator.SEG_LINETO:
System.out.println("SEG_LINETO");
break;
case PathIterator.SEG_CLOSE:
System.out.println("SEG_CLOSE");
g.drawLine((int) coords[0], (int) coords[1],
(int) coords[2], (int) coords[3]);
count++;
break;
case PathIterator.SEG_QUADTO:
System.out.println("SEG_QUADTO");
g.drawLine((int) coords[0], (int) coords[1],
(int) coords[2], (int) coords[3]);
count++;
break;
default:
throw new IllegalArgumentException("Bad path segment");
}
pi.next();
}
There is a problem, i can not get complete word..
It looks like dotted line...
Do you mean to draw the strokes of the characters 'part by part' - something like this code?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class LettersByStrokeAnimation {
private JComponent ui = null;
String text = "";
Font font;
LettersByStrokeAnimation() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new GridLayout(0, 1));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
for (int i = 13444; i < 13450; i++) {
text += new String(Character.toChars(i));
}
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Font[] fonts = ge.getAllFonts();
boolean canDisplay = false;
int i = 0;
while (!canDisplay) {
font = fonts[i];
if (font.canDisplayUpTo(text) < 0) {
canDisplay = true;
font = font.deriveFont(50f);
}
i++;
}
JLabel l = new JLabel(text);
l.setFont(font);
ui.add(l);
ui.add(new AnimatedText(text, font, 200));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
LettersByStrokeAnimation o = new LettersByStrokeAnimation();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
class AnimatedText extends JPanel {
Font font;
int counter;
ArrayList<Shape> shapes;
AnimatedText(String text, Font font, int delay) {
this.font = font;
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.dispose();
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, text);
Shape shape = gv.getOutline(0, 50);
GeneralPath gp = new GeneralPath(shape);
PathIterator pi = gp.getPathIterator(null);
shapes = new ArrayList<Shape>();
while (!pi.isDone()) {
shapes.add(getNextStroke(pi));
}
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
AnimatedText.this.repaint();
}
};
Timer timer = new Timer(delay, timerListener);
timer.start();
}
private final Shape getNextStroke(PathIterator pi) {
double[] coords = new double[6];
GeneralPath gp = new GeneralPath();
boolean closed = false;
while (!closed && !pi.isDone()) {
int pathSegmentType = pi.currentSegment(coords);
closed = pathSegmentType == PathIterator.SEG_CLOSE;
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1], coords[2],
coords[3], coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
Shape shape = new Area(gp);
return shape;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int current = counter % shapes.size();
for (int i = 0; i < current; i++) {
g2.draw(shapes.get(i));
}
counter++;
}
}

java making a screen shot preview with JPanel

I'm making a fun little test screen recording program in java, and I want it to have a preview of your screen before you start recording.. but its a very slow and poor method of which I am using, involving capturing an image, saving it, then reading it in through a bufferedimage and drawing that image using Graphics. Its very slow and not useful as a "preview" is there a way to speed up and have a more efficient "previewing system".
Here is what I have so far:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class MainFrame implements ActionListener, Runnable {
//add frame components
public static JFrame frame = new JFrame("Screen Caper - v1.0.1");
JButton start = new JButton("record");
JButton close = new JButton("Exit");
JPanel preview = new JPanel();
public static boolean running = false;
public static boolean recording = false;
public static boolean paused = false;
public static String curDir = System.getProperty("user.dir");
//get the screen width
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
Container a = new Container();
Container b = new Container();
public MainFrame() {
frame.setSize((int)(width) - 80, (int)(height) - 80);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setup the buttons and JPanel
a.setLayout(new GridLayout(1, 2));
a.add(start);
start.addActionListener(this);
a.add(close);
close.addActionListener(this);
frame.add(a, BorderLayout.NORTH);
b.setLayout(new GridLayout(1, 2));
b.add(preview);
frame.add(b, BorderLayout.CENTER);
//add anything else
running = true;
//set frame to visible
frame.setVisible(true);
run();
}
public static void main(String[] args) {
new MainFrame();
}
public void run() {
Graphics g = frame.getGraphics();
while (running) {
//draw the preview of the computer screen on the JPanel if its not recording already
if (!recording && !paused) {
drawPreview(g);
}
}
}
public void drawPreview(Graphics g) {
BufferedImage image;
try {
image = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(image, "png", new File("test.png"));
} catch (Exception ex) {
ex.printStackTrace();
}
BufferedImage prevIm;
try {
prevIm = ImageIO.read(new File("test.png"));
g.setColor(new Color(0, 0, 0));
g.fillRect(preview.getX() + 3, preview.getY() + 51, preview.getWidth(), preview.getHeight() + 1);
g.drawImage(prevIm, preview.getX() + 3, preview.getY() + 51, preview.getX() + preview.getWidth(), preview.getY() + preview.getHeight(), null);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void record(Graphics g) {
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(start)) {
if (!recording) {
//if the program isn't recording, then start recording
Graphics g = frame.getGraphics();
record(g);
start.setText("Finish");
recording = true;
System.out.println("recording...");
} else {
//else stop recording
start.setText("record");
recording = false;
System.out.println("done");
}
}
if (event.getSource().equals(close)) {
paused = true;
int ans = JOptionPane.showConfirmDialog(null, "Woah there! You're about to quit the application\nAre you sure you want to procced?", "Caution!", JOptionPane.YES_NO_OPTION);
if (ans == JOptionPane.YES_OPTION) {
System.exit(0);
} else if (ans == JOptionPane.NO_OPTION) {
paused = false;
}
}
}
}
any help is appreciated!
Don't use getGraphics, this is not how custom painting is done.
Your run method may simply be running to fast, consider using a javax.swing.Timer instead
Toolkit.getDefaultToolkit().getScreenSize() only returns the "default" screen and does not take into consideration split screens
Capturing the screen is a time consuming process and there's not much you can do about reducing it (as it's outside of your control). You "could" setup a series of Threads whose job it was to capture a given section of the desktop. You could also achieve this using SwingWorkers which would make it easier to sync the updates back to the UI...
Take a look at:
Performing Custom Painting
Concurrency in Swing
How to Use Swing Timers
Updated with example
This is a proof of concept only! You should understand what it's trying to do and borrow ideas from it...
You can see the preview window change inside the preview window...kinda freaky...
It tested this on a virtual desktop of 4480x1600.
Basically, it divides the desktop up into a 4x4 grid and starts a Thread for each section. Each thread is responsible for capturing it's own section of the screen.
It also scales that resulting image down and feeds it back to the UI.
I had started with SwingWorkers, but it seems to be hard to be limited to 10 threads. You could reduce the grid size and use SwingWorkers, they tend to be simpler to use and manage then raw Threads.
Each section is given an id, which allows me keep track of what's changed. Technically, you could just add elements to a List, but how do you determine what's new and what's old?
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class PreviewDesktop {
public static void main(String[] args) {
new PreviewDesktop();
}
public PreviewDesktop() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Puzzler {
private Rectangle virtualBounds;
private double scale;
private Map<Integer, PuzzlePiece> pieces;
public TestPane() {
virtualBounds = getVirtualBounds();
int columns = 4;
int rows = 4;
pieces = new HashMap<>(columns * rows);
int columnWidth = Math.round(virtualBounds.width / (float) columns);
int rowHeight = Math.round(virtualBounds.height / (float) rows);
int id = 0;
for (int row = 0; row < rows; row++) {
int y = virtualBounds.y + (row * rowHeight);
for (int column = 0; column < columns; column++) {
int x = virtualBounds.x + (column * columnWidth);
Rectangle bounds = new Rectangle(x, y, columnWidth, rowHeight);
GrabberWorker worker = new GrabberWorker(id, this, bounds);
System.out.println(id);
id++;
startThread(worker);
}
}
}
#Override
public double getScale() {
return scale;
}
#Override
public void invalidate() {
super.invalidate();
scale = getScaleFactorToFit(virtualBounds.getSize(), getSize());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Integer id : pieces.keySet()) {
PuzzlePiece piece = pieces.get(id);
Rectangle bounds = piece.getBounds();
BufferedImage img = piece.getImage();
g2d.drawImage(img, bounds.x, bounds.y, this);
// If you want to see each sections bounds, uncomment below...
//g2d.draw(bounds);
}
g2d.dispose();
}
#Override
public void setPiece(int id, PuzzlePiece piece) {
pieces.put(id, piece);
repaint();
}
protected void startThread(GrabberWorker worker) {
Thread thread = new Thread(worker);
thread.setDaemon(true);
thread.start();
}
}
public class PuzzlePiece {
private final Rectangle bounds;
private final BufferedImage img;
public PuzzlePiece(Rectangle bounds, BufferedImage img) {
this.bounds = bounds;
this.img = img;
}
public Rectangle getBounds() {
return bounds;
}
public BufferedImage getImage() {
return img;
}
}
public interface Puzzler {
public void setPiece(int id, PuzzlePiece piece);
public double getScale();
}
public class GrabberWorker implements Runnable {
private Rectangle bounds;
private Puzzler puzzler;
private int id;
private volatile PuzzlePiece parked;
private ReentrantLock lckParked;
public GrabberWorker(int id, Puzzler puzzler, Rectangle bounds) {
this.id = id;
this.bounds = bounds;
this.puzzler = puzzler;
lckParked = new ReentrantLock();
}
protected void process(PuzzlePiece piece) {
// puzzler.setPiece(bounds, chunks.get(chunks.size() - 1));
puzzler.setPiece(id, piece);
}
protected void publish(PuzzlePiece piece) {
lckParked.lock();
try {
parked = piece;
} finally {
lckParked.unlock();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
lckParked.lock();
try {
process(parked);
} finally {
lckParked.unlock();
}
}
});
}
#Override
public void run() {
try {
Robot bot = new Robot();
while (true) {
BufferedImage img = bot.createScreenCapture(bounds);
double scale = puzzler.getScale();
Rectangle scaled = new Rectangle(bounds);
scaled.x *= scale;
scaled.y *= scale;
scaled.width *= scale;
scaled.height *= scale;
BufferedImage imgScaled = getScaledInstance(img, scale);
publish(new PuzzlePiece(scaled, imgScaled));
Thread.sleep(500);
}
} catch (AWTException | InterruptedException exp) {
exp.printStackTrace();
}
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
public static double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = (double) iTargetSize / (double) iMasterSize;
return dScale;
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
// System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected static BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected static BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
Now, if you're feeling really adventurous, you could update this idea so that if the underlying image for a section didn't change and the scale didn't change, it didn't send that to the UI, which might help reduce some of the overhead ... and no, I don't have code to do that ;)

Java Graphics image refresh rate

I am making a JFrame holding several images that refresh using repaint(). The problem, however, was that the more images I added (or as the area that was being rendered increased) the framerate drastically decreased.
I was initially attempting to reduce the lag that Swing's unpredictable Timer experiences, but was unsuccessful. This time, I tried to call
repaint()
every 10 milliseconds based on system time. (To bypass the timer, desktop.screenPaint.drawWindow(); is called instead of repaint())
while(true)
{
long starttime = System.currentTimeMillis();
while(System.currentTimeMillis()-starttime < 10)
{
}
desktop.screenPaint.drawWindow();
desktop2.screenPaint.drawWindow();
}
Each "desktop" is just one of the draggable windows that you see if you run the code below. I noticed that when only "desktop" is added to "frame," there is no flickering. However, when "desktop2" is also added, flickering does not go away until the time until each refresh is set to 20 or more milliseconds. It is extremely strange, because when I was still using the timer, even thought the wait was set to 10 milliseconds, the framerate would lower to a frame every 20 milliseconds! I tested a little and noticed a trend: you will get flickering until the wait time to the amount of time that the 10 millisecond timer lagged behind. My question: Why is this happening? And is there any way to prevent the flickering, other than going back to the timer?
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.SwingUtilities.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
import java.io.File;
import java.io.IOException;
public class Display extends JPanel
{
public static int width, height;
public static JFrame frame = new JFrame("");
private static int rh = 10;
public static Display desktop = new Display();
public static Display desktop2 = new Display();
public static void main(String[] args)
{
Dimension screen = new Dimension();
screen = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
width = (int)screen.getWidth();
height = (int)screen.getHeight();
frame.setLayout(null);
frame.setLocation(0, 0);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
desktop.setBounds(0, 0, 620, 500+rh);
desktop2.setBounds(1000, 200, 620, 500+rh);
frame.add(desktop);
frame.add(desktop2);
frame.setUndecorated(true);
frame.setSize(width, height);
frame.setVisible(true);
while(true)
{
long starttime = System.currentTimeMillis();
while(System.currentTimeMillis()-starttime < 10)
{
}
desktop.screenPaint.drawWindow();
//desktop2.screenPaint.drawWindow();
}
}
private BufferedImage image;
private Graphics2D g;
public Listener screenPaint = new Listener();
private Display identify;
public Display()
{
identify = this;
image = new BufferedImage(620, 500+rh, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D)image.getGraphics();
Timer timer = new Timer(1, screenPaint);
timer.start();
addMouseListener(new mMouse());
addMouseWheelListener(new wWheel());
setFocusable(true);
}
private BufferedImage toCompatibleImage(BufferedImage image)
{
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
if (image.getColorModel().equals(gfx_config.getColorModel()))
return image;
BufferedImage new_image = gfx_config.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
}
public void paint(Graphics view)
{
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
view.drawImage(toCompatibleImage(image), 0, 0, 620, 500+rh, null);
view.dispose();
}
private class Listener implements ActionListener
{
int y = 0;
int wy = 0;
int b = 500;
int c = 1500;
int iy = (int)(wy/(c/(double)b));
int a = -1;
int i = -1;
int j = -1;
boolean d = false;
boolean u = false;
int f = 0;
public void moveY(int sy)
{
f += sy;
}
public void actionPerformed(ActionEvent e)
{
//drawWindow();
}
public void drawWindow()
{
int lx = (int)(identify.getLocation().getX());
int ly = (int)(identify.getLocation().getY());
g.setColor(Color.white);
g.fillRect(0, 0, 620, 500+rh);
g.drawImage(new ImageIcon("image.png").getImage(), 0, wy+rh, 610, c, null);
if(c > b)
{
if(d || Mouse.withinRect(610+lx, (int)(-wy/(c/(double)b))+rh+ly, 10, (int)Math.ceil(Math.pow(b, 2)/(double)c)))
{
if(Mouse.pressLeft())
{
if(!d)d = true;
if(a == -1)a = Mouse.y()-(int)(-wy/(c/(double)b));
y = (int)((Mouse.y()-a)*(c/(double)b));
f = y;
g.setColor(Color.black);
}
else
{
if(d)d = false;
if(a != -1)a = -1;
g.setColor(new Color(60, 60, 60));
}
}
else
{
g.setColor(Color.gray);
if(a != -1)a = -1;
}
if(y == f){}
else if(y < f)
{
y += (int)((f-y)*0.1);
if(y < f)y++;
}
else
{
y -= (int)((y-f)*0.1);
if(y > f)y--;
}
if(y < 0)
{
y = 0;
f = 0;
}
else if(y > c-b)
{
y = c-b;
f = y;
}
wy = -y;
if(u || Mouse.withinRect(lx, ly, 620, 10))
{
if(Mouse.pressLeft())
{
if(!u)u = true;
if(i == -1)i = Mouse.x()-lx;
if(j == -1)j = Mouse.y()-ly;
identify.setLocation(Mouse.x()-i, Mouse.y()-j);
}
else
{
if(u)u = false;
if(i != -1)i = -1;
if(j != -1)j = -1;
}
}
else
{
if(u)u = false;
if(i != -1)i = -1;
if(j != -1)j = -1;
}
int scrollBarLength = (int)Math.ceil(Math.pow(b, 2)/(double)c);
g.fillRect(610, (int)(-wy/(c/(double)b))+rh, 10, scrollBarLength);
}
else
{
g.setColor(new Color(200, 200, 200));
g.fillRect(610, rh, 10, b);
}
g.setColor(new Color(50, 50, 50));
g.fillRect(0, 0, 620, rh);
if(identify == desktop)
repaint();
else if(false)
{}
}
}
}
public static boolean LMPress, LMRelease = false;
public static boolean LMRight, LMLeft = false;
private class mMouse extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
LMPress = true; LMRelease = false;
if(SwingUtilities.isRightMouseButton(e))
{
LMRight = true;
LMLeft = false;
}
else if(SwingUtilities.isLeftMouseButton(e))
{
LMLeft = true;
LMRight = false;
}
}
public void mouseReleased(MouseEvent e)
{
LMRelease = true; LMPress = false;
if(SwingUtilities.isRightMouseButton(e))
{
LMRight = true;
LMLeft = false;
}
else if(SwingUtilities.isLeftMouseButton(e))
{
LMLeft = true;
LMRight = false;
}
}
}
private class wWheel implements MouseWheelListener
{
public void mouseWheelMoved(MouseWheelEvent e)
{
int notches = e.getWheelRotation();
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {}
e.getScrollAmount();
screenPaint.moveY(e.getUnitsToScroll()*60); //desired pixel jump: 120
}
}
}
Mouse class:
import java.awt.*;
import java.awt.event.*;
public class Mouse {
public static int x() {
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
return (int) b.getX();
}
public static int y() {
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
return (int) b.getY();
}
public static boolean withinRect(int x, int y, int w, int h) {
if (x() >= x && x() < x + w && y() >= y && y() < y + h) return true;
else return false;
}
public static boolean press() {
return Display.LMPress;
}
public static boolean release() {
return Display.LMRelease;
}
public static boolean pressLeft() {
if (Display.LMPress && Display.LMLeft)
return true;
else return false;
}
public static boolean releaseLeft() {
if (Display.LMRelease && Display.LMLeft)
return true;
else return false;
}
public static boolean pressRight() {
if (Display.LMPress && Display.LMRight)
return true;
else return false;
}
public static boolean releaseRight() {
if (Display.LMRelease && Display.LMRight)
return true;
else return false;
}
}
Updating the view at 1 kHz is unrealistic; instead, consider the alternatives cited here. See this AnimationTest for an example of self-timing to determine your animation budget.
Addendum: The while(true) loop at the top is running at 100 Hz.
The available resolution of System.currentTimeMillis() may vary by platform.
Addendum: I am reopening this question because I was not able to fix it. Any more help?
Absent a Minimal, Complete, Tested and Readable Example, it's hard to say. You appear to be overriding paint(); was noted in Painting in AWT and Swing: The Paint Methods, "Swing programs should override paintComponent()." Moreover, you need to invoke super.paintComponent(), as shown here, to avoid visual artifacts.

dynamically update JPanel background does't work

After reading image from JFilechooser, I am trying to read pixel of an image one by one and display it to JPanel after some delay in sequential manner. can't update the background of JPanel.
public class ImageMain extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private JPanel panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
//private int loopcount = 0;
//private int counter = 0;
public ImageMain() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new JPanel();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
ImageMain img = new ImageMain();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null)
fc = new JFileChooser();
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
height = image.getHeight();
width = image.getWidth();
// final int[][] pixelData = new int[height * width][3];
// int[] rgb;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
} catch (IOException e1) {
System.out.println("IO::" + e1.getMessage());
} catch (Exception e1) {
System.out.println("Exception::" + e1.getMessage());
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}}
Inside ActionPerformed, I got Color object by reading RGB values and then I am stuck at displaying them to JApplet. Suggestion are welcome if there is a better way to achieve this.
Thanks in advance.
The main problem is your performing a long running task within the context of the Event Dispatching Thread, which is responsible for, amongst other things, processing repaint requests.
#Override
public void actionPerformed(ActionEvent e) {
//...
// Nothing will be updated until after the
// actionPerformed method exists
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
The other problem you have is that you are required to only modify the state of the UI from within the context of the EDT
Depending on your exact needs, you could use a SwingWorker, which would allow you to process the pixels in the background while updating the UI from within the context of the EDT, however, because SwingWorker consolidates it's updates, you could miss color changes.
A better solution might be to use a java.swing.Timer which would allow you to trigger updates at a specified period, which are triggered within the context of the EDT.
See Concurrency in Swing for more details...
Updated with example
In order to draw pixels, you need something to draw them on. Now, you could simply add each pixel you want to paint to an array and loop that array each time you need to repaint the component, but that's kind of expensive...
Instead, it would be simpler to have a backing buffer of some kind, onto which you paint the pixels and then paint that buffer to the component, which should be faster.
Basically, the allows you to supply the height and width of the expected image and then supply each pixel as you need..
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
Take a look at Performing Custom Painting for more details...
Then you need some way to process the original image and update the image panel...Now, based on the updated requirements, I would use a SwingWorker, the reason for this, is that the SwingWorker can cache what is passed back to the EDT, this allows the background thread to continue processing and caching the output until the EDT (and system) is ready to process it...
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
#Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
#Override
protected Void doInBackground() throws Exception {
int pixelCount = (int) (points.size() * 0.005);
while (!points.isEmpty()) {
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
return null;
}
}
Basically, this SwingWorker builds a List of "pixel points", it uses the list to randomly remove points from the list, generate a virtual Pixel and publish back to the EDT for processing. The worker will process around 0.5% of the pixels at a time, meaning that the work is always trying to send a "bunch" of pixels back to the EDT, rather the one at a time.
Finally, it will yield to allow other threads to run (and the hopefully for the EDT to update it self)
An image of 650x975 takes roughly 1m and 10s to fully renderer
And the full code...
import core.util.StopWatch;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
public class PixelShower implements ActionListener {
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private ImagePane panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
public PixelShower() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new ImagePane();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
PixelShower img = new PixelShower();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null) {
fc = new JFileChooser();
}
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
panel.reset(image.getWidth(), image.getHeight());
PixelExposerWorker worker = new PixelExposerWorker(image, panel);
worker.execute();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
public class Pixel {
private int x;
private int y;
private int color;
public Pixel(int x, int y, int color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
#Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
#Override
protected Void doInBackground() throws Exception {
StopWatch sw = StopWatch.newInstance().start();
int pixelCount = (int) (points.size() * 0.005);
System.out.println("pixelCount = " + pixelCount + "; " + points.size());
while (!points.isEmpty()) {
StopWatch sw1 = StopWatch.newInstance().start();
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
System.out.println("Took " + sw.stop());
return null;
}
}
}

Problem with JScrollBar Display

I have added the following code for displaying a scrollbar to my textfield. But it still does not appear. Can someone please help me with this problem. I am unable to figure out where error is occuring:
public JTextArea talkArea = new JTextArea();
public JScrollPane talkAreaScrollPane = new JScrollPane(talkArea);
this.getContentPane(talkArea,null);
this.getContentPane(talkAreaScrollPane,null);
The code for the whole file is as follows and it compiles properly without giving error:
/*
* Client.java
*
*/
package ChatClientRMI;
import javax.naming.*;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import java.rmi.RMISecurityManager;
import ChatServerRMI.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
public class Client extends JFrame implements Runnable, ActionListener {
private static final String connectStr = "Connect";
private static final String disconnectStr = "Disonnect";
private String _nickname;
private Thread _thread;
private Context _initialContext;
private JTextField inputField = new JTextField();
public JTextArea talkArea = new JTextArea;
JScrollPane talkAreaScrollPane = new JScrollPane(talkArea);
private JButton _connectButton;
private JButton _disconnectButton;
private Vector serverVector = new Vector();
private JList serverList = new JList(serverVector);
ChatRoom chatroom = null;
String chatroomName;
Client myFrame = this;
MyActionListener myActionListener = new MyActionListener();
boolean loaded = false;
static int REFRESH_TIME = 200;
// a timer for refresh graphic area
javax.swing.Timer frameTimer =
new javax.swing.Timer(REFRESH_TIME, myActionListener);
// client area info
public static int CLIENT_WIDTH = 800;
public static int CLIENT_HEIGHT = 600;
// left panel info
public static int LEFT_PANEL_WIDTH = 120;
public static int LEFT_PANEL_HEIGHT = 420;
public static int LEFT_PANEL_LEFT = 20;
public static int LEFT_PANEL_TOP = 20;
// graphic area info
public static int GRAPHIC_TOP = 30;
public static int GRAPHIC_LEFT = 30;
public static int GRAPHIC_WIDTH = 400;
public static int GRAPHIC_HEIGHT = 300;
// talk area info
public static int TALK_TOP = GRAPHIC_TOP + GRAPHIC_HEIGHT + 5;
public static int TALK_LEFT = GRAPHIC_LEFT;
public static int TALK_WIDTH = GRAPHIC_WIDTH;
public static int TALK_HEIGHT = 175;
// input field info
public static int INPUT_TOP = TALK_TOP + TALK_HEIGHT + 25;
public static int INPUT_LEFT = GRAPHIC_LEFT;
public static int INPUT_WIDTH = GRAPHIC_WIDTH;
public static int INPUT_HEIGHT = 20;
// server list info
public static int SERVER_LIST_TOP = GRAPHIC_TOP;
public static int SERVER_LIST_LEFT = GRAPHIC_LEFT + GRAPHIC_WIDTH + 160;
public static int SERVER_LIST_WIDTH = 120;
public static int SERVER_LIST_HEIGHT = GRAPHIC_HEIGHT; // 420;
// user list info
public static int USER_LIST_TOP = 20;
public static int USER_LIST_LEFT = GRAPHIC_LEFT + GRAPHIC_WIDTH + 10;
public static int USER_LIST_WIDTH = 120;
public static int USER_LIST_HEIGHT = 420;
public static int SHADOW_WIDTH = 5;
// background color
static Color backColor = new Color(130, 60, 170);
// command label
static final String CMD_LABEL[] =
{ "change icon", "query friend", "change location", "open room",
"query hero", "help", "temp leave", "leave" };
// icon info
public static final int MAX_ICONS = 100;
public static final String ICON_FILENAME = "icons.gif";
public static int ICON_WIDTH = 32;
public static int ICON_HEIGHT = 32;
Image icons[] = new Image[MAX_ICONS];
int totalIcons = 16;
static String BACKIMG_FILENAME[] =
{ "back0.jpg", "back1.jpg", "back2.jpg", "back3.jpg" };
Image backImg = null;
Image leftPanelImg = null;
Image graphicImg = null;
Image userListImg = null;
Image serverListImg = null;
// user info
static int MAX_USERS = 100;
UserInfo userInfo[] = new UserInfo[MAX_USERS];
int totalUsers = 0;
int myIdx = 0;
Hashtable users = new Hashtable();
// say delay
static int SAY_TIME = 15;
// say rectangle's width
static int SAY_WIDTH = 100;
// move step
static int ONE_STEP = 10;
boolean endChat = true;
boolean moveEnd = true;
boolean sayEnd = true;
int enterListIndex = -1;
int exitListIndex = -1;
/** Creates new ChatClient */
public Client(String name) {
super(name);
_nickname = name;
try {
_initialContext = new InitialContext();
} catch (Exception e) {
System.out.println(e);
}
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
setSize(new Dimension(CLIENT_WIDTH, CLIENT_HEIGHT));
this.getContentPane().setLayout(null);
this.getContentPane().setBackground(backColor);
talkArea.setEditable(false);
talkArea.setBackground(Color.white);
talkArea.setBounds(new Rectangle(TALK_LEFT, TALK_TOP, TALK_WIDTH,
TALK_HEIGHT));
this.getContentPane().add(talkArea, null);
// set input area
inputField.setBackground(Color.white);
inputField.setBounds(new Rectangle(INPUT_LEFT, INPUT_TOP, INPUT_WIDTH,
INPUT_HEIGHT));
inputField.addActionListener(this);
this.getContentPane().add(inputField, null);
this.getContentPane().add(talkAreaScrollPane, null);
// connect button
_connectButton = new JButton(connectStr);
_connectButton.setBounds(new Rectangle(600, 400, 100, 30));
_connectButton.addActionListener(this);
this.getContentPane().add(_connectButton);
// disconnect button
_disconnectButton = new JButton(disconnectStr);
_disconnectButton.setBounds(new Rectangle(600, 450, 100, 30));
_disconnectButton.setEnabled(false);
// _disconnectButton.addActionListener(this);
this.getContentPane().add(_disconnectButton);
// testButton = new JButton("test");
// testButton.setBounds(new Rectangle(600,500,100,30));
// testButton.addActionListener(this);
// this.getContentPane().add(testButton);
for (int i = 0; i < 5; i++) {
serverVector.add("ChatRoom" + i);
}
serverList.setBackground(new Color(190, 180, 255));
serverList.setBounds(new Rectangle(SERVER_LIST_LEFT + 2,
SERVER_LIST_TOP + 40, SERVER_LIST_WIDTH - SHADOW_WIDTH - 10,
SERVER_LIST_HEIGHT - 40 - 50));
serverList.setSelectedIndex(0);
serverList.setCellRenderer(new CustomCellRenderer());
this.getContentPane().add(serverList);
// add mouse listener for serverList
serverList.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(MouseEvent e) {
int index = serverList.locationToIndex(e.getPoint());
enterListIndex = index;
// setForeground(new Color(0,0,255));
System.out.println("you entered index " + index);
}
public void mouseExited(MouseEvent e) {
int index = serverList.locationToIndex(e.getPoint());
exitListIndex = index;
// setForeground(new Color(0,255,255));
System.out.println("you exited index " + index);
}
});
// add mouse listener
this.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent event) {
mouseClick_performed(event);
}
});
// Always need this to enable closing the frame
this.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
if (endChat) {
System.exit(0);
return;
}
boolean success = false;
try {
success = chatroom.disconnect(_nickname);
} catch (java.rmi.RemoteException err) {
System.out.println(err);
}
if (success)
System.out.println("Disonnected...");
else
System.out.println("Not disconnected.");
System.exit(0);
}
});
// Wait for incoming requests
this.startThread();
// Enable GUI
this.setVisible(true);
// create offscreen images
leftPanelImg = createImage(LEFT_PANEL_WIDTH, LEFT_PANEL_HEIGHT);
graphicImg = createImage(GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
userListImg = createImage(USER_LIST_WIDTH, USER_LIST_HEIGHT);
serverListImg = createImage(SERVER_LIST_WIDTH, SERVER_LIST_HEIGHT);
drawServerList();
serverList.repaint();
(new LoadImageThread()).load();
try {
_initialContext.rebind(_nickname, new ChatUserImpl(this));
} catch (Exception e) {
System.out.println(e);
}
}
public void actionPerformed(java.awt.event.ActionEvent evt) {
Object source = evt.getSource();
if (source == _connectButton) {
if (serverList.getSelectedIndex() == -1) {
JOptionPane.showMessageDialog(this, "please select a chatroom",
"Error Dialog", JOptionPane.ERROR_MESSAGE);
return;
}
_connectButton.removeActionListener(this);
_connectButton.setEnabled(false);
_disconnectButton.addActionListener(this);
_disconnectButton.setEnabled(true);
inputField.addActionListener(this);
chatroomName = (String) serverList.getSelectedValue();
int code = (new Random()).nextInt(totalIcons - 1);
boolean success = false;
try {
System.out.println("chat room name: " + chatroomName);
chatroom =
(ChatRoom) PortableRemoteObject.narrow(_initialContext
.lookup(chatroomName), ChatRoom.class);
success = chatroom.connect(_nickname, code);
} catch (Exception e) {
System.out.println("ChatUserClient exception: " + e.getMessage());
e.printStackTrace();
}
if (success) {
System.out.println("Connected...");
endChat = false;
frameTimer.start();
} else {
System.out
.println("Not connected: the selected nickname is in use. Please choose another nickname.");
}
} else if (source == _disconnectButton) {
_connectButton.addActionListener(this);
_connectButton.setEnabled(true);
_disconnectButton.removeActionListener(this);
_disconnectButton.setEnabled(false);
inputField.removeActionListener(this);
// clear everything
talkArea.setText("");
inputField.setText("");
if (backImg == null) {
Graphics g = graphicImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
getGraphics().drawImage(graphicImg, GRAPHIC_LEFT, GRAPHIC_TOP, this);
} else {
getGraphics().drawImage(backImg, GRAPHIC_LEFT, GRAPHIC_TOP, this);
}
users.clear();
if (endChat) {
return;
}
boolean success = false;
try {
success = chatroom.disconnect(_nickname);
} catch (java.rmi.RemoteException e) {
System.out.println(e);
}
if (success) {
endChat = true;
System.out.println("Disonnected...");
} else {
System.out.println("Not disconnected.");
}
} else if (source == inputField) {
String message = inputField.getText();
try {
chatroom.sendMessage(message, _nickname);
} catch (java.rmi.RemoteException e) {
System.out.println(e);
}
}
} // end actionperformed
public void startThread() {
_thread = new Thread(this);
_thread.start();
}
public void run() {
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
super.paint(g);
if (loaded) {
g.drawImage(graphicImg, GRAPHIC_LEFT, GRAPHIC_TOP, this);
g.drawImage(serverListImg, SERVER_LIST_LEFT, SERVER_LIST_TOP, this);
serverList.repaint();
}
}
// sgn func
public int sgn(int x) {
if (x > 0)
return 1;
if (x < 0)
return -1;
return 0;
}
// everyone move one step
public void moveOneStep() {
int count = 0;
int direction;
for (Enumeration e = users.elements(); e.hasMoreElements();) {
UserInfo p = (UserInfo) e.nextElement();
direction = sgn(p.dx - p.x);
if (direction == 0)
count++;
p.x += direction * ONE_STEP;
direction = sgn(p.dy - p.y);
if (direction == 0)
count++;
p.y += direction * ONE_STEP;
if (java.lang.Math.abs(p.x - p.dx) <= ONE_STEP)
p.x = p.dx;
if (java.lang.Math.abs(p.y - p.dy) <= ONE_STEP)
p.y = p.dy;
if (p.x <= ICON_WIDTH / 2) {
p.x = ICON_WIDTH / 2;
p.dx = p.x;
}
if (p.x >= GRAPHIC_WIDTH - ICON_WIDTH / 2) {
p.x = GRAPHIC_WIDTH - ICON_WIDTH / 2;
p.dx = p.x;
}
if (p.y <= ICON_HEIGHT / 2) {
p.y = ICON_HEIGHT / 2;
p.dy = p.y;
}
if (p.y >= GRAPHIC_HEIGHT - ICON_HEIGHT / 2) {
p.y = GRAPHIC_HEIGHT - ICON_HEIGHT / 2;
p.dy = p.y;
}
}
moveEnd = (count == users.size() * 2);
System.out.println("count = " + count);
System.out.println("size = " + users.size());
System.out.println("messgeEnd = " + moveEnd);
} // end of moveOneStep
// timer action
public void timer_actionPerformed() {
if (endChat)
return;
if (!moveEnd)
moveOneStep();
if (moveEnd && sayEnd)
return;
drawGraphicArea();
getGraphics().drawImage(graphicImg, GRAPHIC_LEFT, GRAPHIC_TOP, this);
}
// mouse event
public void mouseClick_performed(java.awt.event.MouseEvent event) {
if (endChat)
return;
// if (endChat == true || myIdx == -1) return;
// if (userInfo[myIdx].x < 0) return;
if (event.getID() == event.MOUSE_PRESSED) {
int x = event.getX();
int y = event.getY();
if (x < GRAPHIC_LEFT || x >= GRAPHIC_LEFT + GRAPHIC_WIDTH
|| y < GRAPHIC_TOP || y > GRAPHIC_TOP + GRAPHIC_HEIGHT)
return;
moveEnd = false;
UserInfo p = (UserInfo) users.get(_nickname);
p.dx = x - GRAPHIC_LEFT;
p.dy = y - GRAPHIC_TOP;
try {
chatroom.sendLocation(p.dx, p.dy, p.name);
} catch (java.rmi.RemoteException e) {
System.out.println(e);
}
// sendCmd(MsgType.MOVE, userInfo[myIdx].dx, userInfo[myIdx].dy);
}
}
public void printUserList() {
Enumeration usernames = users.keys();
while (usernames.hasMoreElements()) {
System.out.println("user name: " + usernames.nextElement());
}
}
// draw server list
public void drawServerList() {
Graphics g = serverListImg.getGraphics();
g.setColor(backColor);
g.fillRect(0, 0, SERVER_LIST_WIDTH, SERVER_LIST_HEIGHT);
g.setColor(Color.black);
g.fillRoundRect(5, 5, SERVER_LIST_WIDTH - SHADOW_WIDTH, SERVER_LIST_HEIGHT
- SHADOW_WIDTH, 30, 30);
g.setColor(new Color(190, 180, 255));
g.fillRoundRect(0, 0, SERVER_LIST_WIDTH - SHADOW_WIDTH, SERVER_LIST_HEIGHT
- SHADOW_WIDTH, 30, 30);
g.setColor(new Color(0, 0, 255));
g.drawRoundRect(0, 0, SERVER_LIST_WIDTH - SHADOW_WIDTH, SERVER_LIST_HEIGHT
- SHADOW_WIDTH, 30, 30);
g.setFont(new Font(g.getFont().getName(), g.getFont().getStyle(), 20));
g.setColor(Color.black);
FontMetrics fntM = g.getFontMetrics();
String s = new String("Server List");
int x = (SERVER_LIST_WIDTH - fntM.stringWidth(s)) / 2;
g.drawString(s, x, 30);
// update to screen
getGraphics().drawImage(serverListImg, SERVER_LIST_LEFT, SERVER_LIST_TOP,
this);
}
// draw graphic area
public synchronized void drawGraphicArea() {
Graphics g = graphicImg.getGraphics();
FontMetrics fntM = g.getFontMetrics();
if (backImg == null) {
g.setColor(Color.blue);
g.fillRect(0, 0, GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
} else {
g.drawImage(backImg, 0, 0, this);
}
// if (myIdx == -1) return ;
UserInfo p;
g.setFont(new Font(g.getFont().getName(), g.getFont().getStyle(), 12));
int count = 0;
for (Enumeration e = users.elements(); e.hasMoreElements();) {
// draw icon
p = (UserInfo) e.nextElement();
g.drawImage(icons[p.code], p.x - ICON_WIDTH / 2, p.y - ICON_HEIGHT / 2,
this);
// draw name
if (p.name.equals(_nickname))
g.setColor(Color.red);
else
g.setColor(Color.yellow);
int x = (p.x - fntM.stringWidth(p.name) / 2);
int y = (p.y + ICON_HEIGHT / 2);
g.fillRoundRect(x - 2, y, fntM.stringWidth(p.name) + 4, fntM.getHeight(),
10, 10);
g.setColor(Color.black);
g.drawRoundRect(x - 2, y, fntM.stringWidth(p.name) + 4, fntM.getHeight(),
10, 10);
g.drawString(p.name, x, y + fntM.getAscent());
// draw say
if (p.sayTime <= 0) {
count++;
continue;
}
String saySplit[] = new String[100];
int c = 0;
int st = 0, ed = 1;
while (ed <= p.say.length()) {
String s = p.say.substring(st, ed);
if (fntM.stringWidth(s) > SAY_WIDTH) {
saySplit[c] = p.say.substring(st, ed - 1);
c++;
st = ed - 1;
}
ed++;
}
saySplit[c] = p.say.substring(st, ed - 1);
c++;
x = p.x + ICON_WIDTH / 2 + 5;
y = p.y - ICON_HEIGHT / 2 + 5;
int w = ((c > 1) ? SAY_WIDTH : fntM.stringWidth(saySplit[0])) + 5;
int h = fntM.getHeight() * c + 5;
// draw say arrow
g.setColor(Color.green);
if (x + w >= GRAPHIC_WIDTH) {
x = p.x - ICON_WIDTH / 2 - w - 5;
Polygon polygon = new Polygon();
polygon.addPoint(p.x - ICON_WIDTH / 2, p.y - 5);
polygon.addPoint(p.x - ICON_WIDTH / 2 - 8, p.y - 10);
polygon.addPoint(p.x - ICON_WIDTH / 2 - 8, p.y - 4);
// p.addPoint(x + ICON_WIDTH/2, y - 5);
g.fillPolygon(polygon);
} else {
Polygon polygon = new Polygon();
polygon.addPoint(p.x + ICON_WIDTH / 2, p.y - 5);
polygon.addPoint(p.x + ICON_WIDTH / 2 + 8, p.y - 10);
polygon.addPoint(p.x + ICON_WIDTH / 2 + 8, p.y - 4);
// p.addPoint(x + ICON_WIDTH/2, y - 5);
g.fillPolygon(polygon);
}
g.fillRoundRect(x, y, w, h, 10, 10);
g.setColor(Color.black);
// g.drawRoundRect(x, y, w, h, 10, 10);
for (int j = 0; j < c; j++) {
g.drawString(saySplit[j], x + 2, y + 2 + j * fntM.getHeight()
+ fntM.getAscent());
}
p.sayTime--;
} // end of for
sayEnd = (count == users.size());
update(getGraphics());
} // end of drawGraphicArea
class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Object obj = e.getSource();
if (obj == frameTimer) {
timer_actionPerformed();
return;
}
}
}
// ****************************************
// a load image thread class in applet class
// ****************************************
class LoadImageThread extends Thread {
public void load() {
this.start();
}
public void run() {
loadImages();
loaded = true;
try {
sleep(1000);
} catch (java.lang.InterruptedException e) {
}
if (backImg == null) {
Graphics g = graphicImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
(myFrame.getGraphics()).drawImage(graphicImg, GRAPHIC_LEFT,
GRAPHIC_TOP, myFrame);
} else {
myFrame.getGraphics().drawImage(backImg, GRAPHIC_LEFT, GRAPHIC_TOP,
myFrame);
}
}
// load all images
public void loadImages() {
Graphics g = graphicImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
g.setColor(Color.yellow);
g.setFont(new Font(g.getFont().getName(), g.getFont().getStyle(), 30));
g.drawString("loading, please wait ......", 30, 50);
(myFrame.getGraphics()).drawImage(graphicImg, GRAPHIC_LEFT, GRAPHIC_TOP,
myFrame);
MediaTracker m = new MediaTracker(myFrame);
for (int i = 0; i < totalIcons; i++) {
icons[i] = Toolkit.getDefaultToolkit().getImage(i + ".gif");
m.addImage(icons[i], 0);
}
try {
m.waitForAll();
} catch (InterruptedException e) {
System.out.println("can't read image from file");
}
}
} // end of LoadImage class
class CustomCellRenderer extends JLabel implements ListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
String s = value.toString();
setText(s);
// setIcon((s.length() > 10) ? longIcon : shortIcon);
if (isSelected) {
// setBackground(list.getSelectionBackground());
// setForeground(list.getSelectionForeground());
setForeground(new Color(0, 0, 255));
} else {
// setBackground(list.getBackground());
// setForeground(list.getForeground());
setForeground(new Color(0, 255, 255));
}
/*
* if ( index == enterListIndex ){ System.out.println("****************");
* setForeground(new Color(0,0,180)); } if ( index == exitListIndex ){
* System.out.println("---------------"); setForeground(new
* Color(0,255,255)); }
*/
setEnabled(list.isEnabled());
setFont(list.getFont());
return this;
}
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
// args[0] is user nickname
if (args.length != 1) {
System.out.println("Usage: ChatClient nickname");
System.exit(0);
}
Client clientFrame = new Client(args[0]);
}
}
Any idea???
Do I need to set any kind of visibility??
Thanks
No, add the textArea to the ScrollPane then put the ScrollPane in your panel.
As a side note I would suggest learning more about LayoutManagers. They are worth the learning curve.
Example:
//In a container that uses a BorderLayout:
textArea = new JTextArea(5, 30);
...
JScrollPane scrollPane = new JScrollPane(textArea);
...
setPreferredSize(new Dimension(450, 110));
...
add(scrollPane, BorderLayout.CENTER);
Taken from: How to use scroll panes
You're adding both the text area, AND the scrollpane containing it to the content pane.
you're also setting sizes on the textarea, but not the scrollpane. and since your layout is managing all the bounds, the scrollpane is probably 0x0 at 0,0
setSize(new Dimension(CLIENT_WIDTH, CLIENT_HEIGHT));
this.getContentPane().setLayout(null);
this.getContentPane().setBackground(backColor);
talkArea.setEditable(false);
talkArea.setBackground(Color.white);
talkArea.setBounds(new Rectangle(TALK_LEFT, TALK_TOP, TALK_WIDTH, TALK_HEIGHT));
this.getContentPane().add(talkArea, null); // <--- you want this inside the text area, not here!
// set input area
inputField.setBackground(Color.white);
inputField.setBounds(new Rectangle(INPUT_LEFT, INPUT_TOP, INPUT_WIDTH, INPUT_HEIGHT));
inputField.addActionListener(this);
this.getContentPane().add(inputField, null);
this.getContentPane().add(talkAreaScrollPane, null); // <--- you never set the size on here
why are you getting rid of the layout and then managing everything explicitly for size? this would be a lot simpler if you used something like borderlayout and preferred sizes and let those things manage bounds for you.

Categories