1.
This music player is developed based on Android. It was originally a small project done by me and two other friends when WE were in school. Recently, we had time to sort it out. In my previous article, I have introduced the function realization of the playback interface (Android music player development), but the introduction is rather rough, and I will do more detailed sorting next. Content has been synchronized to Gitee repository, GitHub repository.
The original code was written haphazardly to achieve functionality. There is now a preference for code readability and simplicity, so minor changes will be made to the original program. You may not be able to do it all at once and plan to revise it slowly to improve your understanding.
The server side uses the more traditional servlet and JDBC to transfer data. After finishing sorting, the new version will be changed to the SSM framework, which is more concise and efficient. Android uses basic tools, such as the MediaPlayer class, and currently has no plans to change it.
Server: Android Music player development – server
Login: Android Music player development – Login
Register: Android Music Player development – Register
Change password: Android Music player development – change password
Play interface: Android music player development – play interface
(Suitable for those who make a small lesson in daily life)
2. Interface design
First, design a playing interface for the player
The functions of the playback interface include:
In addition to the function button described above, a button to close the service needs to be added in the subsequent debugging, and it will be placed on this interface for the time being
Use an XML file for interface design, named Activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:padding="16dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:background="@drawable/background"
>
<Button
android:id="@+id/quit_btn"
android:layout_gravity="left"
android:background="@drawable/kaiguan"
android:layout_width="25dp"
android:layout_height="25dp"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
>
<com.loopj.android.image.SmartImageView
android:layout_width="260dp"
android:layout_height="260dp"
android:id="@+id/siv_icon"
android:src="@drawable/default_record_album"
android:layout_centerInParent="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/text_view_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:textSize="26dp"
android:textColor="#FFFFFF"
android:text="Title"
/>
<TextView
android:id="@+id/text_view_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/text_view_name"
android:text="Singer"
android:textColor="#FFFFFF"
android:textSize="20dp" />
</RelativeLayout>
<LinearLayout
android:id="@+id/layout_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="40dp"
android:gravity="center_vertical"
>
<SeekBar
android:layout_width="match_parent"
android:id="@+id/seek_bar"
android:max="100"
style="@style/Widget.AppCompat.SeekBar"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:showDividers="middle"
android:gravity="center">
<Button
android:id="@+id/play_way_btn"
android:layout_width="36dp"
android:background="@drawable/xunhuanbofang"
android:layout_marginRight="16dp"
android:layout_height="36dp" />
<Button
android:id="@+id/play_last_btn"
android:layout_width="40dp"
android:layout_marginRight="16dp"
android:background="@drawable/last"
android:layout_height="40dp" />
<Button
android:id="@+id/play_or_pause_btn"
android:layout_width="55dp"
android:gravity="center"
android:background="@drawable/bofang"
android:layout_height="55dp" />
<Button
android:id="@+id/play_next_btn"
android:layout_width="40dp"
android:layout_marginLeft="16dp"
android:background="@drawable/next"
android:layout_height="40dp" />
<Button
android:id="@+id/play_menu_btn"
android:layout_width="40dp"
android:layout_marginLeft="16dp"
android:background="@drawable/menu"
android:layout_height="40dp" />
</LinearLayout>
</LinearLayout>
Copy the code
All ICONS come from Alibaba icon Vector library
The layout file is relatively simple, so I will not introduce it too much here. The general interface and layout are shown as follows:
3. Realization of player functions
3.1 Two Interfaces
First of all, let’s sort out the overall idea. Because there are many functions here, including switching of background logic and interface, as well as updating of progress bar, two interfaces are set up here to separate the logic layer and the presentation layer. The logical layer methods are playLast(), playOrPause(), playNext(), stopPlay(), set the playback progress seekTo(); Presentation layer methods include onPlayerStateChange() for notification of playback status and onSeekChange() for change of playback progress, which are used to update UI.
PlayerControl.java
public interface PlayerControl {
/* * Play */
void playOrPause(a);
/* Play the previous song */
void play_last(a);
/* Play next track */
void play_next(a);
/* Stop playing */
void stopPlay(a);
/* Set the playback schedule */
void seekTo(int seek);
}
Copy the code
PlayerViewControl.java
public interface PlayerViewControl {
/*
播放状态的通知
*/
void onPlayerStateChange(int state);
/* Change the playback schedule */
void onSeekChange(int seek);
}
Copy the code
The PlayerControl interface is implemented by PlayerPresenter.
With two interfaces, don’t worry about implementing them. Now you can write something that initializes the playback screen.
3.2 Initializing User Information
Before initializing the play interface, you need to initialize the user information, because the initialization of the interface depends on the song ID and the play mode of the user information. The user information comes from the information obtained during login.
private String account; / / account
private int musicId; Id / / song
public int playPattern; // Play mode
// Initialize user information
private void initUserData(a){
Intent intent = getIntent();
String userStr = intent.getStringExtra("result");
JSONObject userData = RequestServlet.getJSON(userStr);
account = userData.optString("account");
musicId = userData.optInt("music_id");
playPattern = userData.optInt("pattern");
}
Copy the code
3.3 Initializing the Playback interface
private SeekBar mSeekBar; / / the progress bar
private Button mPlayOrPause;
private Button mPlayPattern;
private Button mPlayLast;
private Button mPlayNext;
private Button mPlayMenu;
private Button mQuit;
private TextView mMusicName;
private TextView mMusicArtist;
private SmartImageView mMusicPic;
public final int PLAY_IN_ORDER = 0; // Play in sequence
public final int PLAY_RANDOM = 1; // Random play
public final int PLAY_SINGLE = 2; // Single loop
// Initialize the interface
private void initView(a){
mSeekBar = (SeekBar) this.findViewById(R.id.seek_bar);
mPlayOrPause = (Button) this.findViewById(R.id.play_or_pause_btn);
mPlayPattern = (Button) this.findViewById(R.id.play_way_btn);
mPlayLast= (Button) this.findViewById(R.id.play_last_btn);
mPlayNext = (Button) this.findViewById(R.id.play_next_btn);
mPlayMenu = (Button) this.findViewById(R.id.play_menu_btn);
mQuit=(Button) this.findViewById(R.id.quit_btn);
mMusicName = (TextView) this.findViewById(R.id.text_view_name);
mMusicArtist = (TextView) this.findViewById(R.id.text_view_artist);
mMusicPic = (SmartImageView) this.findViewById(R.id.siv_icon);
// Mode conversion
if (playPattern==PLAY_IN_ORDER) {
mPlayPattern.setBackgroundResource(R.drawable.xunhuanbofang);
}else if(playPattern==PLAY_RANDOM){
mPlayPattern.setBackgroundResource(R.drawable.suijibofang);
} else if (playPattern==PLAY_SINGLE) {
mPlayPattern.setBackgroundResource(R.drawable.danquxunhuan);
}
// Get the music list
getMusicListThread();
}
Copy the code
There are three aspects to initialize the playback interface:
- Bind all controls in the XML file
- The icon of the play mode button in the interface is changed according to the user’s play mode resolved in the database. The song information cannot be loaded here, because the song resource has not been obtained
- Get the music list from the database and initialize the song information into the interface based on the musicId resolved in the user information, using a child thread getMusicListThread
getMusicListThread
To get the playlist, you need to get data from the server and start a child thread. This calls the getMusicList method in the RequestServlet class (you can create this method in the RequestServlet class and implement it later).
public static JSONArray sMusicList; // List of songs
public int songNum = 0; // Total number of songs
// Get the music list
private void getMusicListThread(a){
new Thread(){
@Override
public void run(a) {
try{
JSONArray result = RequestServlet.getMusicList();
Message msg = new Message();
msg.what = 2;
msg.obj = result;
handler2.sendMessage(msg);
}
catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
private Handler handler2 = new Handler(){
public void handleMessage(android.os.Message msg) {
try {
if (msg.what == 2) {
sMusicList = (JSONArray) msg.obj;
songNum = sMusicList.length();
// Initializes the song interface based on user data and song listsetMusicView(IsPlay.notPlay); }}catch(Exception e) { e.printStackTrace(); }}};Copy the code
The child thread gets the music list on the server side and passes it to the main thread, which calls the **setMusicView()** method to initialize the song-related interface
setMusicView
Before we initialize the song information, we have the user information and the song list, now we can get the individual song information from the song list based on the musicId that is resolved in the user information, and then initialize that song information into the interface.
Before I formally introduce the setMusicView() method, you can see that a parameter is passed before the method is called, using an enumeration type to distinguish whether it needs to be played. When we initialize the interface, we don’t need to play the song, but when we change the song information, we also need to play the song.
public enum IsPlay{
play, notPlay
}
Copy the code
public String playAddress; // Address of the music file
public static final String IMG = "http://10.0.2.2:8080/musicplayer/image/"; // The generic address of the music image
// Set up the interface about the song
public void setMusicView(IsPlay playState){
try {
JSONObject musicInfo = (JSONObject) sMusicList.get(musicId);
String name = musicInfo.optString("name");
String author = musicInfo.optString("author");
String img = musicInfo.optString("img");
playAddress=musicInfo.optString("address");
mMusicPic.setImageUrl(IMG+img,R.mipmap.ic_launcher,R.mipmap.ic_launcher); // Set the song cover on the interface
mMusicName.setText(name); // Set the name of the song on the interface
mMusicArtist.setText(author); // Set the singer on the interface
} catch (Exception e) {
e.printStackTrace();
}
if(playState == IsPlay.play){
if( mPlayerControl ! =null) { mPlayerControl.stopPlay(); } mPlayerControl.playOrPause(playState); }}Copy the code
Call playOrPause(); / / playOrPause(); / / playOrPause(); / / playOrPause(); / / playOrPause(); / / playOrPause(); / / playOrPause(); / / playOrPause(); Why stop playing in the first place? Because of a feature of MediaPlayer, if you need to switch songs, you must first release the MediaPlayer resource and then instantiate an object to load the new resource.
3.4 Initialization Events
Click events involving several buttons
private PlayerControl playerControl = new PlayerPresenter(this);
// Initialize the event
private void initEvent(a){
// Play/pause buttons
mPlayOrPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mPlayerControl! =null){ mPlayerControl.playOrPause(IsPlay.notPlay); }}});// Play the previous song
mPlayLast.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mPlayerControl! =null){ mPlayerControl.playLast(); }}});// Play the next track
mPlayNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mPlayerControl! =null){ mPlayerControl.playNext(); }}});// Play mode
mPlayPattern.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
playPattern = (playPattern+1) %3;
if (playPattern==PLAY_IN_ORDER) {
mPlayPattern.setBackgroundResource(R.drawable.xunhuanbofang);
}else if(playPattern==PLAY_RANDOM){
mPlayPattern.setBackgroundResource(R.drawable.suijibofang);
} else if(playPattern==PLAY_SINGLE) { mPlayPattern.setBackgroundResource(R.drawable.danquxunhuan); }}});// Music list
mPlayMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,MusicListActivity.class); startActivity(intent); }});// Exit button
mQuit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this."Saving information...", Toast.LENGTH_SHORT).show(); saveDataToDB(); }});/ / the progress bar
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// The progress bar has changed
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// The hand is already touched
isUserTouchProgressBar=true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int touchProgress=seekBar.getProgress();
// Stop dragging
if( mPlayerControl ! =null) {
mPlayerControl.seekTo(touchProgress);
}
isUserTouchProgressBar=false; }}); }Copy the code
PlayerControl is the interface of the logical layer, and PlayerPresenter is the implementation class of the interface function. Here, mainActivity is passed as a parameter, which is convenient to call some parameters and methods of MainActivity.
The click of play/pause, play previous, play next button is handled directly by the PlayerControl interface.
The play mode button is clicked, and the play mode is switched according to the sequence of play, random play and single cycle. A complementary operation is made here, so that the value of playPattern is always kept between 0, 1 and 2. Change the play mode button icon according to the playPattern value.
public final int PLAY_IN_ORDER = 0; // Play in sequence
public final int PLAY_RANDOM = 1; // Random play
public final int PLAY_SINGLE = 2; // Single loop
mPlayPattern.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
playPattern = (playPattern+1) %3;
if (playPattern==PLAY_IN_ORDER) {
mPlayPattern.setBackgroundResource(R.drawable.xunhuanbofang);
}else if(playPattern==PLAY_RANDOM){
mPlayPattern.setBackgroundResource(R.drawable.suijibofang);
} else if(playPattern==PLAY_SINGLE) { mPlayPattern.setBackgroundResource(R.drawable.danquxunhuan); }}});Copy the code
When the exit button is clicked, the current user information (play mode, played song ID) needs to be saved to the database, so the child thread implementation is enabled.
mQuit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this."Saving information...", Toast.LENGTH_SHORT).show(); saveDataToDB(); }});Copy the code
Drag the progress bar event listeners need to implement SeekBar. OnSeekBarChangeListener interface, call the SeekBar setOnSeekBarChangeListener the object passed in the event monitoring event listeners. There are three important methods in the interface:
- OnProgressChanged, used when the progress bar changes;
- OnStartTrackingTouch, used when the progress bar starts dragging;
- OnStopTrackingTouch, used when the progress bar stops being dragged.
The third method is used here, which calls the seekTo method of the playerControl interface when you stop dragging the progress bar.
private boolean isUserTouchProgressBar = false; // Check whether the hand touches the progress bar
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// The progress bar has changed
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// The hand is already touched
isUserTouchProgressBar=true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int touchProgress=seekBar.getProgress();
// Stop dragging
if( playerControl ! =null) {
playerControl.seekTo(touchProgress);
}
isUserTouchProgressBar=false; }});Copy the code
saveDataToDB
Dig a lot of holes in front, a lot of procedures are shelved first, there are two specific interface implementation, access to song information, save user information and so on.
SaveDataToDB is the method used to save user information to the database when the exit button is clicked.
private void saveDataToDB(a){
new Thread() {
public void run (a) {
try {
JSONObject result = RequestServlet.savePlayerInformation(account, musicId, playPattern);
Message msg = new Message();
msg.what = 1;
msg.obj = result;
handler1.sendMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
Copy the code
This method calls the savePlayerInformation method in the RequestServlet class to save the song ID and playback mode
Handler handler1 = new Handler(){
public void handleMessage(android.os.Message msg) {
try {
if (msg.what == 1) {
JSONObject result = (JSONObject) msg.obj;
MainActivity.this.finish();
Toast.makeText(MainActivity.this."Withdrawn", Toast.LENGTH_SHORT).show(); }}catch(Exception e) { e.printStackTrace(); }}};Copy the code
RequestServlet.savePlayerInformation()
The savePlayerInformation method is similar to the other methods in the class (since there are so many duplicate parts, you can write a separate method for the duplicate parts).
private static final String SAVE_USER_INFO ="http://192.168.43.xxx:8080/musicplayer/SaveMusic";
public static JSONObject savePlayerInformation(String account,int musicId,int playPattern){
JSONObject result = null;
String path = SAVE_USER_INFO+"? account="+account+"&musicId="+musicId+"&pattern="+playPattern;
HttpURLConnection conn;
try {
conn = getConn(path);
int code = conn.getResponseCode(); // HTTP status, 200 represents corresponding success
if (code == 200){ InputStream stream = conn.getInputStream(); String str = streamToString(stream); result = getJSON(str); conn.disconnect(); }}catch (Exception e){
e.printStackTrace();
}
return result;
}
Copy the code
RequestServlet.getMusicList()
You don’t need to pass parameter information to the server to get the playlist. You can simply call the corresponding servlet.
private static final String GET_MUSIC_LIST = "http://192.168.43.xxx:8080/musicplayer/GetMusicList";
// Get the list of songs
public static JSONArray getMusicList(a){
JSONArray result = null;
String path = GET_MUSIC_LIST;
HttpURLConnection conn;
try {
conn = getConn(path);
int code = conn.getResponseCode();
if (code == 200){
InputStream jsonArray = conn.getInputStream();
String str = streamToString(jsonArray);
result = getJsonArray(str);
conn.disconnect();
}else {
return null; }}catch (Exception e){
e.printStackTrace();
}
return result;
}
Copy the code
3.5 PlayerControl interface implementation
The PlayerControl interface is implemented by PlayerPresenter
The PlayerControl interface handles the logic layer, covering things like the player’s music controls.
Android has a lot of processing multimedia API, MediaPlayer is a very basic one, here with the help of MediaPlayer tool to achieve music playback function.
private MediaPlayer mMediaPlayer=null;
Copy the code
- Define global variables and constants
private MediaPlayer mMediaPlayer = null;
private static final String ADDRESS = "http://192.168.43.xxx:8080/musicplayer/music/";
private PlayerViewControl mViewController = null; / / the presentation layer
private MainActivity mMainActivity = null;
// Play status
public final int PLAY_STATE_PLAY=1; / / in the sow
public final int PLAY_STATE_PAUSE=2; / / pause
public final int PLAY_STATE_STOP=3; / / not sow
public int mCurrentState = PLAY_STATE_STOP; // The default state is to stop playback
private Timer mTimer;
private SeekTimeTask mTimeTask;
// Take the MainActivity
public PlayerPresenter(MainActivity activity){
mMainActivity = activity;
}
Copy the code
- Play/Pause
@Override
public void playOrPause(MainActivity.IsPlay playState) {
if(mViewController == null) {this.mViewController = mMainActivity.mPlayerViewControl;
}
if (mCurrentState == PLAY_STATE_STOP || playState == MainActivity.IsPlay.play) {
try {
mMediaPlayer = new MediaPlayer();
// Specify the playback path
mMediaPlayer.setDataSource(ADDRESS + mMainActivity.playAddress);
// Get ready to play
mMediaPlayer.prepareAsync();
/ / play
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) { mMediaPlayer.start(); }}); mCurrentState = PLAY_STATE_PLAY; startTimer(); }catch(IOException e) { e.printStackTrace(); }}else if (mCurrentState == PLAY_STATE_PLAY) {
// If the current state is play, then pause
if(mMediaPlayer ! =null) { mMediaPlayer.pause(); mCurrentState = PLAY_STATE_PAUSE; stopTimer(); }}else if (mCurrentState == PLAY_STATE_PAUSE) {
// If the current state is paused, continue playing
if(mMediaPlayer ! =null) {
mMediaPlayer.start();
mCurrentState = PLAY_STATE_PLAY;
startTimer();
}
}
mViewController.onPlayerStateChange(mCurrentState);
}
Copy the code
Play or pause involves interface changes, so you need to bind the presentation layer and the implementation of the presentation layer interface is written inside the MainActivity, which is called directly here.
if(mViewController == null) {this.mViewController = mMainActivity.mPlayerViewControl;
}
Copy the code
If the play status is Stop or the parameter passed is Play, the system starts to play.
- Instantiate MediaPlayer, load the song resources, and prepare to play
- Call **start()** to play
- Example Change the playing state to Playing
- Start the time
if (mCurrentState == PLAY_STATE_STOP || playState == MainActivity.IsPlay.play) {
try {
mMediaPlayer = new MediaPlayer();
// Specify the playback path
mMediaPlayer.setDataSource(ADDRESS + mMainActivity.playAddress);
// Get ready to play
mMediaPlayer.prepareAsync();
/ / play
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) { mMediaPlayer.start(); }}); mCurrentState = PLAY_STATE_PLAY; startTimer(); }catch(IOException e) { e.printStackTrace(); }}Copy the code
If the current playback state is playing, call pause() to pause the playback, change the playback state to pause, and stop the timing
else if (mCurrentState == PLAY_STATE_PLAY) {
// If the current state is play, then pause
if(mMediaPlayer ! =null) { mMediaPlayer.pause(); mCurrentState = PLAY_STATE_PAUSE; stopTimer(); }}Copy the code
If the current play state is play pause, this method is called to continue playing
else if (mCurrentState == PLAY_STATE_PAUSE) {
// If the current state is paused, continue playing
if(mMediaPlayer ! =null) { mMediaPlayer.start(); mCurrentState = PLAY_STATE_PLAY; startTimer(); }}Copy the code
Of course, each time this method is called, the interface needs to make a change based on the playback state (see 3.6).
mViewController.onPlayerStateChange(mCurrentState);
Copy the code
- timing
In the play/pause switch above, the timing function is used, which is mainly to update the progress bar according to the playback time
private void startTimer(a) {
if (mTimer == null) {
mTimer=new Timer();
}
if (mTimeTask == null) {
mTimeTask = new SeekTimeTask();
}
mTimer.schedule(mTimeTask,0.500);
}
private void stopTimer(a) {
if(mTimeTask ! =null) {
mTimeTask.cancel();
mTimeTask=null;
}
if(mTimer ! =null) {
mTimer.cancel();
mTimer=null; }}Copy the code
Timer is a normal class, while TimerTask is an abstract class. TimerTask has an abstract method run() that can be called every once in a while to implement interface changes.
The schedule method in the Timer class takes three parameters. The first parameter is the TimerTask object, the second parameter indicates how long it will take to execute, and the third parameter indicates the interval in milliseconds (ms), which I set to 500 milliseconds (slightly longer). This calls the run method every 500 milliseconds after the timer is started.
The run method, on the other hand, calculates a percentage based on the current playing time and the total song length, and submits it to the performance layer to update the progress bar. (See 3.6)
private class SeekTimeTask extends TimerTask {
@Override
public void run(a) {
// Get the current playback progress
if(mMediaPlayer ! =null&& mViewController! =null) {
int currentPosition = mMediaPlayer.getCurrentPosition();
// Record percentages
int curPosition=(int)(currentPosition*1.0 f/mMediaPlayer.getDuration()*100);
if(curPosition<=100) { mViewController.onSeekChange(curPosition); }}}}Copy the code
- Play the last song
To switch songs, you need to determine the user’s current playing mode and switch songs according to the different playing modes (sequence, random, single).
@Override
public void playLast(a) {
// Play in sequence
if (mMainActivity.playPattern == mMainActivity.PLAY_IN_ORDER) {
if (mMainActivity.musicId == 0) {
mMainActivity.musicId = mMainActivity.songNum-1;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
} else {
mMainActivity.musicId = mMainActivity.musicId - 1; mMainActivity.setMusicView(MainActivity.IsPlay.play); }}// Random play
else if (mMainActivity.playPattern == mMainActivity.PLAY_RANDOM) {
mMainActivity.musicId = ( mMainActivity.musicId+(int) (1+Math.random()*(20-1+1))) % mMainActivity.songNum ;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
}
// Single loop
else if(mMainActivity.musicId==mMainActivity.PLAY_SINGLE){ mMainActivity.setMusicView(MainActivity.IsPlay.play); }}Copy the code
If the current ID is 0, then the previous ID should be the total number of songs -1 (id starts from 0 and the last song id is the total number of songs -1). If the current song ID is not 0, the current song ID is directly subtracted by 1. And then I’m gonna call setMusicView and pass it play
// Play in sequence
if (mMainActivity.playPattern == mMainActivity.PLAY_IN_ORDER) {
if (mMainActivity.musicId == 0) {
mMainActivity.musicId = mMainActivity.songNum-1;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
} else {
mMainActivity.musicId = mMainActivity.musicId - 1; mMainActivity.setMusicView(MainActivity.IsPlay.play); }}Copy the code
For random play, use the current song ID + a reasonable random integer as the switched song ID
// Random play
else if (mMainActivity.playPattern == mMainActivity.PLAY_RANDOM) {
mMainActivity.musicId = ( mMainActivity.musicId+(int) (1+Math.random()*(20-1+1))) % mMainActivity.songNum ;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
}
Copy the code
If it is a single loop, the song will be replayed
// Single loop
else if(mMainActivity.musicId==mMainActivity.PLAY_SINGLE){
mMainActivity.setMusicView(MainActivity.IsPlay.play);
}
Copy the code
- Play the next song
Similar to the logic of the previous one, I will not repeat it here
@Override
public void playNext(a) {
// Play in sequence
if (mMainActivity.playPattern == mMainActivity.PLAY_IN_ORDER) {
mMainActivity.musicId = (mMainActivity.musicId + 1) % mMainActivity.songNum;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
}
// Random play
else if (mMainActivity.playPattern == mMainActivity.PLAY_RANDOM) {
mMainActivity.musicId = (mMainActivity.musicId+(int) (1+Math.random()*(20-1+1))) % mMainActivity.songNum ;
mMainActivity.setMusicView(MainActivity.IsPlay.play);
}
// Single loop
else if(mMainActivity.playPattern == mMainActivity.PLAY_SINGLE){ mMainActivity.setMusicView(MainActivity.IsPlay.play); }}Copy the code
- Stop playing
This method is called in the setMusicView method. Before switching songs, you need to release the MediaPlayer, otherwise the program will crash or play several songs at once
@Override
public void stopPlay(a) {
if(mMediaPlayer ! =null ) {
mMediaPlayer.stop();
mCurrentState= PLAY_STATE_STOP;
stopTimer();
// Update the playback status
if(mViewController ! =null) {
mViewController.onPlayerStateChange(mCurrentState);
}
mMediaPlayer.release();// Release resources
mMediaPlayer=null; }}Copy the code
- seekTo
This is called after dragging the progress bar. When you stop dragging the progress bar, you pass in a parameter that represents a percentage and jump directly to the calculated music duration using the seekTo method in the MediaPlayer class
@Override
public void seekTo(int seek) {
/ / between 0 ~ 100
// A conversion is required to obtain seek as a percentage
if(mMediaPlayer ! =null) {
//getDuration() Gets the audio duration
int tarSeek=(int)(seek*1f/100*mMediaPlayer.getDuration()); mMediaPlayer.seekTo(tarSeek); }}Copy the code
3.6 PlayerViewControl interface implementation
Considering the need to use mainActivity bound controls, so directly into the MainActivity to implement the PlayerViewControl interface
public PlayerViewControl mPlayerViewControl = new PlayerViewControl() {
@Override
public void onPlayerStateChange(int state) {
// Modify the UI based on the playback state
switch (state) {
case PLAY_STATE_PLAY:
// During playback, we need to modify the button display to pause
mPlayOrPause.setBackgroundResource(R.drawable.bofangb);
break;
case PLAY_STATE_PAUSE:
case PLAY_STATE_STOP:
mPlayOrPause.setBackgroundResource(R.drawable.bofang);
break; }}@Override
public void onSeekChange(final int seek) {
// To change the playback progress, there is one condition: when the user's hand touches the progress bar, it will not be updated.
runOnUiThread(new Runnable() {
@Override
public void run(a) {
if(! isUserTouchProgressBar) { mSeekBar.setProgress(seek);if(seek==100) { mPlayerControl.playNext(); }}}}); }};Copy the code
- It’s easy to understand how ICONS can be replaced based on the playback state
@Override
public void onPlayerStateChange(int state) {
// Modify the UI based on the playback state
switch (state) {
case PLAY_STATE_PLAY:
// During playback, we need to modify the button display to pause
mPlayOrPause.setBackgroundResource(R.drawable.bofangb);
break;
case PLAY_STATE_PAUSE:
case PLAY_STATE_STOP:
mPlayOrPause.setBackgroundResource(R.drawable.bofang);
break; }}Copy the code
- Progress bar Update
This method is called within the timing function and is now defined to update the progress bar every 500ms. A child thread is used here. In addition, there is a limitation that the progress bar is no longer automatically updated when the user presses it.
@Override
public void onSeekChange(final int seek) {
// To change the playback progress, there is one condition: when the user's hand touches the progress bar, it will not be updated.
runOnUiThread(new Runnable() {
@Override
public void run(a) {
if(! isUserTouchProgressBar) { mSeekBar.setProgress(seek);if(seek==100) { mPlayerControl.playNext(); }}}}); }Copy the code
4. Test
Now the basic functions have been realized, and will continue to improve the function in the future.
Test environment: Android 10, LAN
Preparations: Enable Tomcat and USB debugging
4.1 Adding Database Data
Since there is no song data in the database, now add a few songs
INSERT INTO `music`(name, author, address, img, create_time)
VALUES ('Light years away'.G.E.M..'guangnian.mp3'.'guangnian.jpg', now()),
('goodbye'.G.E.M..'zaijian.mp3'.'zaijian.jpg', now()),
('A Song of Love'.'half Yang'.'yiqu.mp3'.'yiqu.jpg', now()),
('half'.'grain of Chen'.'xiaoban.mp3'.'xiaoban.jpg', now()),
('the fragrance'.'Jay Chou'.'daoxiang.mp3'.'daoxiang.jpg', now()),
('Take what you want.'.'Hu Yanbin'.'niyao.mp3'.'niyao.jpg', now()),
('Thief will do'.'Flower congee, Ma Yuyang'.'dao.mp3'.'dao.jpg', now()),
('Strongest'.'Alan worker'.'Strongest.mp3'.'Strongest.jpg', now());
Copy the code
4.2 Adding a Resource File
Create two new folders in the webApp directory on the server side, one folder for image files and the other folder for song files, so that the Android side can get these resources. (Make sure the name matches the information in the database)
4.3 the login
Now use the account “Cun” to log in. If you look at the database, you can see that the song ID is 1 and the play mode is 0.
For easy reference, the song information is shown below
Enter your account and password and click the login button. You can see from the following figure that login is successful! The initialized song information and playback mode are correct.
4.4 Play/Pause
Normal playback function
Pause function is normal
Test drag progress bar also works
4.5 Previous track/next track
Previous track/next track functions normally
The playback state is random, the function of the previous song/the next song is normal (but there is a high probability of repeating a song at present, and it will be improved later)
4.6 quit
Click the exit button, and the user information is saved to the server normally
5. Subsequent
To improve some features, the function of opening the music list on the far right of the play screen is not implemented yet
6. Improve
6.1 quit
In the original program, in the state of play, return to the main interface, and then open the software needs to log in again.
Now listen for the user’s return and treat the home screen as an Activity, exit and restart the software without restarting it
//MainActivity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
return true;
}
return super.onKeyDown(keyCode, event);
}
Copy the code
7.
Lately I’ve noticed that some people don’t read the beginning.
Procedures have been put into two warehouses, feel helpful to you, please give this text a thumbs-up! Give the warehouse a star! Thanks a million!
Gitee warehouse
Making the warehouse