另外注意Log的使用
注意权限的添加
读取存储卡的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
音乐播放器带有缓存功能
主程序
package com.test.myplayer;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.test.service.MusicService;
import com.test.song.Song;
import com.test.song.SongAdapter;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
"静态常量是为了避免使用时拼写错误,另外因为有具体的名,所以使用时容易辨别它的作用。"
public final static String ACTION = "com.test.myplayer";
public final static int TYPE_PLAYER = 1;
public final static int TYPE_PAUSE = 2;
public final static int TYPE_SEEKTO = 3;
public final static int TYPE_CHECKBOX_CHECKED = 4;
// public final static int TYPE_PRE=4;
// public final static int TYPE_NEXT=5;
public final static String PLAY_TYPE_FLAG = "12";
private TextView mAllTime;//获得歌曲的总时长
private TextView mCurrentTime;//歌曲目前播放到的时长
private ImageButton mButtonPrevious;//上一首
private ImageButton mButtonNext;//下一首
private Boolean mIsChecked = false;//默认checkbox的状态值
private SeekBar seekBar1;//歌曲进度条
private CheckBox mCheckBoxPlayer;//checkbox的实例
private List<Song> mListSong;//Song的集合
private LayoutInflater mInflater;
private SongAdapter mAdapter;//song的适配器
private ListView listView;//listView用来填充song
private List<File> fileMp3 = new ArrayList<File>();//辅助的集合用来村完整的歌曲名
private String songPath;//song的路径
private int mSongNum;//当前播放的song在集合中的位置
private MusicBroadcast mBroadcastReceiver;//广播接收者
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//给所用控件初始化
mAllTime = (TextView) findViewById(R.id.all_time);
mCurrentTime = (TextView) findViewById(R.id.current_time);
mButtonPrevious = (ImageButton) findViewById(R.id.previous_button);
mButtonNext = (ImageButton) findViewById(R.id.next_button);
listView = (ListView) findViewById(R.id.list_song);
mCheckBoxPlayer = (CheckBox) findViewById(R.id.play_checkbox);
seekBar1 = (SeekBar) findViewById(R.id.seekBar1);
//添加点击事件
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);
mBroadcastReceiver = new MusicBroadcast();
//注册广播接收者
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION);
registerReceiver(mBroadcastReceiver, filter);
//集合添加数据一定要在前面。
mListSong = new ArrayList<Song>();
mInflater = getLayoutInflater();
//此时获得是一进入文件管理时的路径
File customFiles = Environment.getExternalStorageDirectory();
//然后通过新建文件则要查找的文件夹
File file = new File(customFiles, "/netease/cloudmusic/Music");
//下面屏蔽的是直接去存储卡自带的Music文件下查找
// File file =
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
File[] files = file.listFiles();
for (File innerfile : files) {
String name = innerfile.getName();
int index = name.lastIndexOf(".");
String last = name.substring(index, name.length());// index包含最后一位判断最后的结尾方式
//获取其中的全部的mp3文件
if (last.equals(".mp3") || last.equals(".wav")) {
fileMp3.add(innerfile);
String songName = name.substring(0, index);
Log.d("lala", songName);
Song song = new Song(songName);
mListSong.add(song);
}
}
//给适配器传入值
mAdapter = new SongAdapter(mInflater, mListSong);
listView.setAdapter(mAdapter);
//给每条listView添加点击事件
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//通过position来判断当前点击的是哪一条list
File sigleSong = fileMp3.get(position);
//获得这是该条在集合中的位置,方便之后播放前一首或后一首时有个参照。
mSongNum = position;
Log.d("nihao", "" + sigleSong.getAbsolutePath());
songPath = sigleSong.getAbsolutePath();
//启动服务播放歌曲
Intent intent = new Intent(getApplicationContext(), MusicService.class);
//将所需的数据通过intent传递。
intent.putExtra("musicpath", songPath);
intent.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
mCheckBoxPlayer.setChecked(true);
startService(intent);
}
});
mButtonPrevious.setOnClickListener(this);
//滑动条添加拖动事件,一共有三个事件使用时注意查看API
seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// fromUser判断是用户改变的滑块的值
if (fromUser == true) {
Intent intent = new Intent(getApplicationContext(), MusicService.class);
intent.putExtra(PLAY_TYPE_FLAG, TYPE_SEEKTO);
intent.putExtra("SeekBar", progress);
startService(intent);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
});
//CheckBox的选择事件
mCheckBoxPlayer.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mIsChecked = isChecked;
//isChecked是此时的状态,通过状态判断来做出相应的动作。
if (mIsChecked) {
SharedPreferences sharedPreferences = getSharedPreferences(MusicService.FILE_NAME, MODE_PRIVATE);
songPath = sharedPreferences.getString(MusicService.PATH_PAUSE, "0");
int psuseTime = Integer.parseInt(sharedPreferences.getString(MusicService.PAUSE_NOTE, "0"));
Intent intent = new Intent(getApplicationContext(), MusicService.class);
intent.putExtra(PLAY_TYPE_FLAG, TYPE_CHECKBOX_CHECKED);
intent.putExtra("musicPausePath", songPath);
intent.putExtra(MusicService.PAUSE_NOTE, psuseTime);
startService(intent);
} else {
Intent intent = new Intent(getApplicationContext(), MusicService.class);
intent.putExtra(PLAY_TYPE_FLAG, TYPE_PAUSE);
intent.putExtra("musicpath", songPath);
startService(intent);
}
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.previous_button:
mSongNum -= 1;
//都是通过intent发送给后台的服务
File sigleSongPre = fileMp3.get(mSongNum);
songPath = sigleSongPre.getAbsolutePath();
Intent intent = new Intent(getApplicationContext(), MusicService.class);
intent.putExtra("musicpath", songPath);
//通过传入不同的类型方便后台服务根据类型来区分
intent.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
//别忘了启动服务
startService(intent);
break;
case R.id.next_button:
mSongNum += 1;
File sigleSongNext = fileMp3.get(mSongNum);
songPath = sigleSongNext.getAbsolutePath();
Intent intentNext = new Intent(getApplicationContext(), MusicService.class);
intentNext.putExtra("musicpath", songPath);
intentNext.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
startService(intentNext);
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//销毁活动时,注意给broadcast取消注册。
unregisterReceiver(mBroadcastReceiver);
}
/**
*在类的内部创建Broadcast来接收服务传递过来的信息
*/
class MusicBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//也是通过类型的判断来区分后台服务传递过来的信息
int type = intent.getIntExtra("type", 0);
switch (type) {
case 1:
int timeall = intent.getIntExtra("time", 0);
seekBar1.setMax(timeall);
int fen = (timeall / 1000) / 60;
int miao = (timeall / 1000) % 60;
String time1 = fen + ":" + miao;
mAllTime.setText(time1);
break;
case 2:
int timecurrent = intent.getIntExtra("time", 0);
seekBar1.setProgress(timecurrent);
int fen2 = (timecurrent / 1000) / 60;
int miao2 = (timecurrent / 1000) % 60;
String time2 = fen2 + ":" + miao2;
mCurrentTime.setText(time2);
break;
default:
break;
}
}
}
}
音乐部分程序
MusicService服务程序
package com.test.service;
import java.io.IOException;
import com.test.myplayer.MainActivity;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class MusicService extends Service {
private MediaPlayer player;
private String songPath;
public final static String FILE_NAME = "pauseNotes";
public final static String PATH_PAUSE = "PATH";
public final static String PAUSE_NOTE = "PAUSE";//暂停时存入缓存的时间标志。
public final static int ALL_TIME = 1;
public final static int CURRENT_TIME = 2;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("123", 156 + "");
得到intent传递的值
int playType = intent.getIntExtra(MainActivity.PLAY_TYPE_FLAG, 0);
switch (playType) {
case MainActivity.TYPE_PLAYER:
if (player == null) {
player = new MediaPlayer();
}
player.reset();
//得到文件的路径
songPath = intent.getStringExtra("musicpath");
try {
Log.d("123", songPath);
player.setDataSource(songPath);
player.prepare();
int time = player.getDuration();
player.start();
Intent intent1 = new Intent(MainActivity.ACTION);
intent1.putExtra("type", ALL_TIME);
intent1.putExtra("time", time);
//发送广播
sendBroadcast(intent1);
//开启线程
MyMusicThread thread = new MyMusicThread();
thread.start();
// player.setOnPreparedListener(new OnPreparedListener() {
//
// @Override
// public void onPrepared(MediaPlayer mp) {
// if (mp.isPlaying()) {
//
// } else {
//
// mp.start();
// int time = player.getDuration();
// Intent intent = new Intent();
// intent.putExtra("type", ALL_TIME);
// intent.putExtra("time", time);
// sendBroadcast(intent);
// MyMusicThread thread = new MyMusicThread();
// thread.start();
// }
//
// }
// });
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case MainActivity.TYPE_PAUSE:
//得到此时的播放进度,然后暂停,并将进度存入缓存
int pausePosition = player.getCurrentPosition();
player.pause();
SharedPreferences sharedPreferences = getSharedPreferences(FILE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(PAUSE_NOTE, "" + pausePosition);
editor.putString(PATH_PAUSE, songPath);
editor.commit();
break;
case MainActivity.TYPE_SEEKTO:
//拖动seekbar的响应事件
int progress = intent.getIntExtra("SeekBar", 0);
if (player != null) {
player.seekTo(progress);
}
break;
case MainActivity.TYPE_CHECKBOX_CHECKED:
if (player == null) {
//防止文件为空
player = new MediaPlayer();
if (!player.isPlaying()) {
songPath = intent.getStringExtra("musicPausePath");// 默认文件中所存储的音乐
int pauseTime = intent.getIntExtra(PAUSE_NOTE, 0);// 初始时要seekto到的时间。
Log.d("lalalla", songPath+pauseTime);
try {
player.setDataSource(songPath);
player.prepare();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int time = player.getDuration();
player.start();
player.seekTo(pauseTime);
Intent intent1 = new Intent(MainActivity.ACTION);
intent1.putExtra("type", ALL_TIME);
intent1.putExtra("time", time);
sendBroadcast(intent1);
MyMusicThread thread = new MyMusicThread();
thread.start();
}
}
break;
default:
break;
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
/**
*创建一个线程不断调整时间然后发送广播给主程序。
*/
class MyMusicThread extends Thread {
@Override
public void run() {
while (player.isPlaying()) {
int time = player.getCurrentPosition();
Intent intent = new Intent(MainActivity.ACTION);
intent.putExtra("type", CURRENT_TIME);
intent.putExtra("time", time);
sendBroadcast(intent);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
song程序,里面含有未开发部分
package com.test.song;
public class Song {
private String name;
private String author;
private int Image;
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getImage() {
return Image;
}
public void setImage(int image) {
Image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Song(String name) {
this.name = name;
}
}
SongAdapter适配器程序
package com.test.song;
import java.io.File;
import java.util.List;
import com.test.myplayer.R;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
public class SongAdapter extends BaseAdapter{
private LayoutInflater mInflater;
private List<Song> mListSong;
public SongAdapter(LayoutInflater mInflater, List<Song> mListSong) {
this.mInflater = mInflater;
this.mListSong = mListSong;
}
@Override
public int getCount() {
return mListSong.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder=null;
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_song, null);
viewHolder=new ViewHolder();
// viewHolder.author=(TextView) convertView.findViewById(R.id.author);
// viewHolder.image=(ImageView) convertView.findViewById(R.id.image);
viewHolder.name=(TextView) convertView.findViewById(R.id.song_name);
convertView.setTag(viewHolder);
}else{
viewHolder=(ViewHolder) convertView.getTag();
}
Song song=mListSong.get(position);
/**
* 当传入完整路径时使用该部分,通过路径使用MediaMetadataRetriever
*/
"这里可以获得歌手名,但是同样需要传入的是完整的路径名"
// File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
// File[] files = file.listFiles();
//
// MediaMetadataRetriever mediaRetraiver=new MediaMetadataRetriever();
// mediaRetraiver.setDataSource(files[0].getAbsolutePath());
// String author=mediaRetraiver.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
// if(author!=null){
// viewHolder.author.setText(author);
//
// }else{
// viewHolder.author.setText("<未知>");
// }
viewHolder.name.setText(song.getName());
// byte[] image=mediaRetraiver.getEmbeddedPicture();
"这些屏蔽程序可以画一个图片添加到单条布局中,但是需要传入完整的文件路径"
// if(image!=null){
// Bitmap bitmap=BitmapFactory.decodeByteArray(image, 0, image.length);
// viewHolder.image.setImageBitmap(bitmap);
// }else{
// viewHolder.image.setImageResource(R.drawable.ic_launcher);
// }
return convertView;
}
class ViewHolder{
ImageView image;
TextView author;
TextView name;
}
}
布局文件
主布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/playpage_background"
android:orientation="vertical" >
<ListView
android:id="@+id/list_song"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:cacheColorHint="#ffffffff"
android:divider="#55ff0000" >
</ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="@+id/all_time"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="00:00"/>
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="254dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="10dp" />
<TextView
android:id="@+id/current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="00:00"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:orientation="horizontal" >
<ImageButton
android:id="@+id/previous_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:scaleX="2"
android:scaleY="2"
android:background="@drawable/previousbackground" />
<CheckBox
android:id="@+id/play_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="2"
android:scaleY="2"
android:background="@drawable/playback"
android:button="@null" />
<ImageButton
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:scaleX="2"
android:scaleY="2"
android:background="@drawable/nextbackground" />
</LinearLayout>
</LinearLayout>
单条Song的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="ni"/>
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
/>
</LinearLayout>
checkbox的按压文件
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/desk_pause" android:state_checked="true"/>
<!--没有加状态的放在后面-->
<item android:drawable="@drawable/desk_play"/>
</selector>
button的按压文件
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/desk_next_prs" android:state_pressed="true"/>
<!--没有加状态的放在后面-->
<item android:drawable="@drawable/desk_next"/>
</selector>