I am a Java developer, designer, public speaker and a JAS 39 Gripen instructor fighter pilot. A somewhat unusual combination I guess, but I like challenges. I have created miglayout.com, migcalendar.com and Wing, a flight planning system used in several Air Forces. I find end user usability to be the most important part of a system and have therefore specialized in creating such applications. Mikael has posted 10 posts at DZone. View Full User Profile

Creating a Media Player in Java - Part 3

10.14.2008
| 12148 views |
  • submit to reddit

Maybe the most important part of a good media player is how usable it is and how good it looks when minimized. Ultimately you should be able to do everything you can do with the full interface yet the real estate should be as small as possible and never be in the way. Obviously there is a lot of hard decisions to make since we don’t live in the ultimate world. Yet.

Currently I have two smaller versions of the GUI. Mini and micro. They should both be dockable to a side of the screen where only a “tab” will be visible when it is semi-hidden. Not sure if that tab will show any info or expose any functionality though. This might even have to differ depending on the platform. There are also the mini-icons available in almost all OS and I don’t want to make a special case for using this media player, so it should play nice with that too.

In the screenshots both versions have the bottom attached see-through pulldown panel. They’re there for the time being but I am unsure if they will make it to the final version. I’m worried that it will look a bit too cluttered, especially for the micro version. Opinions are appreciated.

I have made some smaller changes to the main window as well. Nothing much but I post a screenshot anyway. Changes are mostly around the scrollbars, media add buttons, and I have added a new, or possibly alternative, left panel in the tracks tab. It is a filter panel and only what’s selected will be visible in the tracks list. Normally the user shouldn’t have to be bothered by the actual physical location of the media files, what’s playable is there, but power users and more curious normal users must have an easy way to know what’s where. The acute reader might notice that there seems to be no obvious way to once you have started filtering go back and “show all”. This is something that needs to be resolved. Possibly with a button that is only visible when there’s some filtering applied.

I have now finished the first round of designs and I have actually started coding. Wohoo! I haven’t come that far yet but many parts of the main window is there. Some might say that I am approaching this from the wrong end, starting with the GUI, but I think it’s the only way to do a really good UI. When you have no backing services, other than a home made demo collection of songs, it is easy to get the GUI right and you won’t be tempted to model the GUI after the data, which is by far the most common mistake when you see a crappy UI.

I have shadows, buttons, button glow and fast gradings done already through my already made Decorator implementation, which I have borrowed from migcalendar.com. With it, it is easy to create such effects and they are of course cached in intermediate images when needed. For shadows and glow this means always since they are created with a Gaussian blur filter. I have not yet implemented the fast version of that algorithm that Romain Guy and Chet Hasse introduced in the excellent book Filthy Rich Clients, but I will.

Talking about speed I made a very interesting discovery when I made the tracks TreeTable component. If I overrode validate() to do nothing in my JComponent subclass (used as a TableCellRenderer) the performance of resizing + repainting the JTable was increased 30 times (yes times!). Before that resizing of the window was slow as Java 1.0, but now it is actually pretty fast. This is on Leopard with Java 1.6 and I haven’t tested if the speed increase is the same on the uglier platforms. ;)

Next week I aim for having a WebStarted demo of the GUI. Lets see how that goes. Currently I am struggling with customizing row selection in the JTable. Looks like I have to write my own ListSelectionModel. So Swing..

From http://miginfocom.com/blog

Published at DZone with permission of its author, Mikael Grev.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Dirk Lemmermann replied on Fri, 2008/10/17 - 3:04am

Hi Mikael,

regarding the performance increase that you experienced when you overrode the validate() method: this is exactly what the Swing team does in all of their renderers. But not only this method. The following was taken from the DefaultTableCellRenderer class:

   /*
     * The following methods are overridden as a performance measure to 
     * to prune code-paths are often called in the case of renders
     * but which we know are unnecessary.  Great care should be taken
     * when writing your own renderer to weigh the benefits and 
     * drawbacks of overriding methods like these.
     */

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public boolean isOpaque() { 
	Color back = getBackground();
	Component p = getParent(); 
	if (p != null) { 
	    p = p.getParent(); 
	}
        
	// p should now be the JTable. 
	boolean colorMatch = (back != null) && (p != null) && 
	    back.equals(p.getBackground()) && 
			p.isOpaque();
	return !colorMatch && super.isOpaque(); 
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void invalidate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void validate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void revalidate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(long tm, int x, int y, int width, int height) {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(Rectangle r) { }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void repaint() {
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {	
	// Strings get interned...
	if (propertyName=="text"
                || propertyName == "labelFor"
                || propertyName == "displayedMnemonic"
                || ((propertyName == "font" || propertyName == "foreground")
                    && oldValue != newValue
                    && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {

            super.firePropertyChange(propertyName, oldValue, newValue);
        }
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }

Following this approach made a big difference in my Gantt chart library (http://www.dlsc.com).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.