这两天刚学完了contentprovider和service组件,就综合下所学的,自制了一个简单的音乐播放器。

代码如下:

主activity代码


public class MainActivity extends Activity implements View.OnClickListener,ServiceConnection{

    private TextView musicName, musicTime;//歌曲名,歌曲时间
    private SeekBar seekBar;//歌曲进度
    private ListView musicList;//歌单
    private List<HashMap<String, Object>> list;//数据源
    private Intent intent;//开启服务的意图
    private int item=0;//当前的item
    private MusicService.MyBinder binder;//绑定服务返回的一个binder对象
    private boolean isStart=false;//判断播放器是否在运行
    private static int state=1;//seekbar的一种状态
    private Button btn;//获得开始按钮的实例
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        //歌单适配数据,设置item点击监听,绑定服务并且发送数据给handler
        musicList.setAdapter(new MyAdapter(list,this));
        musicList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                messToServer(position,1);
                item=position;
                sendItem(item,1);
                isStart=true;
                btn.setText("暂停");
            }
        });
        //设置seekbar监听;
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
//                sendState(0);
                state=0;//seekbar按下时的状态避免通过接口回调设置seekbar的进度冲突
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                Log.e("aaa",seekBar.getProgress()+"");
                binder.setSeekbarTo(seekBar.getProgress());//传递松开时的seekbar的进度
                messToServer(item,6);
//                sendState(1);
                state=1;//松开时的状态
            }
        });
    }

    //实例化控件初始化数据
    private void init() {
        findViewById(R.id.musicUp).setOnClickListener(this);
        btn= (Button) findViewById(R.id.musicStart);
        btn.setOnClickListener(this);
        findViewById(R.id.musicStop).setOnClickListener(this);
        findViewById(R.id.musicDwon).setOnClickListener(this);
        seekBar = (SeekBar) findViewById(R.id.seekBar);
        musicName = (TextView) findViewById(R.id.musicName);
        musicTime = (TextView) findViewById(R.id.musicTime);
        musicList = (ListView) findViewById(R.id.musicList);
        list = new ArrayList<>();
        //获取音频文件的uri
        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        String[] projection = {
                MediaStore.Audio.Media.DATA,
                MediaStore.Audio.Media.DISPLAY_NAME
        };
        //通过uri查询的路劲和文件名
        Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
        //遍历返回的cursor获取数据源
        while (cursor.moveToNext()){
            HashMap<String,Object> map=new HashMap<>();
            map.put("path",cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
            map.put("name",cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));
            list.add(map);
        }
        //绑定服务
        intent=new Intent(this,MusicService.class);
        bindService(intent,this, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            //上一曲
            case R.id.musicUp:
                item=(item==0)?(list.size()-1):(item-1);
                sendItem(item,1);
                messToServer(item,2);
                break;
            //开始/暂停
            case R.id.musicStart:
                if(isStart){
                    messToServer(item,4);
                    isStart=false;
                    btn.setText("开始");
                }else{
                    messToServer(item,3);
                    isStart=true;
                    btn.setText("暂停");
                }
                sendItem(item,1);
                break;
            //停止
            case R.id.musicStop:
                isStart=false;
                sendItem(item,1);
                messToServer(item,5);
                btn.setText("开始");
                break;
            //下一曲
            case R.id.musicDwon:
                item=(item==list.size()-1)?(0):(item+1);
                sendItem(item,1);
                messToServer(item,2);
                break;
        }
    }
    //绑定服务封装
    public void messToServer(int item,int what){
        String path=list.get(item).get("path").toString();
        binder.setPath(path,what);
    }

    //实现接口获得从服务器返回的数据,接口回调获得播放器当前的进度和当前歌曲的总时间
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        binder= (MusicService.MyBinder) service;//绑定服务返回的对象
        //使用接口回调获得播放器的状态,当前进度和总体时间
        binder.getService().setCallBack(new MusicService.CallBack() {
            @Override
            public void onPlayerState(int state, int progress, int duration) {
                sendProgress(progress,duration,2);
//                sendState(state,3);
            }
        });
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }

    //创建一个handler,通过what来执行对应操作
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    //设置当前歌曲的名字
                    int item=msg.getData().getInt("item");
                    musicName.setText(list.get(item).get("name").toString());
                    break;
                case 2:
                    //设置seekbar的最大值和当前进度
//                    int state=msg.getData().getInt("state");
                    //如果seekbar的状态为0则不会按回调的数据来设置progress进度避免了冲突
                    if(state==0){
                        break;
                    }
                    int progress=msg.getData().getInt("progress");
                    int duration=msg.getData().getInt("duration");
                    if(duration<0||durantion<999999){//播放器没播放或者停止的时候分别返回一个很大的数和一个很小的负数
                        progress=0;
                        duration=0;
                    }
                    seekBar.setMax(duration);
                    seekBar.setProgress(progress);
                    //把progress和duration转化成分钟和秒数
                    String dtime=(progress/1000)/60+":"+(progress/1000)%60;
                    String ztime=(duration/1000)/60+":"+(duration/1000)%60;
                    musicTime.setText(dtime+"/"+ztime);
                    break;
            }
        }
    };
    //发送歌曲name到handler的方法
    public void sendItem(int item,int what){
        Message mess=new Message();
        mess.what=what;
        Bundle bundle=new Bundle();
        bundle.putInt("item",item);
        mess.setData(bundle);
        handler.sendMessage(mess);
    }

    //发送当前进度和总体进度到hander的方法
    public void sendProgress(int progress,int duration,int what){
        Message mess=new Message();
        mess.what=what;
        Bundle bundle=new Bundle();
        bundle.putInt("progress",progress);
        bundle.putInt("duration",duration);
        mess.setData(bundle);
        handler.sendMessage(mess);
    }
//    public void sendState(int state,int what){
//        Message mess=new Message();
//        mess.what=what;
//        Bundle bundle=new Bundle();
//        bundle.putInt("state",state);
//        mess.setData(bundle);
//        handler.sendMessage(mess);
//    }

    //退出activity停止服务
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(this);
    }
}




service代码


public class MusicService extends Service {
    private MediaPlayer player;
    private String path;//从activity获得的path
    private int what;//从activity获得的动作
    private boolean isPause = false;//判断是否是暂停
    private boolean flag = false;//关闭子线程用
    private CallBack callBack;//一个回调接口
    private boolean isStop=false;//判断是否停止
    private boolean first=true;//判断是否第一次执行
    private int progress=0;//拖动seekbar时返给progress值

    @Override
    public void onCreate() {
        super.onCreate();
        //实例化mediaplayer
        player = new MediaPlayer();
        player.setLooping(true);
        //开启一个子线程来对从activity获得的值操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                flag = true;
                while (flag) {
                    switch (what) {
                        case 1:
                            //点击listview的item传来path进行播放
                            if (isPause == true) {
                                isPause = false;
                            }
                            startPlayer(path);
                            what = 0;
                            break;
                        case 2:
                            //上一曲或者下一曲传来path进行播放,如果播放器没有播放,则什么都不干
                            if (isPause == true) {
                                isPause = false;
                            }
                            if (player.isPlaying()) {
                                startPlayer(path);
                            }else{
                                player.reset();
                            }
                            what = 0;
                            break;
                        //开始播放或者继续播放
                        case 3:
                            startPlayer(path);
                            what = 0;
                            break;
                        //暂停
                        case 4:
                            if(isStop==true){
                                what=3;
                                isStop=false;
                                break;
                            }
                            pausePlayer();
                            what = 0;
                            break;
                        //停止
                        case 5:
                            stopPlayer();
                            what = 0;
                            break;
                        //随着seekbar改变进度
                        case 6:
                            seekBarTo(progress);
                            what=0;
                            break;
                        default:
                            break;
                    }
                    //实现接口
                    if (callBack != null) {
                        if(first==true||isStop==true){
                            callBack.onPlayerState(-1,0,0);
                            isStop=false;
                            first=false;
                            continue;
                        }
                        if (player.isPlaying()) {
                            callBack.onPlayerState(1, player.getCurrentPosition(), player.getDuration());
                        } else {
                            callBack.onPlayerState(0, player.getCurrentPosition(), player.getDuration());
                        }
                    }
                }
            }
        }).start();

    }



    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    public class MyBinder extends Binder {
        //从activity拿到path和what值
        public void setPath(String path, int what) {
            MusicService.this.path = path;
            MusicService.this.what = what;
        }
        //拿到seekbar拖动到的位置值
        public void setSeekbarTo(int progress){
            MusicService.this.progress=progress;
        }

        //返回一个service的实例
        public MusicService getService() {
            return MusicService.this;
        }
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //服务关闭的时候关闭线程关闭player并释放资源
        flag = false;
        player.stop();
        player.release();
    }

    //开始播放歌曲
    public void startPlayer(String path) {
        //如果是暂停状态,则继续播放
        isStop=false;
        if (isPause) {
            player.start();
            isPause = false;
            return;
        }
        //如果player在运行,则重置player
        if (player.isPlaying()) {
            player.stop();
            player.reset();
        }
        try {
            player.setDataSource(path);
            player.prepare();
            player.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //暂停播放器
    public void pausePlayer() {
        if (player.isPlaying()) {
            player.pause();
            isPause = true;
        }
    }

    //停止播放器
    public void stopPlayer() {
            player.stop();
            player.reset();
            isStop=true;
        isPause=false;
    }
    //从seekbar拖动的位置开始播放
    private void seekBarTo(int progress) {
        player.seekTo(progress);
        player.start();
    }


    //接口
    public interface CallBack {
        void onPlayerState(int state, int progress, int duration);
    }

    public CallBack getCallBack() {
        return callBack;
    }

    public void setCallBack(CallBack callBack) {
        this.callBack = callBack;
    }
}






适配器代码


public class MyAdapter extends BaseAdapter {
    private List<HashMap<String,Object>> list;
    private Context context;

    public MyAdapter(List<HashMap<String, Object>> list, Context context) {
        this.list = list;
        this.context = context;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Holder holder=null;
        if(convertView==null){
            convertView=View.inflate(context,R.layout.itemlayout,null);
            holder=new Holder();
            holder.listItem= (TextView) convertView.findViewById(R.id.listItem);
            convertView.setTag(holder);
        }else{
            holder= (Holder) convertView.getTag();
        }
        holder.listItem.setText(list.get(position).get("name").toString());
        return convertView;
    }
    class Holder{
        TextView listItem;
    }
}

主activity的布局文件

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical">

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:gravity="center_vertical">

        <TextView
            android:lines="1"
            android:id="@+id/musicName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="50dp"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/musicTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginRight="10dp" />
    </RelativeLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:layout_margin="10dp"
            android:background="@drawable/button"
            android:id="@+id/musicUp"
            android:text="上曲"
            android:layout_width="60dp"
            android:layout_height="40dp" />
        <Button
            android:layout_margin="10dp"
            android:background="@drawable/button"
            android:id="@+id/musicStart"
            android:text="开始"
            android:layout_width="60dp"
            android:layout_height="40dp" />
        <Button
            android:layout_margin="10dp"
            android:background="@drawable/button"
            android:id="@+id/musicStop"
            android:text="停止"
            android:layout_width="60dp"
            android:layout_height="40dp" />
        <Button
            android:layout_margin="10dp"
            android:id="@+id/musicDwon"
            android:background="@drawable/button"
            android:text="下曲"
            android:layout_width="60dp"
            android:layout_height="40dp" />
    </LinearLayout>
    <TextView
        android:gravity="center"
        android:text="我的歌单"
        android:textSize="20sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:layout_marginTop="10dp"
            android:id="@+id/musicList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></ListView>
    </LinearLayout>

</LinearLayout>

ListView的item布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_margin="5dp"
        android:textSize="15sp"
        android:id="@+id/listItem"
        android:lines="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>