- 在res目录下创建一个raw目录
- raw目录是android中唯一不会自动创建的目录
- 存放的是android无法自动识别的文件
MediaPlayer
创建MeidiaPlayer
- 调用reset方法,初始化MediaPlayer(调与不调没区别)
- 读取音乐文件
//最原始的方式获取文件
Resources resources = getResources();
AssetFileDescriptor assetFileDesc = resources.openRawResourceFd(R.raw.zxsd);
FileDescriptor fileDesc = assetFileDesc.getFileDescriptor();
//优化:通过文件描述符设置数据源
player.setDataSource("http://qzone.haoduoge.com/music2/2014-10-30/1414658946.mp3");
- 准备: 加载数据
player.prepare();
- 启动
player.start();
- 每次退出后要销毁此进程(onDestroy方法)
@Override
public void onDestroy() {
player.stop();//停止
player.release();//释放资源
super.onDestroy();
} 设置开始与结束按钮
@Override public void start() { player.start(); } @Override public void stop() { player.stop(); }
设置单曲循环
public void loop() { player.setLooping(true); }
- 防止ANR异常(阻塞主线程) 使用异步:
player.perpareAsync();
正在准备中(Preparing)走到准备中(Prepared):需要设置个监听器
player.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { //如果这个方法调用,则代表准备完成 Toast.makeText(getApplicationContext(), "准备完成",Toast.LENGTH_LONG).show(); } });`
设置错误监听
`player.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(getApplicationContext(), "发生了错误", Toast.LENGTH_LONG).show(); return false; } });`
完整的回调:
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
isPlaying = false;
//完成的回调
Toast.makeText(getApplicationContext(), "播放完成", Toast.LENGTH_SHORT).show();
}
});
- 如果只在主线程中运行代码,那么一back,则会杀掉进程,so要放在一个Service中:
private MediaPlayer player;//定义成成员方法
@Override
public void onCreate() {
super.onCreate();
player = new MediaPlayer();
//player.reset();
Resources resources = getResources();
AssetFileDescriptor assetFileDesc = resources.openRawResourceFd(R.raw.zxsd);
FileDescriptor fileDesc = assetFileDesc.getFileDescriptor();
try
{
//通过文件描述符设置数据源
player.setDataSource(fileDesc);
player.setOnPerparedListener(new OnPerparedListener()
{
@Override
public void onPerpared(MediaPlayer mp)
{
//如果这个方法调用,则代表准备完成
Toast.makeText(getApplicationContext(),"准备完成",Toast.LENGTH_LONG).show();
}
});
}
//设置错误监听
player.setOnErrorListener(new OnErrorListener()
{
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(getApplicationContext(), "发生了错误", Toast.LENGTH_LONG).show();
return false;
}
});
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
isPlaying = false;
//完成的回调
Toast.makeText(getApplicationContext(), "播放完成", Toast.LENGTH_SHORT).show();
}
});
//准备: 加载数据
// player.prepare();
//异步准备
} catch (IOException e) {
e.printStackTrace();
}
public void onStartClick(View view)
{
player.start();
}
public void onStopClick(View view)
{
player.stop();
}
@Override
public void onDestroy() {
player.stop();
player.release();
super.onDestroy();
}
在清单文件中注册一下此Service:
<service android:name="com.example.music.MusicService"/>
把操作写在了Sverice中,那么在Activity中则完全无法访问,so要与之绑定:
//此为Service中方法
public void start()
{
player.start();
}
public void stop()
{
player.stop();
}
- 在Activity中调用:
public void onStartClick(View view)
{
//调用service方法启动音乐
}
public void onStopClick(View view)
{
//调用service方法暂停音乐
}
- 与之绑定:
//混合启动
Intent intent = new Intent(this,MusicService.class);
startService(intent);//先把Service启动
conn = new Conn();//conn定义成成员变量
bindService(intent, conn, BIND_AUTO_CREATE);
//定义一个内部Conntion类
class Conn implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
}
@Override
public void onServiceDisconnected(ComponentName name)
{
}
}
//在销毁的时候,那么conn应该也随之销毁
@Override
protected void onDestroy()
{
unbindService(conn);
super.onDestroy();
}
- 但是,并不是所有的方法都要对外暴露,so定义一个接口,由MyBinder实现:
package com.example.music;
public interface MusicController
{
public void start();
public void stop();
}
- 如果与之绑定,则要返回一个IBinder对象
class MyBinder extends Binder implements MusicController
{
@Override
public void start()
{
MusicService.this.start();
}
@Override
public void stop()
{
MusicService.this.stop();
}
}
- 定义好的controller接口为空,so直接把binder对象强转:
class Conn implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
controller = (MusicController)service;
}
@Override
public void onServiceDisconnected(ComponentName name)
{
}
}
- 现在可以在开始/暂停button中调用此方法:
public void onStartClick(View view)
{
//调用Service的方法启动音乐
controller.start();
}
public void onStopClick(View view)
{
//调用Service的方法停止音乐
controller.stop();
}
- 由于是在进程中绑定的,那么则会在后台运行,如果再次打开Activity。那么则会发生错误,so把prepareAsync写在start中,由于该方法为异步,则把start写在回调方法中:
public void start()
{
player.prepareAsync();
}
@Override
public void onPrepared(MediaPlayer mp)
{
//如果这个方法调用,则代表准备完成
Toast.makeText(getApplicationContext(), "准备完成", Toast.LENGTH_LONG).show();
player.start();
}
- 创建一个进度条(ProgressBar),but现在使用ProgressBar的子类SeekBar(拖拉条?)
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:max="1000"
/>
- 在Activity中调用SeekBar:
private SeekBar seekbar;
seekbar = (SeekBar) this.findViewById(R.id.seekbar);