How It’s Made: Cover Gallery

Cover gallery or List View in Revolucion Library is a JList which dispays cover images. You can see it at the beginning of the Revolucion Library screencast.

First of all, we need to extend JList to achieve a selection change animation. In Revolucion Library I used TimingFramework, but here is the example of how to do the same with a great Trident animation library by Kirill.

ListSelectionListener is used to listen for changes in lists selection value, so we can start the animation when a cover is selected.
selectionBounds represent the current bounds of a selection rectangle, and selectionAlpha is used to control the visibility of the selection rectangle – when there is no items in the list, value is 0.0f.
timeline is the main timeline for animations. animationSpeed is actually a duration of the animation in milliseconds.

public class AnimatedList extends JList implements ListSelectionListener, ComponentListener {

	private float selectionAlpha = 0.0f;
	private float newSelectionAlpha = 1.0f;

	private Rectangle selectionBounds = new Rectangle(0, 0, 0, 0);
	private Rectangle currentSelectionBounds = new Rectangle(0, 0, 0, 0);
	private Rectangle newSelectionBounds = new Rectangle(0, 0, 0, 0);

	private Timeline timeline;
	private int animationSpeed = 400;

	private int selectionBorderWidth = 1;
	private int selectionBorderRoundness = 10;
	private Color selectionBorderColor = Color.gray;

	public AnimatedList() {
		setSelectionBackground(new Color(255, 255, 255, 60));
		setSelectionForeground(Color.black);
		setOpaque(false);
		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		setLayoutOrientation(JList.HORIZONTAL_WRAP);
		setVisibleRowCount(-1);

		addListSelectionListener(this);
		addComponentListener(this);

		initAnimation();
	}

Next we need to setup the Timeline …
Every time timeline.play() is called, selectionBounds will change from currentSelectionBounds to newSelectionBounds, and selectionAlpha from it’s current value to a newSelectionAlpha value. And we add Repaint callback so JList is repainted when these properties change.


private void initAnimation() {
 	timeline = new Timeline(this);
 	timeline.setDuration(animationSpeed);
 	timeline.addPropertyToInterpolate("selectionBounds", currentSelectionBounds, newSelectionBounds);
 	timeline.addPropertyToInterpolateTo("selectionAlpha", newSelectionAlpha);
 	timeline.setEase(new Spline(0.8f));
 	timeline.addCallback(new Repaint(this));
 	timeline.addCallback(new TimelineCallbackAdapter() {
 		@Override
 		public void onTimelineStateChanged(TimelineState oldState, TimelineState newState, float durationFraction, float timelinePosition) {
 			if (newState.equals(TimelineState.DONE))
 				currentSelectionBounds.setBounds(newSelectionBounds);
 		}
 	});
 }

Then we implement ListSelectionListener and ComponentListener.


 @Override
 public void valueChanged(ListSelectionEvent e) {
 	if (!e.getValueIsAdjusting())
 		updateSelectionBounds();
 }

 @Override
 public void componentHidden(ComponentEvent e) {}

 @Override
 public void componentMoved(ComponentEvent e) {
 	updateSelectionBounds();
 }

 @Override
 public void componentResized(ComponentEvent e) {
 	updateSelectionBounds();
 }

 @Override
 public void componentShown(ComponentEvent e) {
 	updateSelectionBounds();
 }

And here is what updateSelectionBounds() method does:


 public void updateSelectionBounds() {
 	int index = getSelectedIndex();
 	// If there is something selected we set the selection bounds to the bounds of the selected cell ...
 	if (index > -1) {
 		newSelectionBounds.setBounds(getCellBounds(index, index));
 		newSelectionAlpha = 1.0f;
 	}
 	// ... or if there is no selection, we do this to simulate rectangle dissapearing ...
 	else {
 		newSelectionBounds.setBounds(getX() + getWidth() / 2, getY() + getHeight() / 2, 0, 0);
 		newSelectionAlpha = 0.0f;
 	}

 	// ... and than start the animation
 	if (!timeline.isDone())
 		timeline.cancel();
 	timeline.play();
 }

And finally, override the paintComponent() to do the selection rectangle painting.


@Override
 protected void paintComponent(Graphics g) {
 	super.paintComponent(g);

 	Graphics2D g2 = (Graphics2D) g.create();
 	g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, selectionAlpha));
 	g2.setColor(getSelectionBackground());
 	g2.fillRoundRect(selectionBounds.x, selectionBounds.y, selectionBounds.width, selectionBounds.height, selectionBorderRoundness, selectionBorderRoundness);
 	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 	g2.setColor(selectionBorderColor);
 	g2.setStroke(new BasicStroke(selectionBorderWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
 	g2.drawRoundRect(selectionBounds.x, selectionBounds.y, selectionBounds.width, selectionBounds.height, selectionBorderRoundness, selectionBorderRoundness);
 	g2.dispose();
 }

Add getters and setters for selectionBounds and selectionAlpha, and that’s it with animations.

To speed up image painting in the JList, I wrote a ListCellRenderer that will cache cover images.


public class CoverListCellRendererTest extends DefaultListCellRenderer {

 // http://java.sun.com/javase/6/docs/api/java/lang/ref/SoftReference.html
 private HashMap<String, SoftReference<ImageIcon>> thumbnails;

 public CoverListCellRendererTest() {
 	thumbnails = new HashMap<String, SoftReference<ImageIcon>>();

 	setOpaque(false);
 	setHorizontalAlignment(JLabel.CENTER);
 	setHorizontalTextPosition(JLabel.CENTER);
 	setVerticalTextPosition(JLabel.BOTTOM);
 }

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

 	setText(movie.getTitle());

 	String coverFileName = movie.getCover();
 	if (coverFileName != null)
 		setIcon(getThumbnail(coverFileName));

 	return this;
 }

 private ImageIcon getThumbnail(String coverFileName) {
 	ImageIcon icon = null;

 	SoftReference<ImageIcon> reference = thumbnails.get(coverFileName);
 	if (reference != null)
 		icon = reference.get();

 	if (icon == null) {
 		try {
 			BufferedImage image = ImageUtils.getCoverThumb(coverFileName);
 			icon = new ImageIcon(image);
 			thumbnails.put(coverFileName, new SoftReference<ImageIcon>(icon));
 		}
 		catch (IOException e) {
 			icon = new ImageIcon(ImageUtils.getEmptyCover());
 		}
 	}

 	return icon;
 }
}

Advertisements

How It’s Made: Youtube player in Java

Few of you asked, so here is how …

First of all, you need a way to search Youtube and get url of some video. And that is really simple since there is an official Google Data API for using some of the services Google offers, and there is an open source GData Java Client Library, and great tutorials and examples of how to use it.

So, for example, from a query for ‘Revolucion Movie Library’ you will get a List of VideoEntries. And from VideoEntry you can get stuff like video title, location, type, url, duration, thumbnails etc. Video location is a string like “http://www.youtube.com/v/9yi9EdbYaFc&…&#8221;. Remember that :)

Now we need to play that somehow …

If you have a right codec, youtube video can be played in probably any video player – try opening above url (that you remembered :)) in Windows Media Player. But there are problems, at least I had them. First, that seems now is gone (probably fixed in newer versions of WMP or Flash), you couldn’t interact with a video, so you couldn’t click ‘Play’. That can be resolved by appending “&autoplay=1” to url, but than you can’t stop it. And, there comes a second problem – you can’t even really stop it with “Stop” button in your player. I first written a wrapper around WMP so I can use it in Swing, but that did’t work quite well. Also I tried JMC, but it crashed from time to time.

Other solution is playing Youtube videos in web browser. And this way you can even use a chromeless player (“… by definition, a YouTube video with no controls”), and control it using JavaScript. And if we had JWebPane this would be excellent. But since it isn’t finished yet … there are alternatives: WebBrowser from JDIC project, WebBrowser from JDICplus, …, and JWebBrowser from the DJ project. In Revolucion Library I used latter. It’s an SWT-based implementation of web browser component and under Windows it uses Internet Explorer or Mozilla and Mozilla on other platforms. Features that I liked – you can call JavaScript function in current page from Java, and can send commands from html page to Java application. And it works pretty good.

So I made a simple html page with chromeless Youtube player and, among few others, it contains following JavaScript functions:

function onYouTubePlayerReady(playerId) {
  ytplayer = document.getElementById(“myytplayer”);
  …
  sendCommand(“loaded”);
  }

function loadNewVideo(id, startSeconds) {
  if (ytplayer) {
  ytplayer.loadVideoById(id, parseInt(startSeconds));
  }
  }

function closePlayer() {
  stop();
  clearVideo();
  sendCommand(‘close’);
  }
function sendCommand(command) {
  var s = ‘command://’ + encodeURIComponent(command);
  for(var i=1; ionYouTubePlayerReady function and it will send command to my YouTubePlayer component. WebBrowserListener receives the command and using the executeJavascript method calls the loadNewVideo function with videoId as a parameter. VideoId is a part of video url after “/v/” that you might remember from above :) When user clicks “close” button in the html page, closePlayer() function is called and command sent to application.

So, this is it. All in all, this is not the best solution, you must include SWT jar with your application, but it works for now.

I’m desperately waiting for JWebPane …

Added day later:

I forgot to mention WebKit4Java by Genuitec which is currently available for SWT, but there is also going to be a Swing version.

P.S. My english isn’t excellent so I’m sorry for grammatical and other mistakes I made.