I'm having trouble retrieving the variable from my Colour class after i send the index from the GUI after selecting a colour from the drop down list. I can send the index fine and retrieve it from the HashMap, i know this because i use System.out.println to check. Basically my questions are, where have i gone wrong? and What do i need to remember to make sure i don't have this trouble again? Edit: forgot to mention, the button sending the index is in a seperate JPanel which is used for the UI components(buttons and combo boxes).
//edit
class UIPanel extends JPanel{
public MainPanel gpanel;
public Integer data;
public Color colval;
public Colour col;
public UIPanel(MainPanel panel) {
col = new Colour();
gpanel = panel;
Box btnBox = Box.createHorizontalBox();
btnBox.add(setBtn = new JButton());
btnBox.add(Box.createHorizontalGlue());
JButton setBtn = new JButton("Set");
final DefaultComboBoxModel colour = new DefaultComboBoxModel();
colour.addElement("Red");
final JComboBox colours = new JComboBox(colour);
JScrollPane colourScroll = new JScrollPane(colours);
btnBox.setSize(300, 100);
btnBox.add(Box.createHorizontalGlue());
add(btnBox, BorderLayout.NORTH);
//end of edit
Button to send Index from GUI class to Colour class
setBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
data= colours.getSelectedIndex();
col.setCol(data);
}
});
Colour Class where hashmap with the list of colours in.
public class Colour{
public Color colVal;
HashMap<Integer, Color> map = new HashMap<Integer, Color>();
public Colour() {
map.put(0, Color.RED);
map.put(1, Color.BLUE);
map.put(2, Color.YELLOW);
map.put(3, Color.GREEN);
}
public Color setCol(Integer data) {
//Color colours;
colVal = map.get(data);
System.out.println("colour" + colVal);
return colVal;
}
public Color getColVal() {
return colVal;
}
And the paint area on the GUI class where the colour will be sent to from the colour class
class MainPanel extends JPanel{
//private Colour col;
int px, py;
//radius
public Color colvals;
public Colour col;
public MainPanel() {
col = new Colour();
this.addMouseMotionListener(new MouseMotionAdapter() {
// store drag coordinates and repaint
public void mouseDragged( MouseEvent event )
{
px = event.getX();
py = event.getY();
repaint();
}
}); // end call to addMouseMotionListener
}
public void paint( Graphics g ) {
g.setColor(col.colVal);//This is where the colour value will be placed
System.out.println(col.colVal);
g.fillOval( px, py, 15, 15 );
}
}
I'm Probably missing something stupid out but I cant seem to figure it out.
P.S: How complicated will it be to make a Vignere Cipher Application?
JComboBox can be used to directly use any Objects as items with only one little thing to consider: It will use the toString for the label to display. (see JComboBox javadoc - Providing a Custom Renderer).
But instead of using a custom renderer I always preferred to write a very simple helper class once - let's call it ComboBoxItem - that is reusable for any kind of data.
public class ComboBoxItem<T>
{
private T value;
private String label;
public ComboBoxItem(T value, String label)
{
this.value = value;
this.label = label;
}
public T getValue()
{
return this.value;
}
public String getLabel()
{
return this.label;
}
// important! since this is the workaround ;-)
public String toString()
{
return this.label; // or whatever you like
}
}
And then populate the JComboBox with ComboBoxItems instead of String values:
In your code instead of
final DefaultComboBoxModel colour = new DefaultComboBoxModel();
colour.addElement("Red");
colour.addElement("Blue");
colour.addElement("Yellow");
colour.addElement("Green");
colours = new JComboBox(colourValues);
... you will use
final DefaultComboBoxModel colour = new DefaultComboBoxModel();
colour.addElement(new ComboBoxItem<Color>(Color.RED, "Red"));
colour.addElement(new ComboBoxItem<Color>(Color.BLUE, "Blue"));
colour.addElement(new ComboBoxItem<Color>(Color.YELLOW, "Yellow"));
colour.addElement(new ComboBoxItem<Color>(Color.GREEN, "Green"));
colours = new JComboBox(colourValues);
This will make the select contain ComboBoxItems as values which you can simply access by doing the following:
// instead of getSelectedIndex()
ComboBoxItem<Color> item = (ComboBoxItem) colours.getSelectedItem();
Color c = item.getValue();
The same procedure can then be reused for any other kind of values - even complex ones.
Note: If you have a data object with an appropriate toString() representation anyway, you can of course simply use it as a value for the select.
Note2: If a string representation is not enough (e.g. you want to display the color along with the name), have a look at ListCellRenderer which is able to display the item in any desired way (by returning an arbitrary JComponent).
Your setCol(...) method inside of the Colour class should be getCol(...) since it's functioning as a getter:
public class Colour{
public Color colVal;
HashMap<Integer, Color> map = new HashMap<Integer, Color>();
public Colour() {
map.put(0, Color.RED);
map.put(1, Color.BLUE);
map.put(2, Color.YELLOW);
map.put(3, Color.GREEN);
}
// **** change name ****
public Color getCol(Integer data) {
//Color colours;
colVal = map.get(data);
System.out.println("colour" + colVal);
return colVal;
}
// **** not sure you need this method
public Color getColVal() {
return colVal;
}
and in your ActionListener, you retrieve the color but never do anything with it. It should be:
public void actionPerformed(ActionEvent e) {
data = colours.getSelectedIndex();
Color color = col.getCol(data); // note name change
// use Color variable, color, somehow here
mainPanel.setColVals(color); // something like this perhaps
mainPanel.repaint(); // to tell the JVM to repaint the JPanel
}
Also note that in your JPanel class override you should override the paintComponent method, not the paint method, and don't forget to call the super's method.
i.e.,
public void setColVals(Color colVals) {
this.colVals = colVals;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(colVal);
// System.out.println(colVal);
g.fillOval( px, py, 15, 15 );
}
Edit a better answer:
Get rid of Colour entirely.
Use an enum to match your Color to a String and create a JComboBox model out of that enum.
Using an enum will prevent you're having to use magic numbers with the risk that the wrong number has been used, a number that doesn't match a color.
Also, by using an enum, it is trivial to change your code and add more colors. Just add a new item to the enum, and the rest of the program will adapt to the change.
Add a PropertyChangeListener from the MainPanel to the UIPanel and listen for changes to its "bound" Color property.
Use RenderingHints with a Graphics2D object to smooth out the jaggies from your circle drawing.
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class ColorListenerPanel extends JPanel {
public ColorListenerPanel() {
UIPanel uiPanel = new UIPanel();
MainPanel mainPanel = new MainPanel(uiPanel);
setLayout(new BorderLayout());
add(mainPanel, BorderLayout.CENTER);
add(uiPanel, BorderLayout.PAGE_START);
}
private static void createAndShowGui() {
ColorListenerPanel mainPanel = new ColorListenerPanel();
JFrame frame = new JFrame("ColorListenerPanel");
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();
}
});
}
}
class MainPanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final int OVAL_WIDTH = 16;
private int px, py;
private Color color = MyColors.values()[0].getColor();
public MainPanel(UIPanel uiPanel) {
this.addMouseMotionListener(new MouseMotionAdapter() {
// store drag coordinates and repaint
public void mouseDragged(MouseEvent event) {
px = event.getX();
py = event.getY();
repaint();
}
});
uiPanel.addPropertyChangeListener(UIPanel.COLOR, new UiListener());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(px - OVAL_WIDTH / 2, py - OVAL_WIDTH / 2, OVAL_WIDTH, OVAL_WIDTH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class UiListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// not really needed since our listener is added using
// this property name
if (!UIPanel.COLOR.equals(pcEvt.getPropertyName())) {
return;
}
color = (Color) pcEvt.getNewValue();
repaint();
}
}
}
enum MyColors {
RED("Red", Color.RED),
BLUE("Blue", Color.BLUE),
YELLOW("Yellow", Color.YELLOW),
GREEN("Green", Color.GREEN);
private String name;
private Color color;
private MyColors(String name, Color color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
#Override
public String toString() {
return name;
}
}
class UIPanel extends JPanel {
public static final String COLOR = "color";
private MainPanel gpanel;
private Integer data;
private Color color;
private DefaultComboBoxModel<MyColors> comboModel = new DefaultComboBoxModel<>();
private JComboBox<MyColors> colorsCombo = new JComboBox<>(comboModel);
SetColorAction setColorAction = new SetColorAction("Set", KeyEvent.VK_S);
public UIPanel() {
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(colorsCombo);
add(Box.createHorizontalStrut(5));
add(new JButton(setColorAction));
colorsCombo.addActionListener(setColorAction);
add(Box.createHorizontalGlue());
for (MyColors myColor : MyColors.values()) {
comboModel.addElement(myColor);
}
}
public void setColor(Color color) {
Color oldValue = this.color;
Color newValue = color;
this.color = color;
firePropertyChange(COLOR, oldValue, newValue);
}
private class SetColorAction extends AbstractAction {
public SetColorAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent evt) {
MyColors selection = (MyColors) colorsCombo.getSelectedItem();
if (selection != null) {
setColor(selection.getColor());
}
}
}
}
In this Function
public void paint( Graphics g ) {
g.setColor(col.colVal);//This is where the colour value will be placed
System.out.println(col.colVal);
g.fillOval( px, py, 15, 15 );
}
You are using g.setColor(col.colVal); col.colVal will give you the default value assigned to it probably null ,You should use col.getColVal() as this is the getter method you have created for the colVal attribute.
And also in the setter declaration public Color setCol(Integer data) the return type you are using is Color but when you are using this function in your GUI class without a Color variable which can accept a Value returned by your setter .
I don't understand the need of returning values from a Setter method.
Related
I'm building an app where user can upload an image and draw rectangles on top of it for annotation and also attach comments to that certain region/rectangle, like in Word or Docs. I'm a bit confused on how to make the connection between the classes to pass information around. Once user draws a rectangle and clicks on it, they will be able to write comments in the JTextField provided, and the program will associate that rectangle with that comment. I have three classes Box, DrawingArea, and ImageAnnotator. One of the fields/properties of Box (which is the rectangle) is bComment, and inside the ImageAnnotator class I have a JButton which upon click, will need to set the String retrieved from the JTextField in the ImageAnnotator class to Box.bComment. I know the onClick method should be inside the ImageAnnotator class since that's where the button is but I'm a bit confused to how to pass this String over to Box. Should I import Box inside the ImageAnnotator and set it within an onClick method? So now the reverse way, upon clicking the rectangle, how do I set the JTextField in ImageAnnotator with the String retrieved from Box.bComment, that is, when the user clicks on the same rectangle again, the program will display the previously added comment in the text field. All the listeners for clicking on rectangle is inside the DrawingArea class so I would need to somehow get the Text field from ImageAnnotator and fill the text field with String retrieved from Box.bComment. For more clarity, I have provided the classes below.
ImageAnnotator.java:
public class ImageAnnotator extends JFrame {
private JPanel contentPane;
Model model;
DrawingArea drawingArea;
GroupLayout gl_contentPane;
private JTextField textField;
private JButton btnNewButton;
/**
* Create the frame.
*/
public ImageAnnotator(Model m) {
super();
this.model = m;
setTitle("Image Annotator");
...
setContentPane(contentPane);
drawingArea = new DrawingArea();
//ImageName = new JLabel(drawingArea.imageName);
buttonPanel = new ButtonPanel( drawingArea );
textField = new JTextField(); // this is the text field which will be later set to Box.bComment
btnNewButton = new JButton("Add this comment");//this is the button which needs onClick listener
gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(...);
gl_contentPane.setVerticalGroup(...);
contentPane.add(drawingArea);
contentPane.setLayout(gl_contentPane);
}
// onClick listener should be here for JButton
}
Box.java:
public class Box {
int bWidth, bHeight, bX, bY;
String bImageName, bComment;
Color foreground;
Rectangle rectangle;
public Box(int width, int height) {
bWidth = width;
bHeight = height;
}
public Box(Color foreground, Rectangle rectangle) {
this.foreground = foreground;
this.rectangle = rectangle;
}
public void setComment(String comment) { bComment = comment; }
public String getComment() { return bComment; }
public void setX(int x) { bX = x; }
public int getX() { return bX; }
public void setY(int y) { bY = y; }
public int getY() { return bY; }
public Rectangle getRectangle()
{
return rectangle;
}
}
DrawingArea.java:
public class DrawingArea extends JPanel implements BoxSelectionListener
{
private final static int AREA_SIZE = 490;
private BufferedImage image =
new BufferedImage(AREA_SIZE, AREA_SIZE, BufferedImage.TYPE_INT_ARGB);
private Rectangle shape;
private ArrayList<Box> rectangles = new ArrayList<Box>();
//public String imageName = ""; // this will store the image/file name
public DrawingArea()
{
setBackground(Color.WHITE);
MyMouseListener ml = new MyMouseListener();
addMouseListener(ml);
addMouseMotionListener(ml);
}
public void addBoxSelectionListener(BoxSelectionListener listener) {
listenerList.add(BoxSelectionListener.class, listener);
}
public void removeBoxSelectionListener(BoxSelectionListener listener) {
listenerList.remove(BoxSelectionListener.class, listener);
}
protected void fireBoxSelected(Box box) {
BoxSelectionListener[] listeners = listenerList.getListeners(BoxSelectionListener.class);
// Normally, I'd create a event object, which would wrap the source (this) and
// the Box together, but if there are no listeners, it's a bit of
// a waste to do so, so I return early in those cases
if (listeners.length == 0) {
return;
}
for (BoxSelectionListener listener : listeners) {
listener.didSelect(box);
}
}
public void didSelect(Box box) {
// Probably assign this to a "assigned" or "selected" property
// so it can painted differently
// And now we want to notify some kind of listener so that
// it can update the UI as required
box.setForeground(Color.red);
box.getComment();
repaint();
fireBoxSelected(box);
}
class MyMouseListener extends MouseInputAdapter
{
private Point startPoint;
public void mousePressed(MouseEvent e) {
// Mark the clip point
startPoint = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
// Only create the shape when dragging starts
if (shape == null) {
shape = new Rectangle();
}
int x = Math.min(startPoint.x, e.getX());
int y = Math.min(startPoint.y, e.getY());
int width = Math.abs(startPoint.x - e.getX());
int height = Math.abs(startPoint.y - e.getY());
shape.setBounds(x, y, width, height);
repaint();
}
public void mouseReleased(MouseEvent e) {
if (shape != null) {
if (shape.width != 0 || shape.height != 0) {
addRectangle(shape, e.getComponent().getForeground());
}
} else {
for (Box b : rectangles) {
if (b.getRectangle().contains(e.getPoint())) {
didSelect(b);
break;
}
else
b.setForeground(Color.black);
}
}
startPoint = null;
shape = null;
}
}
}
I was able to get those working by making some fields static (the TextField) so that I can reference it from another class, and my mistake was that I was creating instances of the ImageAnnotator class in my DrawingArea class so when I try to reference the TextField from ImageAnnotator inside DrawingArea, it was null. So my solution was instead I called ImageAnnotator directly without creating an instance and I set the TextField to static.
I am having an issue when repainting and keeping all drawn objects on the screen. Basically from my knowledge of Java (Isn't much) is that every time I repaint I am basically calling the draw method of the same object hence therefore just repainting the same object instead of a new object. On mouseRelease I am adding the shape to a list. Every element reference I assume refers to the same shape in memory and therefore calling the same draw method. How would you solve this but still keep the comboBox object oriented? I would prefer not to use strings and a switch case or else if logic.
The draw method which is same for oval and rectangle except the last line.
#Override
public void draw(Graphics context)
{
Point startingPoint = super.getStartingPoint();
Point endingPoint = super.getEndingPoint();
int minX = Math.min(endingPoint.x, startingPoint.x);
int minY = Math.min(endingPoint.y, startingPoint.y);
int maxX = Math.max(endingPoint.x, startingPoint.x);
int maxY = Math.max(endingPoint.y, startingPoint.y);
context.drawOval(minX, minY, maxX - minX, maxY - minY);
}
The JPanel which has the components and the drawing.
public class ShapeMakerPanel
extends JPanel
{
private JPanel controls;
private JPanel currentColor;
private JComboBox<Shape> shapeChoice;
private JCheckBox filled;
private JButton undo;
private JButton clear;
private List<Shape> list;
public ShapeMakerPanel()
{
//Initialize Objects
controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
currentColor = new JPanel();
shapeChoice = new JComboBox<Shape>();
undo = new JButton("Undo");
clear = new JButton("Clear");
filled = new JCheckBox("Filled");
list = new ArrayList<Shape>();
//Set object names
controls.setName("controls");
currentColor.setName("currentColor");
undo.setName("undo");
clear.setName("clear");
shapeChoice.setName("shapeChoice");
//Set JPanel current color to black and size it...will add jcolorchooser
currentColor.setBackground(Color.BLACK);
currentColor.setPreferredSize(new Dimension(25, 25));
/*
* Add items to comboBox, subclasses of type shape
*/
shapeChoice.addItem(new Rectangle());
shapeChoice.addItem(new Oval());
//Add components to the controls panel
controls.add(shapeChoice);
controls.add(currentColor);
controls.add(filled);
controls.add(undo);
controls.add(clear);
//Add listeners, settings and components to main panel for frame
addMouseListener(new ShapePanelMouseListener());
addMouseMotionListener(new ShapePanelMouseListener());
setBackground(Color.WHITE);
add(controls);
}
public List<Shape> getShapes()
{
return list;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
if (!list.isEmpty())
{
for (Shape s : list) {
g2d.setColor(currentColor.getBackground());
s.draw(g2d);
}
}
g2d.dispose();
}
private class ShapePanelMouseListener
extends MouseAdapter
{
private Shape shape;
private ShapePanelMouseListener()
{
shape = (Shape)shapeChoice.getSelectedItem();
}
#Override
public void mousePressed(MouseEvent e)
{
shape = (Shape)shapeChoice.getSelectedItem();
shape.setStartingPoint(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e)
{
shape = (Shape)shapeChoice.getSelectedItem();
shape.setEndingPoint(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
list.add(shape);
System.out.println(list.size());
}
}
I have program where I draw rectangles, however when I change the color of my graphics object(the mouse), it changes all of it too. Do I have to make another arraylist or is it possible to add on to existing one.
public class Rectangles extends JPanel {
private JRadioButton red, black;
private Color c;
private ArrayList<Point> points;
public Rectangles() {
setLayout(new FlowLayout());
setBackground(Color.white);
JPanel pane = new JPanel();
pane.setBackground(Color.gray);
points = new ArrayList<Point>();
addMouseListener(new MovementListener());
red = new JRadioButton("Red");
black = new JRadioButton("Black");
red.addActionListener(new ChangeColorListener());
black.addActionListener(new ChangeColorListener());
ButtonGroup group = new ButtonGroup();
group.add(red);
group.add(black);
red.setBackground(Color.red);
black.setBackground(Color.black);
add(pane);
pane.add(red);
pane.add(black);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(c);
for (int i = 0; i < points.size(); i++) {
Point p = points.get(i);
g.drawRect(p.x, p.y, 15, 15);
}
}
private class MovementListener extends MouseInputAdapter {
public void mouseClicked(MouseEvent ex) {
addMouseMotionListener(new MovementListener());
}
public void mouseMoved(MouseEvent ex) {
Point point = ex.getPoint();
points.add(point);
repaint();
}
}
private class ChangeColorListener implements ActionListener {
public void actionPerformed(ActionEvent ex) {
if (ex.getSource() == red) {
c = Color.red;
} else if (ex.getSource() == black) {
c = Color.black;
}
}
}
You will probably need to add another properties to these objects in the future. I recommend to create another class for that.
class MyPoint {
// if the values would be unchangeable make them final
private Point point;
private Color color;
public MyPoint(Point point, Color color) {
this.point = point;
this.color = color;
}
// getters
}
Also a small advice, in constructor you should only set the parameters. In your case I would create a method named "init()" and there create all there panels etc.
I am creating a drawing board program, basically a simplified version of MS Paint. It is working for the most part but when I draw on the board, the currently selected UI element shows up on the drawing area.
I tried paintComponent, which worked when I called super.paintComponent(g) (as one does) but it cleared the drawing area before I drew the next object. I overrode update and put a println statement in there and it is never being called.
The buttons at the top are red because I set the background to the bottom JPanel to red to see what the background of these buttons would be. So clearly they are part of the bottom JPanel. I am using a BorderLayout for the layout.
Here is my code (with some irrelevant bits removed):
public class JSPaint extends JFrame implements Serializable
{
private static final long serialVersionUID = -8787645153679803322L;
private JFrame mainFrame;
private JPanel bp;
private JButton ...
private DrawingArea da;
public JSPaint()
{
setTitle("JS Paint");
setSize(1024, 768);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Drawing area
da = new DrawingArea();
setLayout(new BorderLayout());
// add the buttons to the panel
buttonPanel();
// Add the drawing area
add(bp, BorderLayout.SOUTH);
bp.setBackground(Color.RED);
add(da, BorderLayout.CENTER);
da.setBackground(Color.BLUE);
setVisible(true);
}
// I put it here too just in case
#Override
public void update(Graphics g)
{
System.out.println("update in JSPaint called.");
paint(g);
}
/*
* Creates the panel for the buttons, creates the buttons and places them on
* the panel
*/
public void buttonPanel()
{
// Create the panel for the buttons to be placed in
bp = new JPanel();
saveButton = new JButton("Save");
loadButton = new JButton("Load");
//more buttons
bp.add(saveButton);
bp.add(loadButton);
//more buttons
// ActionListeners
colorButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
System.out.println("color");
da.color();
}
});
}
public class DrawingArea extends JPanel
{
private static final long serialVersionUID = -8299084743195098560L;
boolean dragged = false;
#Override
public void update(Graphics g)
{
System.out.println("Update in DrawingArea called");
paint(g);
}
/*
* Draws the selected shape onto the screen and saves it into a Stack.
*
*/
public void draw()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
dragged = true;
}
public void mouseMoved(MouseEvent me) {}
});
//more listeners...
});
}
/*
* Draws the selected String onto the screen when the mouse is held down.
*
*/
public void brush()
{
this.addMouseMotionListener(new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
// If we are in drawing mode, draw the String. Create a new
// Figure Object and push it onto the Stack
if(activeButton == "brush")
{
startPoint = me.getPoint();
Figure fig = new Figure("String", startPoint, null, currentColor);
// figures.push(calculate(fig));
toPaint.push(calculate(fig));
repaint();
}
}
public void mouseMoved(MouseEvent me) {}
});
}
// more of the same...
public void paint(Graphics g)
{
toSave.addAll(toPaint);
while(!toPaint.isEmpty())
{
Figure f = toPaint.pop();
String t = f.type;
if(f.color != null)
{
g.setColor(f.color);
}
switch(t)
{
case "Rectangle": g.drawRect(f.x1, f.y1, f.width, f.height);
break;
case "Oval": g.drawOval(f.x1, f.y1, f.width, f.height);
break;
case "Line": g.drawLine(f.x1, f.y1, f.x2, f.y2);
break;
case "Clear":
g.fillRect(0, 0, da.getWidth(), da.getHeight());
clearStack(toSave);
break;
case "String": g.drawString(f.toPrint, f.x1, f.y1);
break;
}
}
}
}
private class Figure implements Serializable
{
private static final long serialVersionUID = 4690475365105752994L;
String type, toPrint;
Color color;
Point start;
Point end;
int x1, y1, x2, y2, width, height;
public Figure(String figureType,
Point startPoint, Point endPoint, Color figureColor)
{
type = figureType;
color = figureColor;
start = startPoint;
end = endPoint;
}
// Rect, Oval
public Figure(String figureType, int figureX, int figureY,
int figureWidth, int figureHeight, Color figureColor)
{
type = figureType;
x1 = figureX;
y1 = figureY;
width = figureWidth;
height = figureHeight;
color = figureColor;
}
// more shapes
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JSPaint();
}
});
}
}
If the currently selected UI element shows up on the drawing area, this can have only one reason: you are doing something wrong with the Graphics objects, possibly calling a translate on them, and not resetting the tranlation when you are done. (See this link for an example of translate used correctly).
I have a JComboBox and would like to have a separator in the list of elements. How do I do this in Java?
A sample scenario where this would come in handy is when making a combobox for font-family-selection; similar to the font-family-selection-control in Word and Excel. In this case I would like to show the most-used-fonts at the top, then a separator and finally all font-families below the separator in alphabetical order.
Can anyone help me with how to do this or is this not possible in Java?
There is a pretty short tutorial with an example that shows how to use a custom ListCellRenderer on java2s
http://www.java2s.com/Code/Java/Swing-Components/BlockComboBoxExample.htm
Basically it involves inserting a known placeholder in your list model and when you detect the placeholder in the ListCellRenderer you return an instance of 'new JSeparator(JSeparator.HORIZONTAL)'
By the time I wrote and tested the code below, you probably got lot of better answers...
I don't mind as I enjoyed the experiment/learning (still a bit green on the Swing front).
[EDIT] Three years later, I am a bit less green, and I took in account the valid remarks of bobndrew. I have no problem with the key navigation that just works (perhaps it was a JVM version issue?). I improved the renderer to show highlight, though. And I use a better demo code. The accepted answer is probably better (more standard), mine is probably more flexible if you want a custom separator...
The base idea is to use a renderer for the items of the combo box. For most items, it is a simple JLabel with the text of the item. For the last recent/most used item, I decorate the JLabel with a custom border drawing a line on its bottom.
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoPartsComboBox extends JComboBox
{
private int m_lastFirstPartIndex;
public TwoPartsComboBox(String[] itemsFirstPart, String[] itemsSecondPart)
{
super(itemsFirstPart);
m_lastFirstPartIndex = itemsFirstPart.length - 1;
for (int i = 0; i < itemsSecondPart.length; i++)
{
insertItemAt(itemsSecondPart[i], i);
}
setRenderer(new JLRenderer());
}
protected class JLRenderer extends JLabel implements ListCellRenderer
{
private JLabel m_lastFirstPart;
public JLRenderer()
{
m_lastFirstPart = new JLabel();
m_lastFirstPart.setBorder(new BottomLineBorder());
// m_lastFirstPart.setBorder(new BottomLineBorder(10, Color.BLUE));
}
#Override
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
if (value == null)
{
value = "Select an option";
}
JLabel label = this;
if (index == m_lastFirstPartIndex)
{
label = m_lastFirstPart;
}
label.setText(value.toString());
label.setBackground(isSelected ? list.getSelectionBackground() : list.getBackground());
label.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
label.setOpaque(true);
return label;
}
}
}
Separator class, can be thick, with custom color, etc.
import java.awt.*;
import javax.swing.border.AbstractBorder;
/**
* Draws a line at the bottom only.
* Useful for making a separator in combo box, for example.
*/
#SuppressWarnings("serial")
class BottomLineBorder extends AbstractBorder
{
private int m_thickness;
private Color m_color;
BottomLineBorder()
{
this(1, Color.BLACK);
}
BottomLineBorder(Color color)
{
this(1, color);
}
BottomLineBorder(int thickness, Color color)
{
m_thickness = thickness;
m_color = color;
}
#Override
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height)
{
Graphics copy = g.create();
if (copy != null)
{
try
{
copy.translate(x, y);
copy.setColor(m_color);
copy.fillRect(0, height - m_thickness, width - 1, height - 1);
}
finally
{
copy.dispose();
}
}
}
#Override
public boolean isBorderOpaque()
{
return true;
}
#Override
public Insets getBorderInsets(Component c)
{
return new Insets(0, 0, m_thickness, 0);
}
#Override
public Insets getBorderInsets(Component c, Insets i)
{
i.left = i.top = i.right = 0;
i.bottom = m_thickness;
return i;
}
}
Test class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoPartsComboBoxDemo extends JFrame
{
private TwoPartsComboBox m_combo;
public TwoPartsComboBoxDemo()
{
Container cont = getContentPane();
cont.setLayout(new FlowLayout());
cont.add(new JLabel("Data: ")) ;
String[] itemsRecent = new String[] { "ichi", "ni", "san" };
String[] itemsOther = new String[] { "one", "two", "three" };
m_combo = new TwoPartsComboBox(itemsRecent, itemsOther);
m_combo.setSelectedIndex(-1);
cont.add(m_combo);
m_combo.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
String si = (String) m_combo.getSelectedItem();
System.out.println(si == null ? "No item selected" : si.toString());
}
});
// Reference, to check we have similar behavior to standard combo
JComboBox combo = new JComboBox(itemsRecent);
cont.add(combo);
}
/**
* Start the demo.
*
* #param args the command line arguments
*/
public static void main(String[] args)
{
// turn bold fonts off in metal
UIManager.put("swing.boldMetal", Boolean.FALSE);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame demoFrame = new TwoPartsComboBoxDemo();
demoFrame.setTitle("Test GUI");
demoFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demoFrame.setSize(400, 100);
demoFrame.setVisible(true);
}
});
}
}
You can use a custom ListCellRenderer which would draw the separator items differently. See docs and a small tutorial.
Try adding this renderer. Just supply a list of index values that you want the separator to be above.
private class SeperatorComboRenderer extends DefaultListCellRenderer
{
private final float SEPARATOR_THICKNESS = 1.0f;
private final float SPACE_TOP = 2.0f;
private final float SPACE_BOTTOM = 2.0f;
private final Color SEPARATOR_COLOR = Color.DARK_GRAY;
private final List<Integer> marks;
private boolean mark;
private boolean top;
public SeperatorComboRenderer(List<Integer> marks)
{
this.marks = marks;
}
#Override
public Component getListCellRendererComponent(JList list, Object object, int index, boolean isSelected, boolean hasFocus)
{
super.getListCellRendererComponent(list, object, index, isSelected, hasFocus);
top = false;
mark = false;
marks.forEach((idx) ->
{
if(index - 1 == idx)
top = true;
if(index == idx)
mark = true;
});
return this;
}
#Override
protected void paintComponent(Graphics g)
{
if(mark)
g.translate(0, (int)(SEPARATOR_THICKNESS + SPACE_BOTTOM));
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
if(mark)
{
g2.setColor(SEPARATOR_COLOR);
g2.setStroke(new BasicStroke(SEPARATOR_THICKNESS));
g2.drawLine(0, 0, getWidth(), 0);
}
}
#Override
public Dimension getPreferredSize()
{
Dimension pf = super.getPreferredSize();
double height = pf.getHeight();
if(top) height += SPACE_TOP;
else if(mark) height += SEPARATOR_THICKNESS + SPACE_BOTTOM;
return new Dimension((int)pf.getWidth(), (int)height);
}
}