I have made a Swing application that just creates a Random Playlist of media files & plays or deletes them.
I have never seen a full Swing application's code, so I dont know if I am writting Swing applications correctly. So I am looking for people to go over my code & give me advice on how to improve it in any way, & most importantly tell me if there is a common Swing structure that maybe I should follow.
Please give me your advice & criticism on my Random Playlist App:
Main Application class:
/*
******************************************************************************************
Random Playlist Application
Author:
Platform: Java Swing & AWT
******************************************************************************************
Main Logic & Frame Class:
Handles all application events & creates the main frame window
******************************************************************************************
*/
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
public class RandomPlaylist extends JFrame implements ActionListener
{
/// Class Variables:
private static final long serialVersionUID = 1L; // How come I have to have this???
private JPanel componentPanel;
private FolderPathPanel pathPanel;
private JTextField playlistSizeTf;
private JTable playlistTb;
private JRadioButton listAllMediaRb;
private JRadioButton randomListMediaRb;
private JButton createPlaylistBt;
private JButton clearPlaylistBt;
private JButton deleteFileBt;
private JButton playAllFileBt;
private JButton playFileBt;
private enum ListType { ALLLIST, RANDOMLIST };
private Vector <File> mediaList;
private Vector <File> directoryList;
private Vector <File> playlist;
private int playlistSize;
private ListType playlistType;
private boolean directoryAltered;
private FileFilter directoryFilter;
/// Class Methods:
/**
* Constructor:
*
*/
public RandomPlaylist()
{
this.setTitle( "Random Playlist Creator" );
this.setDefaultCloseOperation( EXIT_ON_CLOSE );
this.setResizable( false );
initialiseVariables();
initialiseComponents();
bindComponents();
pack();
validate();
this.setVisible( true );
}
/**
* Post: Handle all action events sent to this frame
*
* @param ev
*
*/
public void actionPerformed( ActionEvent ev )
{
if ( ev.getSource() == createPlaylistBt )
{
if ( verifyPlaylistSize() )
{
// Get folder paths if the directories have been altered
if ( directoryAltered )
{
directoryList.clear();
directoryList = pathPanel.getFolderDirectories();
searchFoldersForMedia();
}
if ( directoryList.size() > 0 )
{
createPlaylist();
displayPlaylist();
directoryAltered = false;
}
else JOptionPane.showMessageDialog( this, "Please specify a folder of media files",
"Error", JOptionPane.ERROR_MESSAGE );
}
}
if ( ev.getSource() == clearPlaylistBt )
{
// Should I put the below code in a function?
DefaultTableModel model = (DefaultTableModel) playlistTb.getModel();
while ( model.getRowCount() > 0 )
{
model.removeRow( 0 );
}
playlist.clear();
// OR model.getDataVector().removeAllElements();
// playlistTb.repaint();
}
if ( ev.getSource() == deleteFileBt )
{
File selFiles[] = getSelectedFiles();
if ( selFiles.length > 0 )
{
deleteMedia( selFiles );
}
}
if ( ev.getSource() == playAllFileBt )
{
Vector <File> playlistCpy = (Vector <File>) playlist.clone();
File selFiles[] = (File[]) playlistCpy.toArray();
if ( selFiles.length > 0 )
{
playMedia( selFiles );
}
}
if ( ev.getSource() == playFileBt )
{
File selFiles[] = getSelectedFiles();
if ( selFiles.length > 0 )
{
playMedia( selFiles );
}
}
else if ( ev.getSource() == listAllMediaRb )
{
playlistType = ListType.ALLLIST;
}
else if ( ev.getSource() == randomListMediaRb )
{
playlistType = ListType.RANDOMLIST;
}
}
/**
* Post: Set all class variables to their default value
*
* Question: If a function is void & has no parameters, what do I add to its javadoc?
* Just what the function does? Anything else (info about the variables inside
* the function maybe?)?
*/
private void initialiseVariables()
{
mediaList = new Vector <File>();
directoryList = new Vector <File>();
playlist = new Vector <File>();
playlistSize = 8;
playlistType = ListType.RANDOMLIST;
directoryAltered = true;
directoryFilter = new FileFilter()
{
public boolean accept(File file)
{
if ( file.isDirectory() || isMediaFile( file ) )
{
return true;
}
else return false;
}
};
}
/**
* Post: Set all JComponents to their default values & call buildPanel
* function
*/
private void initialiseComponents()
{
String colNames[] = {"Media Name", "Type", "Size"};
componentPanel = (JPanel) getContentPane();
pathPanel = new FolderPathPanel( this );
playlistSizeTf = new JTextField( "8", 3 );
playlistTb = new JTable( new DefaultTableModel(null, colNames) );
createPlaylistBt = new JButton( "Create Playlist" );
clearPlaylistBt = new JButton( "Clear" );
deleteFileBt = new JButton( "Delete" );
playAllFileBt = new JButton( "Play All" );
playFileBt = new JButton( "Play" );
listAllMediaRb = new JRadioButton( "List All" );
randomListMediaRb = new JRadioButton( "Random List" );
JLabel playlistSizeLb = new JLabel( "Playlist Size: " );
ButtonGroup playlistTypeGrp = new ButtonGroup();
playlistTypeGrp.add( listAllMediaRb );
playlistTypeGrp.add( randomListMediaRb );
randomListMediaRb.setSelected( true );
playlistTb.setPreferredSize( new Dimension(300,150) );
playlistTb.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
// Q. All the below code doesn't work, it should make the JTable
// display horiz & vert lines & show the column names but they
// dont show up??
playlistTb.setShowHorizontalLines( true ); //
playlistTb.setShowVerticalLines( true );
JTableHeader header = playlistTb.getTableHeader();
header.setBackground(Color.yellow);
JScrollPane pane = new JScrollPane( playlistTb );
componentPanel.add(pane);
buildPanelEx2( playlistSizeLb ); // add JComponents to this frame
}
/**
* Post: Add all application JComponents to main panel using the Grid Layout format
*/
private void buildPanel( JLabel playlistSizeLb )
{
Box horizBox = Box.createHorizontalBox();
componentPanel.setLayout( new GridLayout( 5, 1, 0, 10 ) );
componentPanel.setBorder( new CompoundBorder (BorderFactory.createRaisedBevelBorder(),
new EmptyBorder(10,10,0,10)));
componentPanel.add( pathPanel );
horizBox.add( listAllMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( randomListMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeLb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeTf );
componentPanel.add( horizBox );
horizBox = Box.createHorizontalBox();
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( createPlaylistBt );
componentPanel.add( horizBox );
componentPanel.add( playlistTb );
horizBox = Box.createHorizontalBox();
horizBox.add( clearPlaylistBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( deleteFileBt );
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( playAllFileBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playFileBt );
horizBox.setBackground( Color.blue );
horizBox.setForeground( Color.red );
componentPanel.add( horizBox );
}
/**
* Post: Add all application JComponents to main panel using the Border Layout format
*/
private void buildPanelEx( JLabel playlistSizeLb )
{
Box horizBox = Box.createHorizontalBox();
componentPanel.setLayout( new BorderLayout(5,5) ); // 5, 1, 0, 10 ) );
componentPanel.setBorder( new CompoundBorder (BorderFactory.createRaisedBevelBorder(),
new EmptyBorder(0,10,10,10)));
componentPanel.add( pathPanel, BorderLayout.PAGE_START );
horizBox.add( listAllMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( randomListMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeLb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeTf );
componentPanel.add( horizBox, BorderLayout.LINE_END );
horizBox = Box.createHorizontalBox();
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( createPlaylistBt );
componentPanel.add( horizBox, BorderLayout.LINE_START );
componentPanel.add( playlistTb, BorderLayout.AFTER_LAST_LINE );
horizBox = Box.createHorizontalBox();
horizBox.add( clearPlaylistBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( deleteFileBt );
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( playAllFileBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playFileBt );
horizBox.setBackground( Color.blue );
horizBox.setForeground( Color.red );
componentPanel.add( horizBox, BorderLayout.PAGE_END );
}
/**
* Post: Add all application JComponents to main panel using one main Box where
* horizontal boxes are added to it
*/
private void buildPanelEx2( JLabel playlistSizeLb )
{
Box mainBox = Box.createVerticalBox();
Box horizBox = Box.createHorizontalBox();
componentPanel.setLayout( new BorderLayout() );
componentPanel.setBorder( new CompoundBorder (BorderFactory.createRaisedBevelBorder(),
new EmptyBorder(0,10,10,10)));
mainBox.add( pathPanel );
horizBox.setBorder( new EmptyBorder(0,0,10,0) );
horizBox.add( listAllMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( randomListMediaRb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeLb );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playlistSizeTf );
mainBox.add( horizBox );
horizBox = Box.createHorizontalBox();
horizBox.setBorder( new EmptyBorder(0,0,10,0) );
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( createPlaylistBt );
mainBox.add( horizBox );
mainBox.add( playlistTb );
horizBox = Box.createHorizontalBox();
horizBox.setBorder( new EmptyBorder(10,0,0,0) );
horizBox.add( clearPlaylistBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( deleteFileBt );
horizBox.add( Box.createHorizontalGlue() );
horizBox.add( playAllFileBt );
horizBox.add( Box.createHorizontalStrut(10) );
horizBox.add( playFileBt );
horizBox.setBackground( Color.blue );
horizBox.setForeground( Color.red );
mainBox.add( horizBox );
componentPanel.add( mainBox, BorderLayout.CENTER );
}
/**
* Post: Register/Add event listeners to JComponents
*/
private void bindComponents()
{
// playlistTb add a listener
listAllMediaRb.addActionListener( this );
randomListMediaRb.addActionListener( this );
createPlaylistBt.addActionListener( this );
clearPlaylistBt.addActionListener( this );
deleteFileBt.addActionListener( this );
playAllFileBt.addActionListener( this );
playFileBt.addActionListener( this );
}
/**
* Post: Record whenever a new 'folder destination' has been input or a folder
* path has been altered within the JComponent FolderPathPanel pathPanel.
*/
public void adviseDirectoryChange()
{
directoryAltered = true;
}
/**
*
* Post: Returns true if the value in playlistTf is a valid integer
* value and stores the value in the variable playlistSize
*
*
* @return Returns true if value in playlist is an int > 0, else false
*
*/
private boolean verifyPlaylistSize()
{
try
{
int size = Integer.parseInt( playlistSizeTf.getText() );
// Make sure size is not 0 or less
if ( size < 1 )
{
return false;
}
playlistSize = size;
}
catch ( NumberFormatException ne )
{
JOptionPane.showMessageDialog( this, "Playlist Size must be an integer greater than 0",
"Error", JOptionPane.ERROR_MESSAGE );
return false;
}
return true;
}
/**
* Post: Search directories for media files & store them in the vector
* mediaList
*/
private void searchFoldersForMedia()
{
mediaList.clear();
for ( int i=0; i < directoryList.size(); i++)
{
File folder = directoryList.elementAt( i );
File directoryFiles[] = folder.listFiles( directoryFilter );
// If folder actually exists
if ( directoryFiles != null )
{
recursiveCollectMedia( directoryFiles, 1 );
}
else JOptionPane.showMessageDialog( this,
folder.getName() + " is an invalid directory or does not exist",
"Error", JOptionPane.ERROR_MESSAGE );
}
}
/**
*
* Post: Recursively collect all media files inside the array files. This
* function will also search in any directories within 'files' array
* if the degree of separation is above zero/within a specified
* limit
*
*/
private void recursiveCollectMedia( File files[], int degreeSeparation )
{
int fileIndex = 0;
int maxIndex = files.length;
while ( fileIndex < maxIndex )
{
// if this file is a directory search in there too
if ( files[ fileIndex ].isDirectory() && degreeSeparation > 0 )
{
recursiveCollectMedia( files[ fileIndex ].listFiles( directoryFilter ),
degreeSeparation - 1 );
}
else if ( files[ fileIndex ].isFile() )
{
mediaList.add( files[ fileIndex ] );
}
fileIndex++;
}
/* To make it a REAL recursive function
if ( files.length > 0 )
{
// if this file is a directory search in there too
if ( files[ 0 ].isDirectory() && degreeSeparation > 0 )
{
recursiveCollectMedia( files[ 0 ].listFiles( directoryFilter ),
degreeSeparation - 1 );
}
else if ( files[ 0 ].isFile() )
{
mediaList.add( files[ 0 ] );
files.deleteElement(0);
recursiveCollectMedia( files, degreeSeparation );
}
}
else return; */
}
/**
*
* Post: Returns true if the parameter is a media file with a valid extension.
* Valid extension types; avi, wmv, flv, mpeg, mp3, mp4.
*
* @param f file found in specified directory
* @return Returns true if file extension type meets criteria else false
*
*/
private boolean isMediaFile( File f )
{
/*
Q. Is there a better way to determine the file type/extension of a file?
The below code seems to only be able to identify between applications & directories?
FileDataSource ds = new FileDataSource( f );
String fileType = ds.getContentType();
System.out.println( fileType );
System.out.println( f.getName() );
if ( fileType == "application/octet-stream" ) //"video/quicktime" || fileType == "video/x-msvideo" ) // ||
//fileType == "mpeg" || fileType == "wmv" )
{
return true;
}
*/
if ( f.getName().endsWith("rmvb") )
{
return true;
}
else if ( f.getName().endsWith("avi") )
{
return true;
}
else if ( f.getName().endsWith("mp3") )
{
return true;
}
else if ( f.getName().endsWith("mp4") )
{
return true;
}
else if ( f.getName().endsWith("mpeg") )
{
return true;
}
else if ( f.getName().endsWith("wmv") )
{
return true;
}
else if ( f.getName().endsWith("flv") )
{
return true;
}
else return false;
}
/**
* Post: Create a playlist of random media files found in directories
*/
private void createPlaylist()
{
playlist.clear();
// If no media files exist in directories
if ( mediaList.size() <= 0 )
{
JOptionPane.showMessageDialog( this, "No media files found in directories",
"Notify", JOptionPane.INFORMATION_MESSAGE );
return;
}
// Build a playlist of randomly selected media
else if ( playlistType == ListType.RANDOMLIST )
{
if ( mediaList.size() < playlistSize )
{
playlistSize = mediaList.size();
}
Vector <Integer> randomNums = randomGenerate( playlistSize, mediaList.size() );
for (int i=0; i<playlistSize; i++)
{
playlist.add( mediaList.elementAt( randomNums.elementAt(i) ) );
}
}
// Build a playlist containing all media files
else playlist = (Vector <File>) mediaList.clone();
}
/**
* Post: Display random selection of media files in JTable
*/
private void displayPlaylist()
{
DefaultTableModel model = (DefaultTableModel) playlistTb.getModel();
for (int i=0; i<playlist.size(); i++)
{
File selFile = playlist.elementAt(i);
Object newRow[] = { selFile.getName(), "", selFile.getTotalSpace() };
model.addRow( newRow );
}
validate();
// playlistTb.repaint(); // do I need to do these???
// pack();
}
/**
*
* Post: Return a vector containing random numbers with no repeats.
*
* @param size the size of the vector to be returned
* @param max the maximum value a random number can have
* @return Returns an integer vector of random numbers
*
*/
private Vector<Integer> randomGenerate( int size, int max )
{
Vector <Integer> randomNums = new Vector<Integer>();
// max must be greater than the perferred size to avoid infinite loop
if ( max < size )
{
return null;
}
while ( randomNums.size() < size )
{
int rand = (int) Math.abs(Math.random() * max) ;
// Make sure rand is not a repeated number
if ( !randomNums.contains( rand ) )
{
randomNums.add( rand );
}
}
return randomNums;
}
/**
*
* Post: Determine which JTable rows the user has selected therefore what files
* the user has selected
*
* @return Returns an array of files associated with the JTable rows the
* user has selected
*
*/
private File[] getSelectedFiles()
{
int selRows[] = playlistTb.getSelectedRows();
// if nothing is selected
if ( selRows.length == -1 )
{
return null;
}
File selFiles[] = new File[ selRows.length ];
for (int i=0; i< selFiles.length; i++)
{
selFiles[i] = playlist.elementAt( selRows[i] );
}
return selFiles;
}
/**
*
* Post: Play all selected media files with the operating systems default player
*
* @param media a file array containing all files the user wants to play/open
*
*/
private void playMedia( File media[] )
{
Desktop myDesktop = null;
if ( !Desktop.isDesktopSupported() )
{
JOptionPane.showMessageDialog( this,
"The Java class java.awt.Desktop is not supported on the current platform ",
"Error:", JOptionPane.ERROR_MESSAGE );
return;
}
else myDesktop = Desktop.getDesktop();
// Play all media files
for (int i=0; i< media.length; i++)
{
try
{
myDesktop.open( media[i] );
System.out.println( "Should be playing it ");
}
catch ( IOException e )
{
JOptionPane.showMessageDialog( this,
"Error opening media file","Failed to open file: " + media[i].getName(),
JOptionPane.ERROR_MESSAGE );
}
}
}
/**
*
* Post: Delete all files within the array 'media' from the users hard drive
*
* @param media a file array containing all files the user has selected to
* delete from their hard drive
*
*/
private void deleteMedia( File media[] )
{
int decision = JOptionPane.showConfirmDialog( this,
"Are you sure you want to delete " + media.length + " files?",
"Prompt", JOptionPane.YES_NO_CANCEL_OPTION );
if ( decision == JOptionPane.YES_OPTION )
{
for (int i=0; i<media.length; i++)
{
boolean success = false;
if ( !media[i].exists() )
{
throw new IllegalArgumentException(
"Delete Function: no such file or directory: " + media[i].getName());
}
else if ( !media[i].canWrite() )
{
throw new IllegalArgumentException(
"Delete Function: write protected " + media[i].getName() );
}
else success = media[i].delete();
if ( success )
{
// delete cell from JTable
}
else JOptionPane.showMessageDialog( this,
"Failed to delete " + media[i].getName(),
"Error:", JOptionPane.ERROR_MESSAGE );
}
}
JOptionPane.showMessageDialog( this,
"Delete Function finished",
"Notify:", JOptionPane.INFORMATION_MESSAGE );
}
/**
* Main:
*/
public static void main(String[] args)
{
RandomPlaylist test = new RandomPlaylist();
}
}
Folder Destination JPanel class:
/*
******************************************************************************************
Random Playlist Application
Author:
Platform: Java Swing & AWT
******************************************************************************************
'Folder Path' Panel Class:
Allows us to dynamically create JComponents which allow the user to specify a
specific folder to import media files(to create a playlist) from.
******************************************************************************************
*/
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
public class FolderPathPanel extends JPanel implements ActionListener
{
/// Class Variables:
private RandomPlaylist parent;
private Vector <JTextField> textFields;
private Vector <JButton> browseBts;
private JButton addNewElementBt;
/// Class Methods:
/**
* Constructor:
*
*/
public FolderPathPanel( RandomPlaylist nParent )
{
parent = nParent;
textFields = new Vector <JTextField>();
browseBts = new Vector <JButton>();
addNewElementBt = new JButton( "Add" );
initialise();
insertNewElement();
insertNewElement();
}
/**
* Post: Handle all action messages sent to this panel
*/
public void actionPerformed( ActionEvent ev )
{
if ( ev.getSource() == addNewElementBt )
{
insertNewElement();
parent.pack();
}
else if ( ev.getSource() instanceof JButton )
{
// User has selected to choose a folder to import media files from
JFileChooser folderDialog = new JFileChooser();
folderDialog.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
if ( folderDialog.showDialog(null, "Select") == JFileChooser.APPROVE_OPTION )
{
File folder = folderDialog.getSelectedFile();
int textFieldIndex = browseBts.indexOf( ev.getSource() );
textFields.elementAt( textFieldIndex ).setText( folder.getPath() );
parent.adviseDirectoryChange();
}
/* OR
int returnVal = folderDialog.showOpenDialog( LoadAction.this );
if ( JFileChooser.APPROVE_OPTION == folderDialog.showOpenDialog(LoadAction.this) )
{
System.out.println("==> Dir="+returnVal);
}
*/
}
else if ( (ev.getSource() instanceof JTextField) ) //&& ev.getActionCommand() == keyChange)
{
// The user has altered the contents of a text field so advice parent
// we need to research/search new directories for media files
parent.adviseDirectoryChange();
}
}
/**
* Post: Add main JComponents to this panel
*/
private void initialise()
{
Box hBox = Box.createHorizontalBox();
this.setLayout( new GridLayout(1,1,0,5) );
this.setBorder( new EmptyBorder(0,5,0,5) );
addNewElementBt.addActionListener( this );
hBox.add( Box.createHorizontalGlue() );
hBox.add( Box.createHorizontalStrut(10) );
hBox.add( addNewElementBt );
this.add( hBox );
this.validate();
}
/**
* Post: Add a new JTextField & JButton to allow the user to select another
* folder to import media files from
*/
private void insertNewElement()
{
GridLayout panelLayout = (GridLayout) this.getLayout();
Box hBox = Box.createHorizontalBox();
textFields.add( new JTextField() );
browseBts.add( new JButton("Browse") );
browseBts.lastElement().addActionListener( this );
hBox.add( textFields.lastElement() );
hBox.add( Box.createHorizontalStrut(10) );
hBox.add( browseBts.lastElement() );
// add new grid cell to panel
panelLayout.setRows( panelLayout.getRows() + 2 );
panelLayout.removeLayoutComponent( addNewElementBt );
this.add( hBox); // add to 2nd last grid cell
this.add( addNewElementBt );
this.validate();
this.repaint();
}
/**
*
* Post: Send all the directories in this panels' JTextFields to Random Playlist
* class (so it can search them for media files)
*
* @return Returns a string vector containing the absolute directories the user
* has chosen/identified for importing media files
*
*/
public Vector <String> getFolderPaths()
{
Vector <String> folderPaths = new Vector <String>();
for ( int i=0; i<textFields.size(); i++ )
{
String folderDir = textFields.elementAt( i ).getText();
if ( folderDir != "" || folderDir != null )
{
folderPaths.add( folderDir );
}
}
return folderPaths;
}
/**
*
* Post: Send all the directories in this panels' JTextFields to Random Playlist
* class (so it can search them for media files)
*
* @return Returns a file vector containing the absolute directories the user
* has chosen/identified for importing media files
*
*/
public Vector <File> getFolderDirectories()
{
Vector <File> folderPaths = new Vector <File>();
for ( int i=0; i<textFields.size(); i++ )
{
String folderDir = textFields.elementAt( i ).getText();
if ( !folderDir.equals("") && !folderDir.equals(null) )
{
folderPaths.add( new File(folderDir) );
}
}
return folderPaths;
}
}

New Topic/Question
Reply




MultiQuote






|