Android小卷毛音乐播放器开发教程(一)
前言:2014年12月5号的时候,刚加入学校里的团队不久,组长会给新人发布任务,让我们在做项目的过程中能快速的熟悉android的各项基本知识。第一次的任务就是做一个android的音乐播放器。当时拿到任务的时候也是愣了≡ ̄﹏ ̄≡,什么都不知道,怎么做。然而身为一个程序员,速效的学习并且能灵活的使用,是一项必备的技能,面对问题,咱不能怂,怂就输一半儿,二话不说就是干。大二的小达课还是比较多的,抽出零碎的时间,还有牺牲周末等等,花了18天的时间,憋出了个播放器(辣鸡播放器,大神勿喷,初次接触android)。期间很感谢简美音乐播放器(作者:小巫)的许多易懂的教程,还有网上各路大神的代码,下面就展示下小达的成果。(小心被亮瞎,界面丑暴,23333)。
第一阶段完成的功能:
1.获取歌曲并显示为歌曲列表
2.点击列表自动播放
3.暂停、上一首、下一首
4.播放模式的选择
5.自动切换到下一首
第二阶段完成的功能:
1.歌词的动态显示(没有滚起来。。。)
2.我的最爱歌曲加入和删除
3.最近播放的歌曲记录
待完成的任务:
1.专辑图片的获取(搞了好长时间没弄出来)
2.来电的处理
3.歌曲分享等模块
4.创建自己的歌单
废话不多说,直接上源码。小卷毛音乐播放器下载
各位飘过的、路过的大神,代码写的有点乱,求轻喷,23333,刚接触android,求指点,我会好好的改正,谢谢咯。
小卷毛播放器的全貌,,,自己都不忍直视,这界面还是后来做到一半了改过的,没有设计师的苦逼程序猿,之前的那个UI丑的不能要。做完这个东西最大的体会就是,UI设计还是超重要的,改起来有点麻烦(好像是个人太水了哈。。。)。有人会问,,我这素材到哪里找的?木有设计师,只能自己默默的反编译天天动听了,,偷别人设计师的,23333,会在后面一篇双手奉上我的教程。
简单说一下,主界面能用的模块有我的音乐、我的最爱、最近播放。整个播放器我先用的是好多个Activity,但是做到中间发现Activity不怎么灵活,在学习的过程中发现有个东西叫Fragment(不要翻译成碎片,不然怪怪的,23333)。这个Fragment更加灵活方便。后来花了2天的时间边学边改,真是痛苦。将整个播放器只用了一个Activity,其余的全是fragment。
先贴出activity的源代码吧。
package com.example.dada.myapplication;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Message;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import android.os.Handler;
public class MainActivity extends Activity implements AppConstant, //用Fragment的时候,Activity实现Fragment的内部接口
MainFragment.OnMainFragmentInteractionListener,
MyMusicFragment.OnMyMusicFragmentInteractionListener,
PlayFragment.OnPlayFragmentInteractionListener,
RecentlyPlayFragment.OnFragmentInteractionListener,
MyFavoriteFragment.OnFragmentInteractionListener{
private String music_url; //记录音乐的路径
private boolean isPause; //记录当前播放器的是否暂停
private boolean isChangToNext; //下一首按钮
private boolean notification_previous_music; //通知栏上一首
private boolean notification_next_music; //通知栏下一首
private boolean notification_pause_music; //通知栏停止播放
private boolean notification_exit; //通知栏退出
private int index; //歌词的索引
private int play_mode; //播放模式
private int current_position; //当前进度条的位置
private int current_position_bar;
private long exitTime; //记录连续按两次退出
private MsgReceiver msgReceiver; //Message的接收器,接收歌曲的信息
private BarReceiver barReceiver; //进度条的接收器
private NotificationReceiver notificationReceiver; //通知栏广播接收器
private Button latestPlayListButton; //最近播放
private ImageButton play_button; //播放按钮控件
private ImageButton previous_song_button; //上一首按钮
private ImageButton next_song_button; //下一首按钮控件
private ImageButton play_mode_button; //播放模式按钮
private ListView music_list; //音乐列表控件
private ListView recently_play_music_list; //最近播放音乐列表
private SeekBar seek_bar; //进度条控件
private Intent intent_to_service; //向service发送广播的intent
private Intent intent_to_fragment; //向fragment发送广播的intent
private Intent intent_to_changeMusic; //换歌曲
private Intent progress_change_intent_to_service; //进度条拖动
private TextView music_info_textView; //显示歌曲信息的textview
private TextView singer_info_textView; //显示歌手信息的textview
private FindSongs finder; //查找歌曲的类的实例
private PopupWindow popupPlayModeWindow; //播放模式下拉窗口
private MainFragment mainFragment; //主要的fragment
private NotificationManager myNotificationManager; //通知栏Manager
private Notification myNotification; //通知栏
private FragmentManager fragmentManager; //用于管理Fragment的相互切换
private FragmentTransaction fragmentTransaction;
/*FragmentManage:
FragmentManager能够实现管理activity中fragment. 通过调用activity的getFragmentManager()取得它的实例.
FragmentTransaction:
FragmentTransaction对fragment进行添加,移除,替换,以及执行其他动作。
从 FragmentManager 获得一个FragmentTransaction的实例 :
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
*/
private MyMusicFragment myMusicFragment; //我的音乐Fragment实例
public static boolean isFavorite = false; //当前音乐是否为我的最爱
public static String[] music_array = null; //自动补全的数组
public static List<Mp3Info> mp3Infos; //歌曲列表
public static int music_position; //音乐的位置
public static MyDataBase myDataBase; //自己封装的一个数据库类实例
@Override
public void onMainFragmentInteraction(int message) { //MainFragment的回调函数
switch(message){
case PlayerMsg.CHANGE_TO_MY_MUSIC_FRAGMENT:
onMyMusicFragmentInteraction(PlayerMsg.CHANGE_TO_MY_MUSIC_FRAGMENT);
break;
case PlayerMsg.FRAGMENT_RANDOM_PLAY:
changeMusic(PlayerMsg.RANDOM_MODE,AppConstant.PlayerMsg.NEXT_MUSIC,mp3Infos);
Toast.makeText(this,"小卷毛为您点了一首歌",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onMyMusicFragmentInteraction(int message,int position) { //MyMusicFragment的回调函数
if(message == PlayerMsg.LIST_CLICK){
if (mp3Infos != null) {
isPause = false;
initService(position);
}
}
}
public void onMyMusicFragmentInteraction(int message) { //MyMusicFragment回调函数
myMusicFragment = new MyMusicFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.animator.slide_in_left,R.animator.slide_out_right);
switch(message){
case PlayerMsg.CHANGE_TO_MY_MUSIC_FRAGMENT:
fragmentTransaction.replace(R.id.fragment_layout, myMusicFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
break;
case PlayerMsg.BACK_TO_MAIN_FRAGMENT:
fragmentTransaction.replace(R.id.fragment_layout, mainFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
break;
}
}
public void onPlayFragmentInteraction(int message){ //PlayFragment的回调函数
Mp3Info mp3_Info = mp3Infos.get(music_position);
switch (message){
case PlayerMsg.DISMISS_CLICK:
myMusicFragment = new MyMusicFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.animator.slide_in_up,R.animator.slide_out_down);
fragmentTransaction.replace(R.id.fragment_layout, mainFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
break;
case PlayerMsg.ADD_TO_FAVORITE:
isFavorite = true;
mp3_Info.setFavorite(isFavorite);
myDataBase.AddData(
MyDataBase.TABLE_NAME_FAVORITE,
(int)mp3_Info.getId(),
mp3_Info.getTitle(),
mp3_Info.getArtist(),
mp3_Info.getDuration(),
mp3_Info.getUrl(),
(int)mp3_Info.getAlbum_id()
);
break;
case PlayerMsg.DELETE_FROM_FAVORITE:
isFavorite = false;
mp3Infos.get(music_position).setFavorite(isFavorite);
myDataBase.DeleteData(
MyDataBase.TABLE_NAME_FAVORITE,
(int)mp3_Info.getId());
break;
}
}
public void onFragmentInteraction(){}
public void onFragmentInteraction(int message,int position){
// if(message == PlayerMsg.LIST_CLICK){
// if (mp3Infos != null) {
// isPause = false;
// initService(position);
// }
// }
}
public void onFavoriteFragmentInteraction(){
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
mainFragment = new MainFragment();
fragmentManager = getFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_layout, mainFragment).commit();
index = 0;
exitTime = 0;
music_position = 0;
current_position = 0;
play_mode = AppConstant.PlayerMsg.LOOP_MODE;
isPause = true;
finder = new FindSongs();
msgReceiver = new MsgReceiver();
barReceiver = new BarReceiver();
notificationReceiver = new NotificationReceiver();
View play_mode_window = this.getLayoutInflater().inflate(R.layout.popup_window_layout,null);
popupPlayModeWindow = new PopupWindow(play_mode_window,280,360);
music_list = (ListView)findViewById(R.id.music_list);
recently_play_music_list = (ListView)findViewById(R.id.recently_play_music_list);
play_button = (ImageButton)findViewById(R.id.play_button);
seek_bar = (SeekBar)findViewById(R.id.process_bar);
play_mode_button = (ImageButton)findViewById(R.id.play_mode_button);
play_mode_button.setImageResource(R.drawable.play_mode_photo);
next_song_button = (ImageButton)findViewById(R.id.next_song_button);
latestPlayListButton = (Button)findViewById(R.id.latestPlayListButton);
previous_song_button = (ImageButton)findViewById(R.id.previous_song_button);
intent_to_service = new Intent("com.example.communication.PLAY");
intent_to_fragment = new Intent("com.example.communication.MUSIC_LIST_SELECTOR");
intent_to_changeMusic = new Intent("com.example.communication.ChANGE_MUSIC");
progress_change_intent_to_service = new Intent("com.example.communication.PROGRESS_BAR");
myDataBase = new MyDataBase(getApplicationContext(),recently_play_music_list); //实例化数据库类
myDataBase.CreateDataBase();
play_button.setImageResource(R.drawable.play_photo);
play_button.setOnClickListener(new View.OnClickListener() { //播放按钮的监听器
@Override
public void onClick(View v) {
if(isPause){
isPause = false;
play_button.setImageResource(R.drawable.pause_photo);
}
else{
isPause = true;
play_button.setImageResource(R.drawable.play_photo);
}
intent_to_service.putExtra("position",current_position);
intent_to_service.putExtra("isPause",isPause);
sendBroadcast(intent_to_service);
}
});
mp3Infos = finder.getMp3Infos(getContentResolver());
next_song_button.setOnClickListener(new View.OnClickListener() { //下一首按钮的监听器
@Override
public void onClick(View v) {
changeMusic(play_mode,AppConstant.PlayerMsg.NEXT_MUSIC,mp3Infos);
}
});
previous_song_button.setOnClickListener(new View.OnClickListener() { //上一首按钮监听
@Override
public void onClick(View v) {
changeMusic(play_mode,AppConstant.PlayerMsg.PREVIOUS_MUSIC,mp3Infos);
}
});
seek_bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { //进度条拖动响应
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
current_position_bar = seekBar.getProgress();
progress_change_intent_to_service.putExtra("current_position",current_position_bar);
sendBroadcast(progress_change_intent_to_service);
}
});
play_mode_button.setOnClickListener(new View.OnClickListener() { //播放模式按钮监听器
@Override
public void onClick(View v) {
if(popupPlayModeWindow.isShowing()){
popupPlayModeWindow.dismiss();
}
else{
if(play_mode == AppConstant.PlayerMsg.LOOP_MODE)
Toast.makeText(getApplicationContext(), "当前模式为循环播放模式", Toast.LENGTH_SHORT).show();
if(play_mode == AppConstant.PlayerMsg.RANDOM_MODE)
Toast.makeText(getApplicationContext(),"当前模式为随机播放模式",Toast.LENGTH_SHORT).show();
popupPlayModeWindow.showAsDropDown(v);
}
}
});
music_array = getMusicList();
}
@Override
protected void onStart() {
IntentFilter intentMsgFilter = new IntentFilter();
IntentFilter intentBarFilter = new IntentFilter();
IntentFilter intentNotificationFilter = new IntentFilter();
intentMsgFilter.addAction("com.example.communication.RECEIVER"); //注册歌曲信息的广播接收器
registerReceiver(msgReceiver,intentMsgFilter);
intentBarFilter.addAction("com.example.communication.BAR"); //注册进度条的广播接收器
registerReceiver(barReceiver,intentBarFilter);
intentNotificationFilter.addAction("com.example.communication.NOTIFICATION_TO_ACTIVITY");
registerReceiver(notificationReceiver,intentNotificationFilter);
super.onStart();
}
/*为了防止按返回键杀掉Activity,对返回键的按钮响应做了重写,
* 连续按两次,检测间隔时间,达到时间要求直接返回桌面,
* 和按home键效果类似。
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
if((System.currentTimeMillis()-exitTime) > 2000){
Toast.makeText(this, "再按一次返回桌面", Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addCategory(Intent.CATEGORY_HOME);
startActivity(i);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
/*在Activity界面按手机的更多选项按钮,弹出的选择框
*
*
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
if(id == R.id.action_exit){
System.exit(0);
}
return super.onOptionsItemSelected(item);
}
/*
* 接受从service发过来的广播
* 包含歌曲名和艺术家名
*/
private class MsgReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
music_info_textView = (TextView)findViewById(R.id.music_info_textView);
singer_info_textView = (TextView)findViewById(R.id.singer_info_textView);
music_info_textView.setText(intent.getStringExtra("title"));
singer_info_textView.setText(intent.getStringExtra("artist"));
}
}
/*
* 接受从service发过来的广播
* 更新进度条的当前位置
*/
private class BarReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if(seek_bar.getMax() - current_position <= 1100 ){
changeMusic(play_mode,AppConstant.PlayerMsg.NEXT_MUSIC,mp3Infos);
}
else{
current_position = intent.getIntExtra("position",0);
seek_bar.setProgress(current_position);
}
}
}
/*
* 接受从service发过来的广播
* 通知栏的相关信息
*/
private class NotificationReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
notification_previous_music = intent.getBooleanExtra("notification_previous_music",false);
notification_next_music = intent.getBooleanExtra("notification_next_music",false);
notification_pause_music = intent.getBooleanExtra("notification_pause_music",false);
notification_exit = intent.getBooleanExtra("notification_exit",false);
if(notification_previous_music){
changeMusic(play_mode,AppConstant.PlayerMsg.PREVIOUS_MUSIC,mp3Infos);
}
if(notification_next_music){
changeMusic(play_mode,AppConstant.PlayerMsg.NEXT_MUSIC,mp3Infos);
}
if(notification_pause_music){
play_button.setImageResource(R.drawable.play_photo);
}
else{
play_button.setImageResource(R.drawable.pause_photo);
}
if(notification_exit){
System.exit(0);
}
}
}
/*
* 自定义函数
* 用来切歌
* 传入的参数包含:当前播放模式(mode)、上一首还是下一首(msg),歌曲的列表(List<Mp3Info> mp3Infos)
*/
private void changeMusic(int mode,int msg,List<Mp3Info> mp3Infos){
isChangToNext = true;
isPause = false;
current_position = 0;
play_button.setImageResource(R.drawable.pause_photo);
switch (mode){
case AppConstant.PlayerMsg.LOOP_MODE:
switch (msg){
case AppConstant.PlayerMsg.NEXT_MUSIC:
if(music_position < mp3Infos.size() - 1 )
music_position ++;
else
music_position = 0;
break;
case AppConstant.PlayerMsg.PREVIOUS_MUSIC:
if(music_position >= 1 )
music_position --;
else
music_position = mp3Infos.size() - 1;
break;
}
break;
case AppConstant.PlayerMsg.RANDOM_MODE:
music_position = (int)(Math.random() * (mp3Infos.size() - 1));
break;
}
try{
initService(music_position);
Mp3Info mp3_Info = mp3Infos.get(music_position);
isFavorite = mp3_Info.getFavorite();
intent_to_fragment.putExtra("selector_position",music_position); //发广播,改变listView_selector的位置
sendBroadcast(intent_to_fragment);
music_url = mp3_Info.getUrl();
music_info_textView.setText(mp3_Info.getTitle());
singer_info_textView.setText(mp3_Info.getArtist());
seek_bar.setMax((int)mp3_Info.getDuration());
intent_to_service.putExtra("isPause",isPause);
intent_to_changeMusic.putExtra("music_title",mp3_Info.getTitle());
intent_to_changeMusic.putExtra("music_artist",mp3_Info.getArtist());
intent_to_changeMusic.putExtra("music_url",music_url);
intent_to_changeMusic.putExtra("isChangeToNext",isChangToNext);
sendBroadcast(intent_to_service);
sendBroadcast(intent_to_changeMusic);
isChangToNext = false;
// SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
// Date curDate = new Date();
long time = System.currentTimeMillis();
if(myDataBase.IsExistData(MyDataBase.TABLE_NAME,(int)mp3_Info.getId())){ //最近播放存在该歌曲就更新播放时间,不存在就加入数据库
myDataBase.UpdateData(MyDataBase.TABLE_NAME,(int)mp3_Info.getId()
,time);
}
else{
myDataBase.AddData(
MyDataBase.TABLE_NAME,
(int)mp3_Info.getId(),
mp3_Info.getTitle(),
mp3_Info.getArtist(),
mp3_Info.getDuration(),
mp3_Info.getUrl(),
(int)mp3_Info.getAlbum_id(),
time
);
}
}
catch(Exception e){
e.printStackTrace();
}
}
/*
* 播放模式的popupwindow里按键监听器
* 在布局文件里面注册的监听器
*/
public void loop_play_mode_listener(View v){
Toast.makeText(getApplicationContext(),"更改为循环播放模式",Toast.LENGTH_SHORT).show();
play_mode = AppConstant.PlayerMsg.LOOP_MODE;
play_mode_button.setImageResource(R.drawable.play_mode_photo);
popupPlayModeWindow.dismiss();
}
/*
* 播放模式的popupwindow里按键监听器
* 在布局文件里面注册的监听器
*/
public void random_play_mode_listener(View v){
Toast.makeText(getApplicationContext(),"更改为随机播放模式",Toast.LENGTH_SHORT).show();
play_mode = AppConstant.PlayerMsg.RANDOM_MODE;
play_mode_button.setImageResource(R.drawable.random_play_mode);
popupPlayModeWindow.dismiss();
}
/*
* Activity下方控制台的点击监听
* 在布局文件里面注册的监听器
* 点击后会切换至另外一个Fragment,专门用来显示歌词的playFragment
*/
public void main_activity_bottom_layout_listener(View v){
String current_music_url = mp3Infos.get(music_position).getUrl();
PlayFragment playFragment = PlayFragment.newInstance(current_music_url);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.animator.slide_in_up,R.animator.slide_out_down);
fragmentTransaction.replace(R.id.fragment_layout, playFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
/*
* 初始化一个service
* 用于在后台播放歌曲
* 独立于activity
*/
private void initService(int position) {
music_position = position;
Mp3Info mp3Info = mp3Infos.get(position);
Intent intent = new Intent("com.example.communication.MSG_ACTION");
play_button.setImageResource(R.drawable.pause_photo);
intent.putExtra("url", mp3Info.getUrl());
intent.putExtra("title", mp3Info.getTitle());
intent.putExtra("artist", mp3Info.getArtist());
intent.putExtra("album", mp3Info.getAlbum());
intent.putExtra("album_id", mp3Info.getAlbum_id());
intent.putExtra("MSG", AppConstant.PlayerMsg.PLAY_MSG);
intent.setClass(MainActivity.this, PlayerService.class);
seek_bar.setMax((int) (mp3Info.getDuration()));
startService(intent);
}
/*
* 这个是把所有的歌曲标题获取,存放到一个String类型数组里
* 用于AutoCompleTextView的自动补全
*/
public static String[] getMusicList(){
String[] str = new String[mp3Infos.size() - 1];
for(int i = 0;i < mp3Infos.size() - 1;i++){
str[i] = mp3Infos.get(i).getTitle();
}
return str;
}
/*
* 这个可以忽略
* 用于实现我的最爱和最近播放列表的点击播放
* 没有弄好
*/
public static int findPosition(String url){
int position = 0;
while(position < mp3Infos.size()){
if(url.equals(mp3Infos.get(position).getUrl())){
return position;
}
else{
position++;
}
}
return -1;
}
}
今天就先写这么多啦,小达会再抽时间继续奉上我的(辣鸡)教程的,23333。