Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Monday, March 1, 2010

Customs Toggle buttons



First I will talk about why I ended up creating this customs jtoggle button.

I was asked, what would be the best way to show a choice among 2 available options (option A or option B).

Usually when you want the user to select one option among a few (let’s say less than 5) you can use some JRadioButton/JCheckBox/JToggleButton.
JCombobox/JList are usually used if you have more possible options, doesn’t really make sense (at least for me) to have a JComboBox/JList with only 2 or 3 available options

I had to show the icon corresponding to each of the 2 options.
I could have used JRadioButton + a JLabel (to show the icon) or a JToggleButton.
The image being quiet big (> 100*100 pixels), using a JToggleButton is just wrong and plain ugly for this kind of icon.
So my only option left was to use a JRadioButton with a JLabel to show the icon.

After some tests, I wasn’t really satisfied about how it looked.
I really don’t like the look of the selected/unselected icon sticked to the icon representing the option.











So I though why not use directly the image as component with some kind of visual feedback apply to the image (and not outside like for the JRadioButton)

The visual feedbacks I considered for this are the following:
-translucent if not selected
-smaller image if not selected
-a selection ring
And of course it should have animated transition between selected/unselected state switch


You can find the implementation at: ShrinkingToggleImageButton








Some time later, I needed to have a panel to select among 3 options, but this time there was no icon. So I should have just used 3 JRadioButton and be done with it. But considering the application is running on a device with touch-screen and that selecting an option trigger a big change in the screen, it made more sense to have JToggleButton used there.



















As having 3 JToggleButton side by side doesn’t look that great, I decided to see If I could get something that make sense with a better look.


I rapidly decided to try using ShrinkingToggleImageButton, after playing a bit to create the image, I came up with the following result.




















I was happy about the result, it looks way better on the screen it’s used on, than some JRadioButton/JToggleButton.





Some time later, I needed a way to select options among 3 again, but this time it had an icon + some text, as I had the same constraints (touch-screen – big buttons needed), I started to play again with ShrinkingToggleImageButton.

This time I created a class to hold this component, instead of just giving the right image to the shrinkingToggleImageButton: ShrinkingToggleImageAndTextButton

Also it can deal with multiple line text, use \n to wrap to the next line


























Thursday, February 11, 2010

Bean reader JTable

I have already written about JTable on this blog, a lot can be written about it.
This time I will talk about a custom jtable I developed in order to have the simplest way to show READ ONLY data.

First how I was coding before:


class Person{
String firstName;
String lastName;
/*..*/
}

DefaultTableModel dtm= new DefaultTableModel();
for(Person p: getListPersons()){
Dtm.add(new Object[]{p.getFirstName(),p.getLastName()});
}

Dtm.addColumn(“firstName”);
Dtm.addColumn(“lastName”);

JTable table=new JTable();
table.setModel(dtm);


And to know which Object is currently selected:

Int index=table.getSelectedIndex();
Int modelIndex=table.convertRowIndexToModel(index);
Person selected=getListPersons.get(modelIndex);


After coding this way too many time I wondered:
As we only show only one kind of object in the table, wouldn’t it be easier:
to have a table of T
to be able to add/remove a T
to be able to get the selected T
All this without coding a custom JTable or/and TableModel each time


So the plan is to have an easy way to
-Define the class of objects to show using the JTable
-Define the columns
-Add/remove an object from the table
-Get the selected objects


So now let’s see the implementation I called BeanReaderJTable

First we need a generic parameter

public class BeanReaderJTable<T> extends JTable {/*…*/}


The contructor take the field names and the column names you want as header value.


public BeanReaderJTable(String[] fields, String[] title)


Adding/removing a row or getting the selected objects can’t be easier:

addRow(T)
addRow(T[])
addRow(Collection<T>)
removeRow(T)
getSelectedObject():T
getSelectedObjects():T[]



What is doing the actual job is the GenericTableModel.
The important job is done in getValueAt(int,int). The reflexion API is used to retrieve the value of a given pair field+row


Now let’s see a sample:


// declare the type + fields+column title
String[] fields = new String[] { "size", "size.width", "size.height", "class", "visible", null };
String[] titles = new String[] { "size", "width", "height", "class", "is visible", null };
BeanReaderJTable<Component> table = new BeanReaderJTable<Component>(fields, titles);
//populate the table
table.addRow(getAllComponents(frame));


You may have noticed, I left the last field empty, doing so, an empty column is created that can be used to set for example a button cell editor

And also what you can see, is that you can access nested field like size.width

This time you should actually be able to access the code source repository, my java.net project has been approved:
BeanReaderJTable





Saturday, January 16, 2010

Animated cell renderer

This article will take as example list cell renderer but the same way can be applied to animate tree/table renderer.

Let’s start from where the last article end: the renderer is bigger when the cell is selected, why not animate the fact that the renderer size increase over time, to let the user know it’s actually the one that he selected that get bigger.

First we need a way to store the animation value for each cell:


Map<Integer, Float> mapAnimation = new HashMap<Integer, Float>();


The renderer will look up for the animation value:

public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, final boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index,isSelected, cellHasFocus);

Float progress = mapAnimation.get(index);
if (progress == null) {
progress = 0f;
}
label.setFont(label.getFont().deriveFont(10 + 20 * progress));
label.setPreferredSize(new Dimension(50, (int) (15 + 35 * progress)));
return label;
}




A selection listener to start the animation:
list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
final Timeline timeline = new Timeline();
timeline.setDuration(200);
timeline.addCallback(new TimelineCallback() {
/*…see below…*/
});
timeline.play();
}
});



The TimelineCallBack allow us to define what to do on each timeline pulse, it’s where the animation map will get populated.

new TimelineCallback() {
@Override
public void onTimelinePulse(float durationFraction,float TimelinePosition) {
// set the progress for the selected index
mapAnimation.put(list.getSelectedIndex(), durationFraction);
//set the progress for the last selected index
if (oldSelected[0] != -1) {
mapAnimation.put(oldSelected[0], 1 - durationFraction);
}
//compute the size for each cell with the new animation values
SwingUtilities.invokeLater(new Runnable(){
Override
public void run(){
JlistUtils.computeListSize(list);
}
});
}




And that’s it! You now have an animated renderer.




A bit more complex examples:













as always you can find the source code in the source code repository here

Dynamic size cell list renderer

What I mean by dynamic size is that the renderer can changed size for a given index.

Let’s take what can be seen at first as a very easy example:
For example when you select an item, you would like to increase the font size of the label.

It seems easy, let’s just create a custom renderer and change the font size based on the isSelected parameter.


public Component getListCellRendererComponent(JList list, Object value,int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
label.setFont(label.getFont().deriveFont(35f));
} else {
label.setFont(label.getFont().deriveFont(10f));
}
return label;
}




The font size is bigger but the size of the label is not increased, hence the label is partly hidden .

So let’s try to change the size of the label:


public Component getListCellRendererComponent(JList list, Object value,int index, boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
label.setFont(label.getFont().deriveFont(35f));
label.setPreferredSize(new Dimension(50, 50));
} else {
label.setFont(label.getFont().deriveFont(10f));
label.setPreferredSize(new Dimension(50, 15));
}
return label;
}



To our surprise it doesn’t work. Why?

The answer is in the BasicListUI class, the height of each cell is cached, it doesn’t get computed each time the getListCellRendererComponent method get called. That’s where our problem lies.

By looking a bit more closely at BasicListUI, one can see that the cache is populated in the updateLayoutState method and that’s only getting called after a change in the model.

We have to call this method in order to compute the new size of each cell, but this method is protected,so we won’t be able to call this method directly so we will use reflection.


public static void computeListSize(final JList list) {
if (list.getUI() instanceof BasicListUI) {
BasicListUI ui = (BasicListUI) list.getUI();

try {
Method method = BasicListUI.class.getDeclaredMethod("updateLayoutState");
method.setAccessible(true);
method.invoke(ui);
list.revalidate();
list.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
}




In our case we need to compute the cell size when a cell gets selected:

list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
JlistUtils.computeListSize(list);
}
});



But as you can see if you run this webstart demo , it doesn’t work, so why ?
The answer is in the updateLayoutState method.:
protected void updateLayoutState(){
//…
Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
//…
}



The 2 last parameters are isSelected and cellHasFocus, which means we can’t use isSelected+cellHasFocus to determine the size of the renderer.

So the last version of our renderer is:


public Component getListCellRendererComponent(JList list,Object value, int index, boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (list.getSelectedIndex() == index) {
label.setFont(label.getFont().deriveFont(35f));
label.setPreferredSize(new Dimension(50, 50));
} else{
label.setFont(label.getFont().deriveFont(10f));
label.setPreferredSize(new Dimension(50, 15));
}
return label;
}



This time we actually have what we expected: the cell selected is bigger.






You can find the source of those demo in the following package in the source code repository here


all the jars files has been signed to run via webstart; that was needed because of the use of Method.setAccessible to be able to call a protected method

Saturday, January 9, 2010

Customs JDialog/JFrame

I wanted a JDialog/JFrame that looks better than the standards ones, so I ended up coding these classes : DialogWithDropShadow/FrameWithDropShadow , and yes something I am really bad at is naming.

They are JDialog / JFrame with round corner and drop shadow.

It’s JDialog/JFrame which has been set non opaque using the WindowsUtils class( wrapper around AWTUtilities + some other methods). It’s the custom content pane that deal with painting the background+shadow, and the layered pane is used to position the title bar


Under SubstanceDustCoffeeLookAndFeel



under metal LAF



Title bar

For the dialog you can choose to have (default value) or not the close button using the following constructor
DialogWithDropShadow (Window frame, boolean draggable, boolean withCloseButton)
or the setter
setWithCloseButton(boolean)
If the close button is not visible and that the title is empty there will be not y offset.










Background

You can change the background color using either getContentPane().setBackground(Color)









or setContentPaneBackground(Paint)












Fade in / Fade out

This window can be shown or hidden using a fade in/fade out animation with the 2 followings methods:

startShowAnim()

startHideAnim()

Those 2 methods in fact only call WindowFadeInManager.fadeIn/fadeout

WindowFadeInManager is a utility class used to fade in or fade out windows, you can use it on any window, but you will need at least java 6 update 10 to use this feature, if you don’t, it won’t crash, it will just setVisible true/false on the given window


Resizable

By default they are resizable, it uses com.jidesoft.swing.Resizable behind the scene to make it resizable, just call setResizable(false) to remove this default behavior.


Draggable
Both dialog and frame are draggable by default, not only on the title bar, from anywhere if the mouse event is not catched by another component. draggable class


You can find the source code here



dialog under substance

frame under substance

dialog under metal

frame under metal


Friday, January 8, 2010

Text prompt

I really like the solution to create a text prompt I saw at http://tips4java.wordpress.com/2009/11/29/text-prompt/

Something I really like also is smooth transition between component states, so I had to add fade in / fade out transition for this text prompt component!


My first idea was to do the animation using the alpha component of the foreground. But after the first test i noticed that i forgot the icon of the JLabel!

So i ended up using JXPanel on which one i add the JLabel, and then doing the animation of the JXPanel alpha property.


You can find the source in the repository here

Feel free to try it out:


I have one problem with this component, it only hapens under substance LAF, i have to create the textPrompt component in another invokeLater else i can't see the text prompt, i still need to investigate this point.


Ok, that's it for my first article on a swing component, i hope i didn't forget anything and that the web start demo works.