Skip to main content

slider demo

20 replies [Last post]
ariano
Offline
Joined: 2005-09-24
Points: 0

A while ago I published an adapted version of SliderDemo3 on
https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?...

This demo used to be part of the Swing tutorial, but it seems to have been lost in the current version of this tutorial.

I rewrote this demo because I see so much demo code that, I think, is not a good example of how to program in Java. It is more about how to program than about how to use sliders. My interest was mainly in improving existing demo code, not about improving the tutorial.

For example, I think any demo code related to gui programming should use the MVC pattern.

Anyway, maybe this code can be made part of a future version of the Java/Swing tutorial.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
teleporter
Offline
Joined: 2012-05-14
Points: 0

Are you guys all programmers?

jano21

nice thanks

ariano
Offline
Joined: 2005-09-24
Points: 0

Here is the original SliderDemo3.java.

Cheers, Arjan.

----------------------------------------------------------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.NumberFormatter;
import java.beans.*;

/*
* SliderDemo3.java is a 1.4 application that requires all the
* files in the images/doggy directory. It adds a formatted text
* field to SliderDemo.java.
*/
public class SliderDemo3 extends JPanel
implements ActionListener,
WindowListener,
ChangeListener,
PropertyChangeListener {
//Set up animation parameters.
static final int FPS_MIN = 0;
static final int FPS_MAX = 30;
static final int FPS_INIT = 15; //initial frames per second
int frameNumber = 0;
int NUM_FRAMES = 14;
ImageIcon[] images = new ImageIcon[NUM_FRAMES];
int delay;
Timer timer;
boolean frozen = false;

//This label uses ImageIcon to show the doggy pictures.
JLabel picture;

//Add a formatted text field to supplement the slider.
JFormattedTextField textField;

//And here's the slider.
JSlider framesPerSecond;

public SliderDemo3() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

delay = 1000 / FPS_INIT;

//Create the label.
JLabel sliderLabel = new JLabel("Frames Per Second: ", JLabel.CENTER);
sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);

//Create the formatted text field and its formatter.
java.text.NumberFormat numberFormat =
java.text.NumberFormat.getIntegerInstance();
NumberFormatter formatter = new NumberFormatter(numberFormat);
formatter.setMinimum(new Integer(FPS_MIN));
formatter.setMaximum(new Integer(FPS_MAX));
textField = new JFormattedTextField(formatter);
textField.setValue(new Integer(FPS_INIT));
textField.setColumns(5); //get some space
textField.addPropertyChangeListener(this);

//React when the user presses Enter.
textField.getInputMap().put(KeyStroke.getKeyStroke(
KeyEvent.VK_ENTER, 0),
"check");
textField.getActionMap().put("check", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!textField.isEditValid()) { //The text is invalid.
Toolkit.getDefaultToolkit().beep();
textField.selectAll();
} else try { //The text is valid,
textField.commitEdit(); //so use it.
} catch (java.text.ParseException exc) { }
}
});
//XXX: Neither actionPerformed nor invalidEdit gets invoked
// when the user tries to make an invalid edit -- e.g.,
// typing 100 and pressing Enter. There's no beep, seemingly
// no opportunity to respond to the incorrect value.
// I fixed this by adding an action for Enter, which made
// the text field's action listener never get called, so I
// removed the action listener.

//Create the slider.
framesPerSecond = new JSlider(JSlider.HORIZONTAL,
FPS_MIN, FPS_MAX, FPS_INIT);
framesPerSecond.addChangeListener(this);

//Turn on labels at major tick marks.
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setMinorTickSpacing(1);
framesPerSecond.setPaintTicks(true);
framesPerSecond.setPaintLabels(true);
framesPerSecond.setBorder(
BorderFactory.createEmptyBorder(0,0,10,0));

//Create the label that displays the animation.
picture = new JLabel();
picture.setHorizontalAlignment(JLabel.CENTER);
picture.setAlignmentX(Component.CENTER_ALIGNMENT);
picture.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLoweredBevelBorder(),
BorderFactory.createEmptyBorder(10,10,10,10)));
updatePicture(0); //display first frame

//Create a subpanel for the label and text field.
JPanel labelAndTextField = new JPanel(); //use FlowLayout
labelAndTextField.add(sliderLabel);
labelAndTextField.add(textField);

//Put everything together.
add(labelAndTextField);
add(framesPerSecond);
add(picture);
setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

//Set up a timer that calls this object's action handler.
timer = new Timer(delay, this);
timer.setInitialDelay(delay * 7); //We pause animation twice per cycle
//by restarting the timer
timer.setCoalesce(true);
}

/** Add a listener for window events. */
void addWindowListener(Window w) {
w.addWindowListener(this);
}

//React to window events.
public void windowIconified(WindowEvent e) {
stopAnimation();
}
public void windowDeiconified(WindowEvent e) {
startAnimation();
}
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}

/** Listen to the slider. */
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
int fps = (int)source.getValue();
if (!source.getValueIsAdjusting()) { //done adjusting
textField.setValue(new Integer(fps)); //update ftf value
if (fps == 0) {
if (!frozen) stopAnimation();
} else {
delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 10);
if (frozen) startAnimation();
}
} else { //value is adjusting; just set the text
textField.setText(String.valueOf(fps));
}
}

/**
* Listen to the text field. This method detects when the
* value of the text field (not necessarily the same
* number as you'd get from getText) changes.
*/
public void propertyChange(PropertyChangeEvent e) {
if ("value".equals(e.getPropertyName())) {
Number value = (Number)e.getNewValue();
if (framesPerSecond != null && value != null) {
framesPerSecond.setValue(value.intValue());
}
}
}

public void startAnimation() {
//Start (or restart) animating!
timer.start();
frozen = false;
}

public void stopAnimation() {
//Stop the animating thread.
timer.stop();
frozen = true;
}

//Called when the Timer fires.
public void actionPerformed(ActionEvent e) {
//Advance the animation frame.
if (frameNumber == (NUM_FRAMES - 1)) {
frameNumber = 0;
} else {
frameNumber++;
}

updatePicture(frameNumber); //display the next picture

if ( frameNumber==(NUM_FRAMES - 1)
|| frameNumber==(NUM_FRAMES/2 - 1) ) {
timer.restart();
}
}

/** Update the label to display the image for the current frame. */
protected void updatePicture(int frameNum) {
//Get the image if we haven't already.
if (images[frameNumber] == null) {
images[frameNumber] = createImageIcon("images/doggy/T"
+ frameNumber
+ ".gif");
}

//Set the image.
if (images[frameNumber] != null) {
picture.setIcon(images[frameNumber]);
} else { //image not found
picture.setText("image #" + frameNumber + " not found");
}
}

/** Returns an ImageIcon, or null if the path was invalid. */
protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = SliderDemo3.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}

/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);

//Create and set up the window.
JFrame frame = new JFrame("SliderDemo3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Create and set up the content pane.
SliderDemo3 animator = new SliderDemo3();
animator.setOpaque(true); //content panes must be opaque
frame.setContentPane(animator);

//Display the window.
frame.pack();
frame.setVisible(true);
animator.startAnimation();
}

public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

sfshaza
Offline
Joined: 2004-06-03
Points: 0

LOL. Granted, SliderDemo3 is rather bloated. But you have to remember that it was extended and extended again. If you look at SliderDemo.java (the original) you'll agree that it's a fairly compact demo:
http://java.sun.com/docs/books/tutorial/uiswing/examples/components/Slid...

Sharon

ariano
Offline
Joined: 2005-09-24
Points: 0

Hi Sharon,

true, but I did not modify SliderDemo, I modified SliderDemo3, so it is fair to compare the 'bloatedness' of my version of the slider demo with the 'bloatedness' of SliderDemo3.

Maybe I should rewrite SliderDemo as well, to see if the rewrite is much more bloated than the original?

Cheers, Arjan.

ariano
Offline
Joined: 2005-09-24
Points: 0

Sharon, you obviously spent quite some time looking at the SliderDemo3 example code, thanks for that. You have perfectly well grasped what my intentions with this demo were.

You raise a perfectly valid issue, to not bloat the code that confuses the information the tutorial tries to convey and the trade-offs involved. Now, I am not the one to comment on how to deal with these trade-offs, I am obviously biased!

Let me give some more food for thought, then. I attach the original SliderDemo3.java source file, so you can see where it all started.

The original SliderDemo3.java source file is 261 lines long. That is quite a bit. The demo code implements four different kinds of listeners, it deals with timers, images, labels, formatted text fields, layout issues, keyboard input, buttons, the event dispatching thread. And of course a slider. My point is, that the original source code is bloated with a lot of code not related to sliders.

The mvc/SliderDemo3.java source code is also bloated, with the exact same issues, plus one more: MVC. Is the difference significant?

If all examples were written using MVC, then the increased complication of MVC becomes less significant, perhaps even insignificant.

I am not sure if I understand your remarks about the tutorial not teaching 'basic Object Oriented Programming 101'. Because this obviously does not stop the tutorial from being an example of how to program in Java properly.

Apart from using MVC, there is another reason why I rewrote the demo code: to show how to code in Java using 'best' practices, whatever 'best' may mean. The mvc/SliderDemo3.java source file defines 5 named classes, the original only 1. The purpose is to put things where they belong. In the original code the SliderDemo3 class sets the slider's properties (and those of all other objects as well), in the rewritten code the slider sets its own properties. This increases the clarity of the code, with the added advantage of increased potential for reusability. But again, I am biased.

Cheers, Arjan.

xcirclex
Offline
Joined: 2006-05-16
Points: 0

Hi Ariano,

I want to get your code but I cannot access to:
https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?...

Could you let me know how to get it?

Thanks,

ariano
Offline
Joined: 2005-09-24
Points: 0

I don't know what's going wrong at your end, if I click on your link I see a page that has a link to slider-demo-3.zip, which is at
https://jdk-collaboration.dev.java.net/files/forums/2899/1463/766/slider...

I can download that zip no problem.

Does that help? If not, can you more accurately explain the problem?

Cheers, Arjan.

xcirclex
Offline
Joined: 2006-05-16
Points: 0

When clicking on the link, it shows:

Your account does not have the "Project Forum - View" permission needed for you to access the page you requested in the jdk-collaboration project (view your permissions). Either ask the project administrator for more permission, or log in using a different account.

But I don't know how to get the permission.

Thanks,

ariano
Offline
Joined: 2005-09-24
Points: 0

My guess is, that you go to
https://javatutorials.dev.java.net/
and then follow the 'Request project membership/role' link.

But I don't know for sure. I don't really understand why Sun is making it so difficult to get access, I don't think it helps their own cause.

Good luck, Arjan.

aberrant
Offline
Joined: 2006-02-02
Points: 0

Arjan you posted the code under https://jdk-collaboration.dev.java.net.

"JDK Collaboration - This project is for people who want to contribute bug fixes and other improvements into J2SE relative to the J2SE 6.0 Snapshot project , under the terms of the Java Research License (JRL) and the (SCA). Sun Contributor Agreement [SCA FAQ] "

That means at one time or another you signed the SCA. Thats why you, Sharon and I have access and other people don't. We should repost it here in the javatutorials section. Right now only Sharon has access to do that. I'll see what I can do.

sfshaza
Offline
Joined: 2004-06-03
Points: 0

If it's OK with you, Arjan, I'll post it here. Just give me the word.

Sharon

ariano
Offline
Joined: 2005-09-24
Points: 0

Hi Sharon, of course, please post!

Cheers, Arjan.

sfshaza
Offline
Joined: 2004-06-03
Points: 0

Here you go!

slider-demo-3.zip

sfshaza
Offline
Joined: 2004-06-03
Points: 0

I spent some time going through this example and it's very interesting. Arjan took an old Swing tutorial demo called Slider-Demo-3 and extensively modified it. I'm not sure why we dropped Slider-Demo-3 in the last update of the Swing tutorial, but you can still see SliderDemo and SliderDemo2 on the tutorial's slider page: http://java.sun.com/docs/books/tutorial/uiswing/components/slider.html

When you download Arjan's example (which uses the JDK 5 release), you will see there are three versions of SliderDemo3. The first version employs MVC design. That version is located in src/slider/mvc. There is a package.html file that explains what he's done and where you can find more information on MVC design. The two references he lists are:
- a Java blueprints page: http://java.sun.com/blueprints/patterns/MVC-detailed.html
- his own article: http://www.onjava.com/pub/a/onjava/2004/07/07/genericmvc.html

The "mvc' version of the demo includes several classes within the SliderDemo3.java file. He then creates the "split" version of the demo where he splits up the SliderDemo3.java file into a separate file for each class. (You can find the documentation for that in src/slider/split/package.html.)

Finally, he creates the "adorn" version of the demo (src/slider/adorn/package.html). Here, he illustrates the power of MVC design by adding several buttons to the GUI (the "View" portion of the example) without modifying the rest of the demo. (Which is pretty much the whole point of separating the logic from the GUI.)

Arjan's point here is that the tutorial doesn't really use MVC design in its examples. In fact, I searched and only found two references of "MVC" in the tutorial itself. Here's what the tutorial says about Swing's architecture:

-------------------------------------------------------

Although Swing's model architecture is sometimes referred to as a Model-View-Controller (MVC) design, it really isn't. Swing components are generally implemented so that the view and controller are indivisible, implemented by a single UI object provided by the look and feel. The Swing model architecture is more accurately described as a separable model architecture. If you're interested in learning more about the Swing model architecture, see A Swing Architecture Overview, an article in The Swing Connection.

-------------------------------------------------------

So, the question here is... should the tutorial examples employ MVC design?

This is the sort of thing we do discuss whenever we overhaul/update the tutorial. Proper MVC design increases the complexity of an example. Many tutorial examples are designed to be small and to illustrate some very specific technology or API. We often intentionally avoid any implementation that will bloat the code and confuse the information we are trying to convey.

There's always a trade-off.

For example, when we made the decision to always create the GUI on the Event Dispatch Thread (EDT) - this affected most every example in the tutorial and many people thought we were adding unneeded complexity. But we decided it was important for the very reason Arjan states: people do use the tutorial as a jumping off point so we wanted to create the GUI properly -- on the EDT, even if that decision complicated our examples.

Another issue to consider is that we have made a conscious decision that the tutorial won't attempt to teach basic programming concepts. So, for example, we don't try to teach basic Object Oriented Programming 101. There are better books out there to deal with this subject. Early versions of the Java Tutorial did address this more fully because it was a "newer" concept back then, but over time we de-emphasized this information. Of course, we can't ignore it entirely, but we aren't trying to re-invent the wheel.

So the tutorial won't be teaching MVC design, per se. But I do think there's room for it to show examples which were created with MVC design.

Thanks for your examples, Arjan. This is a great topic.

Sharon

Message was edited by: sfshaza

aberrant
Offline
Joined: 2006-02-02
Points: 0

This is a tough question. MVC is one of many valid design patterns. These patterns should absolutely be in a programmers toolbox. I must say though that unless the API and architecture push the developer towards using a specific pattern that it should not be the focus of a component level tutorial. Arjan has definitely made it the focus of the tutorial and the actual details on working with sliders have been obfuscated. Maybe this is a candidate for the "Advanced Topics" section that has been proposed.

On a technical note this demo uses a List to hold listeners. All swing components use the EventListenerList. http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/event/EventListenerL...

The most importation thing though is the iteration order for firing events. They are fired last to first. Here is some example code from the EventListenerList javadoc.

for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==FooListener.class) {
// Lazily create the event:
if (fooEvent == null)
fooEvent = new FooEvent(this);
((FooListener)listeners[i+1]).fooXXX(fooEvent);
}
}

As you can see it iterates through the list backwards. This is done to handle the situation where a listener is removed as a result of the event being fired. Removal of elements can change how forward looping occurs and in some circumstances the same listener can be fired more then once.

I'm happy to see other people posting beside Sharon and I.

Collin

ariano
Offline
Joined: 2005-09-24
Points: 0

Hello Collin,

thanks for your reply, you also obviously spent some time with the demo code, thanks for that.

I disagree with you about me changing the focus of the tutorial to MVC. The focus is still on the Swing components, but using MVC to show proper GUI programming. This to give newbees a good set of examples from which to start working on their own projects.

What is your point about the EventListenerList? Is it that, because Swing uses it, the MVC implementation should use it as well?

About the backwards iteration by EventListenerList, I chose forward iteration because I do not want backwards iteration: the listener that adds itself first should be notified first.

In List's javadoc it says: "The List interface provides a special iterator, called a ListIterator, ...". In ListIterator it says: "An iterator for lists that allows the programmer to traverse the list in either direction, modify the list during iteration, and ...". Do you have information about forward iteration being dangerous when removing elements from a List? Or, for that matter, about backwards iteration being less dangerous?

Cheers, Arjan.

aberrant
Offline
Joined: 2006-02-02
Points: 0

Arjan,

The issue is that during the event firing process a the target of a notification can choose to remove itself from the collection of listeners. This removal process would happen during the iteration process from outside the current iterator and throw a ConcurrentModificationException.

The following program illustrates the type of problem.

public class ListTest {

private ArrayList test = new ArrayList();

public ListTest(){
test.add("1");
test.add("2");
test.add("3");
test.add("4");
}

private void runForwardsRemoval(){
Iterator itt = test.iterator();
while(itt.hasNext()){
String item = itt.next();
System.out.println(item);
removeItem(item);
}
}

public void removeItem(String item){
test.remove(item);
}

private void runBackwardsRemoval(){
for(int i = test.size() -1; i >= 0; i--){
String item = test.get(i);
System.out.println(item);
removeItem(item);
}
}

public static void main(String[] args){

ListTest test = new ListTest();
test.runForwardsRemoval();
}
}

In this very simple example using the iterator to remove the "item" would solve the problem. Since targets of events have no access to the remove method of iterator they cannot safely remove themselves in a forward order system. Backwards remove does not have this issue.

Also backwards firing gives consumers of the interface the ability to pre-empt unwanted behaviour. In some systems, like key listeners, the event can even be consumed by the most recently added listener and ignored by all others.

EventListenerList has some functionality that you may or may not find useful. Iteration order though really is important. I learned about it in the book Swing Hacks in the chapter "fire events and stay bug free".

Collin

ariano
Offline
Joined: 2005-09-24
Points: 0

I'll be damned. You're right. Thanks for your persistence. I have worked with the mvc code for years now, never had a problem. Now that I come to think of it, it may be that backwards iterating is not always safe either, for example if a listener adds or removes other listeners.

Anyway, let's not get sidetracked too much by these technical details, they can be resolved relatively easily. Deciding on how the tutorial should be shaped is the far more difficult question.

Cheers, thanks again, Arjan.

aberrant
Offline
Joined: 2006-02-02
Points: 0

No problem Arjan. Thanks for contributing.

Collin