Está en la página 1de 19

Android Media Player

People ask me all the time how to develop an Android program that can load up and play music. Sadly, there are not a lot of good answers on the internet. The good news is, it's really not all that bad. Today we are going to: 1. Learn how to implement features of the android.media.MediaPlayer class. 2. Load Drawables on the fly from the SD card using the createFromPath() function 3. Learn what assets are and how to incorporate these into our program 4. Learn how to load media from an SD card 5. Write a fully functional Media Player program 6. Become a better Java Monkey! By the end of this tutorial, you will not only have a fully functional simple media player, but you will also have a clear understanding of how to incorporate media into your apps. Before going any further, make sure your development environment is set up correctly for Android development. If you are unsure, check my guide here. *** I recommend you download the source code for this project here, as it contains sample tracks and all of the drawables you will need, as well as a cheesy icon I made myself ***

File New Android Project

New Android Project (Name, Path, Target)

New Android Project (Properties)

File Import

Import Existing Projects into Workspace

Browse for the project you want to import and click OK, check the checkbox, and then click Finish

Here we go!
When making a media player, a few things come to mind: 1. There will be multiple tracks to load 2. Moving from track to track 3. Play, Pause, and Stop functionality 4. Shuffle and Loop functionality 5. The SD Card must be mounted by the phone in order to load music/pictures from it Doesn't sound too hard right? So let's begin: 1. Create a new project File > New > Android Project

2. Name it MyMediaPlayer 3. Set the MinSdk to 3 and the TargetSdk to 8 4. Name your package 5. Eclipse will create the onCreate Activity for you (e.g. 'MyMediaPlayerActivity.java' ) If you want to name it something else, go ahead; it won't hurt anything. If you have downloaded the source for this project and want to simply import it: 1. Make sure that theMyMediaPlayer project is in the "Eclipse Projects" folder 2. File > Import > Existing Projects into Workspace 3. Browse > MyMediaPlayer > OK 4. Make sure the checkbox in the "Projects:" box is checked > Finish

Now let's take a look at the manifest file.

Manifest.xml
?

1 2 <?xml version="1.0" encoding="utf-8"?> 3 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 4 package="com.technegames.mymediaplayer" android:versionCode="1" 5 android:versionName="1.0"> 6 <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" /> 7 8 <application android:icon="@drawable/icon" android:label="@string/app_name"> 9 <activity android:name=".MyMediaPlayerActivity" android:configChanges="orienta android:label="@string/app_name"> 10 <intent-filter> 11 <action android:name="android.intent.action.MAIN" /> 12 <category android:name="android.intent.category.LAUNCHER" /> 13 </intent-filter> 14 </activity> 15 </application> 16 <uses-permission android:name="android.permission.WAKE_LOCK" /> 17 </manifest> 18 19 Manifest
As you can see, the manifest acts like a parental guardian for your App. It is responsible for supplying the versionCode, which is important when uploading applications to the Android Market. It also determines how and when activities can be launched via intent filters. The manifest is also

where you specify certain permissions such as internet access. For example, in MyMediaPlayer, we use the WAKE_LOCK permission so that we can keep the phone on while the app is running. Otherwise, the song would pause itself after 20 seconds or so. Without the permission specified in the manifest, the application would crash upon loading because it attempted to utilize a wake lock without permission. The Android Manifest is an important XML document, so familiarize yourself with it. Eclipse will generate a manifest file for you when creating a new project, so if you created this project from scratch, the only line you would have to add would be the "uses-permission" for the WAKE_LOCK Alright, while on the topic of XML documents, let's take a look at our main layout. After this we will get to the Java code I promise!

main.xml
?

1 2 3 <?xml version="1.0" encoding="utf-8"?> 4 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/screen" android:layout_width="fill_parent" android:layout_height= 5 <LinearLayout android:orientation="vertical" android:layout_alignParentTop="true" 6 android:layout_alignParentLeft="true" android:layout_width="fill_parent" andro 7 android:weightSum="7" > 8 <LinearLayout android:layout_weight="6" android:orientation="vertical" android: 9 android:layout_alignParentLeft="true" android:layout_width="fill_parent" a <ImageView android:id="@+id/bg" android:layout_width="fill_parent" android: 10 </ImageView> 11 </LinearLayout> 12 <LinearLayout android:layout_weight="1" android:id="@+id/buttons" android:orien 13 android:layout_alignParentBottom="true" android:layout_alignParentLeft="tr 14 android:layout_width="fill_parent" android:layout_height="0dip" android:ba <Button android:id="@+id/btnPrevious" android:layout_width="0dip" android:l 15 android:layout_weight="1" android:background="@drawable/previous" andr 16 android:onClick="click" /> 17 <View android:layout_weight="1" android:layout_width="0dip" android:layout_ 18 <Button android:id="@+id/btnPlay" android:layout_width="0dip" android:layou android:layout_weight="1" android:background="@drawable/play" android: 19 android:onClick="click" /> 20 <View android:layout_weight="1" android:layout_width="0dip" android:layout_ 21 <Button android:id="@+id/btnNext" android:layout_width="0dip" android:layou 22 android:layout_weight="1" android:background="@drawable/next" android: 23 android:onClick="click" /> </LinearLayout> 24 </LinearLayout> 25 </RelativeLayout> 26 27 28 main

If you are unfamilar with XML, at first glance this code might look very confusing. I assure you it is very simple, and is one of the greatest features of Android. If you are coming into Android Development from a Java Swing background, you will be happy to know that you can completely separate your layout/gui elements from your main code by setting it up in an XMLdocument. In the MyMediaPlayer program, we use a Relative Layout with Linear Layouts nested inside. The Relative Layout allows us to specify where the Linear Layouts are placed in relation to each other. The topmost Linear Layout is used for the album art. We use theandroid:layout_alignParentTop="true" and android:layout_alignParentLeft="true"to specify that the album art layout is aligned to the topleft corner of the screen. We use layout_weight to dictate that the layout containing the image takes up 6/7 of the screen, with the remaining 1/7 for the buttons. Speaking of buttons, in each of the buttons there is android:onClick="click". This means that when you click on any of these buttons, they call a function called click in MyMediaPlayerActivity.java, which we will get to in a bit. The best way to learn Android layouts is to simply play around with one that works. Adjust settings and values and notice the changes. Parent/Child relationships can get very complicated, so it is best to just take it one step at a time. There is a nice Tool online that generates an XML layout based on what you drag-and-drop onto the screen. If you think this tool may help you with Android Layouts, click here to download it! Alright, Let's dive into some java code!

Music.java
?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

package com.technegames.mymediaplayer; import java.io.FileDescriptor; import java.io.IOException; import android.content.res.AssetFileDescriptor; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; public class Music implements OnCompletionListener{ MediaPlayer mediaPlayer; boolean isPrepared = false; public Music(AssetFileDescriptor assetDescriptor){ mediaPlayer = new MediaPlayer(); try{ mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), assetDesc mediaPlayer.prepare(); isPrepared = true; mediaPlayer.setOnCompletionListener(this); } catch(Exception ex){ throw new RuntimeException("Couldn't load music, uh oh!");

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

} } public Music(FileDescriptor fileDescriptor){ mediaPlayer = new MediaPlayer(); try{ mediaPlayer.setDataSource(fileDescriptor); mediaPlayer.prepare(); isPrepared = true; mediaPlayer.setOnCompletionListener(this); } catch(Exception ex){ throw new RuntimeException("Couldn't load music, uh oh!"); } } public void onCompletion(MediaPlayer mediaPlayer) { synchronized(this){ isPrepared = false; } } public void play() { if(mediaPlayer.isPlaying()){ return; } try{ synchronized(this){ if(!isPrepared){ mediaPlayer.prepare(); } mediaPlayer.start(); } } catch(IllegalStateException ex){ ex.printStackTrace(); } catch(IOException ex){ ex.printStackTrace(); } } public void stop() { mediaPlayer.stop(); synchronized(this){ isPrepared = false; } } public void switchTracks(){ mediaPlayer.seekTo(0); mediaPlayer.pause(); } public void pause() { mediaPlayer.pause(); }

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 } 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Music

public boolean isPlaying() { return mediaPlayer.isPlaying(); } public boolean isLooping() { return mediaPlayer.isLooping(); } public void setLooping(boolean isLooping) { mediaPlayer.setLooping(isLooping); } public void setVolume(float volumeLeft, float volumeRight) { mediaPlayer.setVolume(volumeLeft, volumeRight); } public void dispose() { if(mediaPlayer.isPlaying()){ stop(); } mediaPlayer.release(); }

Alright, if you gave the above code a good scan, you probably noticed that Music.java is responsible for instantiating our media player and controlling it. It implements the OnCompletionListener so that we can monitor when a song is finished playing. The reason we do all of this in a separate class is because we will instantiate it every time we seek to a different track, rather than trying to load all of the tracks into memory. Whenever you feel comfortable with the Music class, let's move on to the main attraction, MyMediaPlayerActivity.java

MyMediaPlayerActivity.java Variables

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

package com.technegames.mymediaplayer; import import import import import import import import import import import import import import import import import import import import import import import import import java.io.File; java.io.FileDescriptor; java.io.FileInputStream; java.io.IOException; java.util.ArrayList; java.util.List; java.util.Random; android.app.Activity; android.content.Context; android.content.res.AssetFileDescriptor; android.content.res.AssetManager; android.graphics.drawable.Drawable; android.media.AudioManager; android.os.Bundle; android.os.Environment; android.os.PowerManager; android.os.PowerManager.WakeLock; android.view.Menu; android.view.MenuItem; android.view.View; android.view.Window; android.view.WindowManager; android.widget.Button; android.widget.ImageView; android.widget.Toast;

public class MyMediaPlayerActivity extends Activity { WakeLock wakeLock; private static final String[] EXTENSIONS = { ".mp3", ".mid", ".wav", ".ogg", ".mp4" List<String> trackNames; //Playable Track Titles List<String> trackArtworks; //Track artwork names AssetManager assets; //Assets (Compiled with APK) File path; //directory where music is loaded from on SD Card File path2; //directory where album artwork is loaded from on SD Card Music track; //currently loaded track ImageView bg; //Track artwork Button btnPlay; //The play button will need to change from 'play' to 'pause', so Random random; //used for shuffle boolean shuffle; //is shuffle mode on? boolean isTuning; //is user currently jammin out, if so automatically start playin int currentTrack; //index of current track selected int type; //0 for loading from assets, 1 for loading from SD card

MyMediaPlayerActivity Variables
Before we discuss the onCreate activity, let's talk about our Class Level variables. First, we have our wakeLock, which if you recall from earlier is used to keep the phone on. We also have our EXTENSIONS array, which is used to validate that the files we load into our program are actual sound files. The trackNames ArrayList is used to store all of the track names in memory for reference by the media player. The trackArtworks ArrayList matches the trackNames List except it does not contain any extensions. It is used when trying to find a picture with the same name as the track currently playing and if so load that image. The assets are used to retrieve a list of all the files in the 'assets' folder of your program. Assets are the resources that get compiled along with your program. I've included 4 tracks in the source of this program below. The program by default loads up the assets folder. By pressing the MENU button on your phone and then clicking the SOURCE option you can specify the program to load tracks from the 'music' folder on the root of your SD card. The rest of the variables are pretty self explanatory and if not I've included inline comments.

MyMediaPlayerActivity.java onCreate, onResume, onPause


?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.La setVolumeControlStream(AudioManager.STREAM_MUSIC); PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyMediaPlayer") setContentView(R.layout.main); initialize(0); } @Override public void onResume(){ super.onResume(); wakeLock.acquire(); } @Override public void onPause(){ super.onPause(); wakeLock.release(); if(track != null){ if(track.isPlaying()){ track.pause(); isTuning = false; btnPlay.setBackgroundResource(R.drawable.play); } if(isFinishing()){ track.dispose(); finish();

} 28 } else{ 29 if(isFinishing()){ 30 finish(); 31 } } 32 33 } 34 35 36 37 38 39 onCreate, onResume, onPause

These functions override their parent counterparts from Activity class. They are important because they handle the Activity Lifecycle. Lines 4-8 set up full-screen mode and a wakeLock to insure that the phone screen does not turn off (which would result in an onPause event). On Line 9 we set the content view to the layout we created above, meaning we will have an ImageView and 3 Buttons on the screen. The last thing we do in onCreate is call initialize(0). The 0 in the parentheses means that the media player will load files from the program's assets upon load. Change it to a 1 to load from the SD Card upon load. We will look at the initialize method in the next code block. The onResume method is where we will acquire the wakeLock. We do this in onResume because we release it in the onPause method. If we were to acquire the wakeLock in the onCreate method, it would only be done so once. The onPause method is fired when the program is interrupted. For example, the phone is receiving a phone call or the user presses the HOME button. To prepare for something like this, we override the onPause method from Activity. If track is not null and it is playing it gets paused and the pause button gets turned into a play button. We could have also chosen to override onStop, but the isFinishing method serves the same purpose. If the program is finishing, we dispose the mediaplayer object and call the Activity's finish() method.

MyMediaPlayerActivity.java initialize, addTracks, loadTrack


?

1 2 3 4 5 6 7 8 9

private void initialize(int type){ bg = (ImageView) findViewById(R.id.bg); btnPlay = (Button) findViewById(R.id.btnPlay); btnPlay.setBackgroundResource(R.drawable.play); trackNames = new ArrayList<String>(); trackArtworks = new ArrayList<String>(); assets = getAssets(); currentTrack = 0; shuffle = false; isTuning = false; random = new Random();

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

this.type = type; addTracks(getTracks()); loadTrack(); }

//Generate a String Array that represents all of the files found private String[] getTracks(){ if(type == 0){ try { String[] temp = getAssets().list(""); return temp; } catch (IOException e) { e.printStackTrace(); Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_LONG).show( } } else if(type == 1){ if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOU path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTOR path2 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTO String[] temp = path.list(); return temp; } else{ Toast.makeText(getBaseContext(), "SD Card is either mounted elsewhere or } } return null; }

//Adds the playable files to the trackNames List private void addTracks(String[] temp){ if(temp != null){ for(int i = 0; i < temp.length; i++){ //Only accept files that have one of the extensions in the EXTENSIONS arr if(trackChecker(temp[i])){ trackNames.add(temp[i]); trackArtworks.add(temp[i].substring(0, temp[i].length()-4)); } } Toast.makeText(getBaseContext(), "Loaded " + Integer.toString(trackNames.size( } } //Checks to make sure that the track to be loaded has a correct extenson private boolean trackChecker(String trackToTest){ for(int j = 0; j < EXTENSIONS.length; j++){ if(trackToTest.contains(EXTENSIONS[j])){ return true; } } return false; } //Loads the track by calling loadMusic private void loadTrack(){

if(track != null){ 56 track.dispose(); 57 } 58 if(trackNames.size() > 0){ 59 track = loadMusic(type); setImage("drawable/" + trackArtworks.get(currentTrack)); 60 } 61 62 } 63 64 65 66 67 68 69 70 71 72 73 74 75 initialize, addTracks, loadTrack

The initialize method initializes all of our variables so that we do not end up getting null pointer exceptions. The bottom of the method adds all of the tracks from either the compiled assets or from "scard/music", making sure to only add tracks with a valid extension ( e.g. ".mp3", ".mid" ) After that, the loadTrack() method is called. The loadTrack() method will be called everytime a new track has been selected. In the loadTrack() method, we call loadMusic() and setImage(), which we will take a look at below.

MyMediaPlayerActivity.java loadMusic, setImage


?

1 2 3 4 5 6 7 8 9 10 11 12 13

//loads a Music instance using either a built in asset or an external resource private Music loadMusic(int type){ switch(type){ case 0: try{ AssetFileDescriptor assetDescriptor = assets.openFd(trackNames.get(curren return new Music(assetDescriptor); } catch(IOException e){ e.printStackTrace(); Toast.makeText(getBaseContext(), "Error Loading " + trackNames.get(current } return null; case 1: try{ FileInputStream fis = new FileInputStream(new File(path, trackNames.get(cu FileDescriptor fileDescriptor = fis.getFD();

return new Music(fileDescriptor); 14 } catch(IOException e){ 15 e.printStackTrace(); 16 Toast.makeText(getBaseContext(), "Error Loading " + trackNames.get(current 17 } return null; 18 default: 19 return null; 20 } 21 } 22 23 //Sets the background image to match the track currently playing or a default image 24 private void setImage(String name) { if(type == 0){ 25 int imageResource = getResources().getIdentifier(name, null, getPackageName()) 26 if(imageResource != 0){ 27 Drawable image = getResources().getDrawable(imageResource); bg.setImageDrawable(image); 28 } else{ 29 int defaultImageResource = getResources().getIdentifier("drawable/defaultb 30 if(defaultImageResource != 0){ 31 Drawable image = getResources().getDrawable(defaultImageResource); 32 bg.setImageDrawable(image); } 33 } 34 } else if(type == 1){ 35 if(new File(path2.getAbsolutePath(), trackArtworks.get(currentTrack) + ".jpg") 36 bg.setImageDrawable(Drawable.createFromPath(path2.getAbsolutePath() + "/" 37 } else{ int defaultImageResource = getResources().getIdentifier("drawable/defaultb 38 if(defaultImageResource != 0){ 39 Drawable image = getResources().getDrawable(defaultImageResource); 40 bg.setImageDrawable(image); 41 } } 42 } 43 44 } 45 46 47 48 49 50 51 52 53 loadMusic, setImage

The loadMusic is probably the most important method in the entire program. You will want to learn the Music.java class and this method because they can translate to any program that needs music, not just a media player.

In our loadMusic() method, we either load up a track from our compiled assets (the ones I include with the download above) or one of your songs located in the "music" folder on your SD Card. The setImage() method is called immediately the loadMusic() method to change the album art accordingly. In our simple example, it simply scans the "pictures" folder on your SD Card for a picture with same name as the track currently playing excluding the extension and sets it accordingly. If no image can be found, a default image is loaded up from the drawable folder of your project. Alright, I know we have covered a lot of ground here, but we are almost done. The next two sections cover the user input.

MyMediaPlayerActivity.java Menu, Options, setShuffle


?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

@Override public boolean onCreateOptionsMenu(Menu menu){ super.onCreateOptionsMenu(menu); createMenu(menu); return true; } private void createMenu(Menu menu){ MenuItem miLooping = menu.add(0, 0, 0, "Looping");{ miLooping.setIcon(R.drawable.looping); } MenuItem miShuffle = menu.add(0, 1, 1, "Shuffle");{ miShuffle.setIcon(R.drawable.shuffle); } MenuItem miStop = menu.add(0, 2, 2, "Stop");{ miStop.setIcon(R.drawable.stop); } MenuItem miSource = menu.add(0, 3, 3, "Source");{ miSource.setIcon(R.drawable.source); } }

@Override public boolean onOptionsItemSelected(MenuItem item){ switch(item.getItemId()){ case 0: //Set Looping synchronized(this){ if(track.isLooping()){ track.setLooping(false); Toast.makeText(getBaseContext(), "Playing Tracks Sequentially", Toast } else{ track.setLooping(true); Toast.makeText(getBaseContext(), "Looping " + trackNames.get(currentTr } } return true;

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

case 1: //Set Shuffle synchronized(this){ if(shuffle){ setShuffle(false); } else{ setShuffle(true); } } return true; case 2: //Stop Music synchronized(this){ track.switchTracks(); btnPlay.setBackgroundResource(R.drawable.play); } return true; case 3: //Change Source from Assets to SD Card and vice versa synchronized(this){ type++; if(type > 1){ type = 0; } } if(type == 0){ Toast.makeText(getBaseContext(), "Loading Tracks from Assets ", Toast.LEN } else if(type == 1){ Toast.makeText(getBaseContext(), "Loading Tracks from SD Card", Toast.LEN } initialize(type); return true; default: return false; } } //Simply sets shuffle to isShuffle and then displays a message for confirmation private void setShuffle(boolean isShuffle) { shuffle = isShuffle; if(shuffle){ Toast.makeText(getBaseContext(), "Shuffle On", Toast.LENGTH_SHORT).show(); } else{ Toast.makeText(getBaseContext(), "Shuffle Off", Toast.LENGTH_SHORT).show(); } }

78 79 80 81 82 83 Menu, Options, setShuffle


First off, we must override a functon called onCreateOptionsMenu(). It is called whenever the Menu Button is pressed on your phone. It is particularly useful if there are extra controls you want to provide to your users without having to clutter the screen with additional buttons. In our onCreateOptionsMenu() method, we call a createMenu() method. The createMenu() method is responsible for setting populating the menu options. We have 4 menu options in total: "Looping", "Shuffle", "Stop", and "Source" The onOptionsItemSelected() method provides functionality for these menu options. We use a switch on the item.getItemId() function, which returns an int that distinguishes the options from each other. The "Looping" and "Stop" Options seem pretty self explanatory, so I won't go over those. When the "Shuffle" Option is clicked, the setShuffle() method is called, which sets shuffle to to either true of false and displays a message for confirmation. When the "Source" Option is clicked, type is switched from 0 to 1 and vice versa and the initialize() method is called again, except this time with the new type. If the type is set to 1, the SD Card will be loaded up instead of the "assets" folder of your project. Alright, let's move on to the on-screen controls!

MyMediaPlayerActivity.java click, setTrack, playTrack


?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public void click(View view){ int id = view.getId(); switch(id){ case R.id.btnPlay: synchronized(this){ if(isTuning){ isTuning = false; btnPlay.setBackgroundResource(R.drawable.play); track.pause(); } else{ isTuning = true; btnPlay.setBackgroundResource(R.drawable.pause); playTrack(); } } return; case R.id.btnPrevious: setTrack(0);

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

loadTrack(); playTrack(); return; case R.id.btnNext: setTrack(1); loadTrack(); playTrack(); return; default: return; } } private void setTrack(int direction){ if(direction == 0){ currentTrack--; if(currentTrack < 0){ currentTrack = trackNames.size()-1; } } else if(direction == 1){ currentTrack++; if(currentTrack > trackNames.size()-1){ currentTrack = 0; } } if(shuffle){ int temp = random.nextInt(trackNames.size()); while(true){ if(temp != currentTrack){ currentTrack = temp; break; } temp++; if(temp > trackNames.size()-1){ temp = 0; } } } }

//Plays the Track private void playTrack(){ if(isTuning && track != null){ track.play(); Toast.makeText(getBaseContext(), "Playing " + trackNames.get(currentTrack).sub } }

62 63 64 65 click, setTrack, playTrack, setShuffle


If you recall from earlier when we created our main.xml layout, we talked about the android:onClick="click" in each of the buttons. The click function must be public, have a return type of void, and accept a View instance as a parameter; otherwise you will receive errors. Since all of the buttons call the same function, how do we know which button called it? We set up a switch on the view.getId() function, which returns the id of the button. The Next and Previous Buttons simply call the setTrack() method, the loadTrack() method we discussed earlier, and then the playTrack() method, which plays the newly loaded song if the user was playing the last song. The setTrack() method moves the currentTrack index either forward or backward. If shuffle is on, then currentTrack index becomes a random number not equal to what it was before the method call. The playTrack() method simply calls the play() method from Music.java, provided that track is not null.

Run Run As Android Application

Compiling and Running!


Did I forget anything? I think that's about it, if you have been following along and creating your project from scratch, make sure you do not have any pesky red underlines in your code. At this point, we can go ahead and run our program. You can either click Run > Run As > Android Application or right-click your project and Run As > Android Application Well? Do the Buttons work? How about the Menu Options? Try alternating between Loop and Shuffle Mode. Click the Source button to load up your SD Card, but make sure you disconnect your phone from the PC first!

Playing the built in tracks from the "assets" folder of your project

Playing Songs from the SD Card's "music" folder

Congratulations
You now have a working custom media player for your Android Device! The program works decently, but there is more to be desired isn't there? If you wanted to publish this it most likely would not perform well on the Market, as there are much better media players available. To make this media player better, you could add a thread that constantly checks if the onCompletionListener event is satisfied and if so go to the next song. Or you could stream album art from a locally stored database instead of relying on the "pictures" folder of the SD card. Animating visualizations would be nice too wouldn't they? The possibilities are endless! As an Android Developer, these choices are entirely yours, so experiment and try new things! *** If you had any problems with this tutorial, get the source code hereand take a look at it. Play around with the code and understand how it all works together. Good Luck! ***

También podría gustarte