Hello All, it's been a while since I've had a question for you all.
I'm trying to write a paint app, I've done so before, but this time I decided to add Layers, and I wanted a JList of the layers, I wrote a cell renderer, that not only renders the names of the layer, but a small scale image of the layer, but I also have two checkboxes on the component that gets returned, and the checkboxes don't change when clicked, is there a simple way to make the list pass events down to other components?
Ezzaral 2,714 Posting Sage Team Colleague Featured Poster
Simple way? Not that I know of. JList is not really considered an editing component and doesn't expose methods for it list JTable does. What you need is a cell editor in addition to your renderer.
You could take a look at this guy's blog entry where he builds an editable JList and perhaps modify it to your needs: http://www.jroller.com/santhosh/date/20050607
Or you could use a JTable instead, with a custom cell renderer.
sciwizeh 62 Posting Pro in Training
I didn't want to post my code before because it's long and dididn't have any comments, now that I've commented it I suppose I'll post it. I have the JList displaying exactly what I wanted to, including the CheckBoxes, but I can't click the checkboxes. Here is my current code
ListPane.java -- test driver, eventually going to be directly in the paint program
package paintpp.layers;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
/**
* @author Bill
*
*/
public class LayerPane extends JPanel {
/**
* the list that will be displayed
*/
JList list = new JList();
/**
* scroll pane for the list
*/
JScrollPane pane = new JScrollPane(list);
/**
* model for the list
*/
public DefaultListModel model = new DefaultListModel();
/**
* renders a layer in list cell format
*/
LayerListRenderer rend = new LayerListRenderer();
/**
* make a new default layer pane
*/
public LayerPane() {
list.setCellRenderer( rend );
list.setModel( model );
this.add( pane );
}
/**
* because eclipse complains if I don't make one
*/
private static final long serialVersionUID = -4271150524981636755L;
/**
* main just to test if stuff is working with the list managing classes here
// FIXME NOT WORKING JList isn't allowing events to reach the checkboxes in the list
// display works well, though
* @param args command line arguments
*/
public static void main( String[] args ) {
//frame for test
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
//test layerpane
LayerPane lpane = new LayerPane();
frame.add(lpane);
//add three new layers to the list, and for each of them generate random colors throughout
lpane.model.add(0, new Layer("hello",100,100) );
RandomImage((Layer)lpane.model.get( 0 ));
lpane.model.add(0, new Layer("second",100,100) );
RandomImage((Layer)lpane.model.get( 0 ));
lpane.model.add(0, new Layer("third",100,100) );
RandomImage((Layer)lpane.model.get( 0 ));
//set visible
frame.setVisible( true );
}
/**
* random colors
* @param l graphics
*/
public static void RandomImage(Layer l) {
Graphics g = l.getGraphics();
for(int i = 0; i<l.getWidth();i++) {
for(int j = 0; j<l.getHeight();j++) {
g.setColor( new Color((int)Math.floor( Math.random()*256 ),(int)Math.floor( Math.random()*256 ),(int)Math.floor( Math.random()*256 )) );
g.drawRect(i,j,0,0);
}
}
}
}
Layer.java -- an extended BufferredImage, that now has a name and a few extra flags
/**
*
*/
package paintpp.layers;
import java.awt.image.BufferedImage;
/**
* A class to hold a Layer, Comparable, only so that treemap will work correctly
* @author Bill
*
*/
public class Layer extends BufferedImage implements Comparable<Layer>{
/**
* name of this layer
*/
String name;
/**
* is the layer visible
*/
private boolean visible = true;
/**
* is the layer locked (not moddable)
*/
private boolean locked = false;
/**
* Constructor
* @param name name of the layer
* @param width width of the layer
* @param height height of the layer
*/
public Layer(String name, int width, int height) {
this(name,width,height,TYPE_INT_ARGB);
}
/**
* Constructor
* @param name name of the layer
* @param width width of the layer
* @param height height of the layer
* @param imageType type of image
*/
public Layer(String name, int width, int height, int imageType ) {
super( width, height, imageType );
this.name=name;
}
/**
* getter for name
* @return name
*/
public String getName() {
return name;
}
/**
* @param visible the visible to set
*/
public void setVisible( boolean visible ) {
this.visible = visible;
}
/**
* @return the visible
*/
public boolean isVisible() {
return visible;
}
/**
* setter for name
* @param nName new name
*/
public void setName(String nName) {
name=nName;
}
/**
* @param locked the locked to set
*/
public void setLocked( boolean locked ) {
this.locked = locked;
}
/**
* @return the locked
*/
public boolean isLocked() {
return locked;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals( Object arg0 ) {
if(arg0 instanceof Layer) {
return super.equals( arg0 )&&((Layer)arg0).name.equals(name);
}
return false;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return super.hashCode()+2*name.hashCode();
}
/**
*
*/
@Override
public int compareTo( Layer o ) {
return name.compareTo( o.name );
}
}
LayerListRenderer.java -- the list renderer
package paintpp.layers;
import java.awt.Component;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import java.util.TreeMap;
/**
* To render layers in a list, it's a simple thing turned complex by me<br>
* rather than just return a new component every time the only required<br>
* method is called I use a Treemap, so that I don't have to construct a<br>
* new thing every time, and I was hoping that because the components are<br>
* really there, that the List might pass events to them and I don't know why not.
* FIXME really need the checkboxes to get events to them
* @author Bill
*
*/
public class LayerListRenderer implements ListCellRenderer {
/**
* treemap that stores the layer panels accordign to the layers that they can display
*/
TreeMap<Layer, LayerPanel> renderers = new TreeMap<Layer,LayerPanel>();
/**
* because eclipse made me
*/
private static final long serialVersionUID = -6121867104182335719L;
/**
* fairly simple, if the map has the specified layer, return the component that is already there to display it,<br>
* if not make a new one, add it to the map, and return it;
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
*/
@Override
public Component getListCellRendererComponent( JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus ) {
if(value instanceof Layer) {
Layer l = (Layer)value;
if(renderers.containsKey( l )) {
return renderers.get( l ).setSelected( isSelected );
} else {
renderers.put( l, new LayerPanel(l) );
return renderers.get( l ).setSelected( isSelected );
}
}
return null;
}
}
LayerPanel.java -- the component that actually knows how to display a layer
/**
*
*/
package paintpp.layers;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
/**
* Class that displays a layer for a list of layers.
* also contains checkboxes that should control what functionality the layer currently supports.
* FIXME the checkboxes aren't responding to events.
* @author Bill
*/
public class LayerPanel extends JPanel implements ItemListener {
/**
* Layer to display
*/
Layer layer;
/**
* whether this is a selected component
*/
boolean isSelected;
/**
* checkbox for visibility
*/
JCheckBox visible = new JCheckBox("Visible",true);
/**
* checkbox for locking
*/
JCheckBox locked = new JCheckBox("Locked",false);
/**
* new renderer with layer nl
* @param nl layer to display with this
*/
public LayerPanel(Layer nl) {
layer=nl;
visible.addItemListener( this );
locked.addItemListener( this );
this.add( new Preview() );
this.add( visible );
this.add( locked );
this.setPreferredSize( new Dimension(200,50) );
}
/**
* sets the selected value, and returns the newly changed this
* @param bool new value
* @return changed this
*/
public LayerPanel setSelected(boolean bool) {
isSelected=bool;
if(isSelected) {
this.setBackground( new Color(100,100,255) );
} else {
this.setBackground( new Color(255,255,255) );
}
return this;
}
/**
* this is what will notify the underlying layer that the checkboxes are changed.
*/
@Override
public void itemStateChanged( ItemEvent e ) {
if(e.getSource()==visible) {
if(e.getStateChange()==ItemEvent.SELECTED) {
layer.setVisible( true );
} else {
layer.setVisible( false );
}
} else if(e.getSource()==locked) {
if(e.getStateChange()==ItemEvent.SELECTED) {
layer.setLocked( true );
} else {
layer.setLocked( false );
}
}
}
/**
* this inner class is here to paint the smaller scale image of the layer.
* all it does is override paint to display the layer it works fine
* @author Bill
*
*/
class Preview extends JPanel {
private static final long serialVersionUID = 8953781420286542857L;
public Preview() {
this.setPreferredSize(new Dimension(20,20));
}
public void paint(Graphics g) {
g.drawImage(layer,0,0,20,20,0,0,layer.getWidth(),layer.getHeight(),null);
}
}
/**
*
*/
private static final long serialVersionUID = 8953781420286542857L;
}
I just want the checkboxes to respond to normal clicking, I will take a look at the blog later (I don't really have time at the moment), I suppose I could try to change his text boxes to check boxes, but I would rather not have to radically change what I have.
I hope seeing my code will help you, find something that I can do,
Ezzaral 2,714 Posting Sage Team Colleague Featured Poster
The point is, you have a custom renderer - which is not an editor.
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.