实验课—Android开发可以简单播放的音乐播放器

简易播放器

本次实验要求实现一个简单的音乐播放器。

主要实现的功能有:可以实现多首音乐播放(此处音乐放置在Raw下,并非是获取存储中的文件)、实现列表中上一首下一首播放、进度条可以拖动、播放时显示当前歌曲信息。

功能不难,基本上都是常规代码设计,搞清楚逻辑,写起来就完事儿了。再次特别感谢参考博客:小妖的博客

1.布局

2.Adapter设配列表

3.点击列表,触发监听事件

4.播放

5.开始和暂停

6.上一首、下一首

7.效果图

 

首先是布局:ListVIew、seekBar等,设置背景、以及顶部项目名,没有啥难度。

 


 

 接下来是Adapter适配器适配ListVIew。

首先是先新建一个MusicFile类,将一首音乐的歌曲名、歌手名、专辑封面、总时长、歌曲分类封装起来。

private String musicName;
private int imageID;
private String singer;
private String sorter;
private String minutes;
private int path;

然后为每个列表项布局music_item.xml

 

 MusicAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class MusicAdapter extends ArrayAdapter<MusicFile> {
    private int resourceId;

    public MusicAdapter(Context context, int textViewResourceId, List<MusicFile> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //根据position获取待显示的Name实例
        MusicFile music = getItem(position);
        //获取子视图控件实例
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        ImageView imageID = (ImageView) view.findViewById(R.id.imageView);
        TextView musicName = (TextView) view.findViewById(R.id.music_name);
        TextView singer = (TextView) view.findViewById(R.id.singer);
        TextView sorter = (TextView) view.findViewById(R.id.sorter);
        TextView time = (TextView) view.findViewById(R.id.minutes);
        //将Name实例内的名字和头像赋值给子视图控件实例
        imageID.setImageResource(R.drawable.music1);
        musicName.setText(music.getMusicName());
        singer.setText(music.getSinger());
        sorter.setText(music.getSorter());
        time.setText(music.getMinutes());
        return view;
    }
}

 

MainActivity.java中全局变量列表:

    private ListView musiclist;
    private TextView musiclistCurrentTime;
    private TextView musiclistTotalTime;
    private TextView infotextView1;
    private TextView infotextView2;
    private ImageView imageView;
    private ImageView musiclistPlay;
    private SeekBar seekBar;

    private MediaPlayer mediaPlayer;
    private Handler handler;
    private Thread thread = new Thread();
    private MusicAdapter adapter;
    private ListView listView;
    private List<MusicFile> MusicList = new ArrayList<>();

    private static final int IDLE = 0;   //空闲:没有播放音乐
    private static final int PAUSE = 1;  //暂停:播放音乐时暂停
    private static final int START = 2;  //正在播放音乐
    private boolean flag = false;//控制进度条的索引

    private static final int CURR_TIME_VALUE = 1;

    private int currState = IDLE;//当前播放器的状态
    private int currPosition;//list的当前选中项的索引值(第一项对应0)

 

MainActivity中,适配ListView:

    private MusicAdapter adapter;
    private ListView listView;
    private List<MusicFile> MusicList = new ArrayList<>();
    adapter = new MusicAdapter(MainActivity.this, R.layout.music_item, MusicList);
    listView = (ListView) findViewById(R.id.musiclist);
    listView.setAdapter(adapter);

初始化数据:

    //初始化列表数据
    private void initInfo() {
        MusicFile nw = new MusicFile("新世界", R.drawable.nwimage, "华晨宇", "5:51", "心愿歌单", R.raw.newworld);
        MusicList.add(nw);
        MusicFile mh = new MusicFile("疯人院", R.drawable.mhimage, "华晨宇", "6:37", "华语精选", R.raw.madhouse);
        MusicList.add(mh);
        MusicFile nc = new MusicFile("Nobody Can Save Me", R.drawable.ncimage, "Linkin Park", "3:45", "摇滚精选", R.raw.ns);
        MusicList.add(nc);
        MusicFile ss = new MusicFile("神树", R.drawable.ssimage, "华晨宇", "5:19", "Love Songs", R.raw.shenshu);
        MusicList.add(ss);
        MusicFile co = new MusicFile("After the After party", R.drawable.aaimage, "Charli XCX", "3:39", "Love Songs", R.raw.aa);
        MusicList.add(co);
        MusicFile aa = new MusicFile("Children of A Miracle", R.drawable.coimage, "Don Diablo", "3:09", "欧美精选", R.raw.co);
        MusicList.add(aa);

        MusicFile sp = new MusicFile("杀破狼", R.drawable.spimage, "JS", "4:40", "心愿歌单", R.raw.sp);
        MusicList.add(sp);
        MusicFile yl = new MusicFile("月球", R.drawable.yqimage, "银临", "4:39", "华语精选", R.raw.yl);
        MusicList.add(yl);
        MusicFile nc1 = new MusicFile("Nobody Can Save Me", R.drawable.ncimage, "Linkin Park", "3:45", "摇滚精选", R.raw.ns);
        MusicList.add(nc1);
        MusicFile aa1 = new MusicFile("Children of A Miracle", R.drawable.coimage, "Don Diablo", "3:09", "欧美精选", R.raw.co);
        MusicList.add(aa);
        MusicFile nw2 = new MusicFile("新世界", R.drawable.nwimage, "华晨宇", "5:51", "心愿歌单", R.raw.newworld);
        MusicList.add(nw2);
        MusicFile co1 = new MusicFile("After the After party", R.drawable.aaimage, "Charli XCX", "3:39", "Love Songs", R.raw.aa);
        MusicList.add(co1);


    }

 

 


 

到现在为止,UI界面已经初始化好了,接下来就是处理点击列表项,播放音乐。

//列表点击事件
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        MusicFile currMf = MusicList.get(position);
        infotextView1.setText(currMf.getMusicName());
        infotextView2.setText(currMf.getSinger());
        //获取专辑图片
        imageView.setImageResource(currMf.getImageID());
        //点击列表项,ImageView的初始状态都为play
        musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause));
        if (currState == IDLE) {
            //记录当前列表项的下标
            currPosition = position;
            play(position);
        //在播放中的音乐,重置掉    
        } else {
            mediaPlayer.reset();
            //记录下标
            currPosition = position;
            play(position);
        }
    }

 


 

播放函数play:

    //播放
    private void play(int position) {
        //根据当前列表项的下标,获取MusicList中对应的歌曲以及路径
        MusicFile currMf = MusicList.get(position);
        int currPath = currMf.getPath();
        //初始化mediaPlayer
        mediaPlayer = MediaPlayer.create(this, currPath);
        mediaPlayer.start();
        //初始化seekBar
        initSeekBar();
        //设置当前状态为START
        currState = START;
        //开启新线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                flag = true;
                while (flag) {
                    //实时更新进度条
                    if (mediaPlayer.getCurrentPosition() < seekBar.getMax()) {
                        seekBar.setProgress(mediaPlayer.getCurrentPosition());
                        Message msg = handler.obtainMessage(CURR_TIME_VALUE,
                                toTime(mediaPlayer.getCurrentPosition()));
                        handler.sendMessage(msg);
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        flag = false;
                    }
                }
            }
        }).start();
    }

 

handler初始化:

        handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case CURR_TIME_VALUE:
                        musiclistCurrentTime.setText(msg.obj.toString());
                }
            }


        };

 

其中initSeekBar()

    //初始化进度条
    private void initSeekBar() {
        //获取总时长
        int duration = mediaPlayer.getDuration();
        seekBar.setMax(duration);
        seekBar.setProgress(0);
        if (duration > 0) {
            musiclistTotalTime.setText(toTime(duration));
        }
    }

 

对要显示的时间进行格式化处理,显示“xx:xx”:totime()

    //格式化时间
    private String toTime(int duration) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("mm:ss", Locale.getDefault());
        sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
        date.setTime(duration);
        return sdf.format(date);
    }

 


 基本点击播放效果也已经完成,接下来就是下方按钮(此处用的是ImageView进行布局,没有本质差别)所产生的对应效果。

onPlayClick()

    //播放按钮(前提是已经点击列表项)
    public void onPlayClick(View v) {
        switch (currState) {
            case PAUSE:
                mediaPlayer.start();
                musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause));
                currState = START;
                break;
            case START:
                mediaPlayer.pause();
                musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.play));
                currState = PAUSE;
                break;
        }
    }

 


点击下一首和上一首,原理基本相同,稍微有一点差别就是对于两端音乐上下首的处理。

上一首:onPreviousClick()

    //上一首
    public void onPreviousClick(View v) {
        previous();
    }

    private void previous() {
        if (currPosition > 0) {
            switch (currState) {
                case IDLE:
                    //滚动列表到下一项
                    musiclist.smoothScrollToPosition(currPosition - 1);
                    //模拟点击Item时间,触发监听器
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(currPosition - 1, null, null),
                            currPosition - 1,
                            musiclist.getItemIdAtPosition(currPosition - 1));
                    break;
                case START:
                case PAUSE:
                    stop();
                    musiclist.smoothScrollToPosition(currPosition - 1);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(currPosition - 1, null, null),
                            currPosition - 1,
                            musiclist.getItemIdAtPosition(currPosition - 1));
                    break;
            }
        //如果currPosition等于0,也就是列表第一个
        } else {
            switch (currState) {
                case IDLE:
                    //则设置滚动到列表尾,也就是最后一个musiclist.getCount() - 1
                    musiclist.smoothScrollToPosition(musiclist.getCount() - 1);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(musiclist.getCount() - 1, null, null),
                            musiclist.getCount() - 1,
                            musiclist.getItemIdAtPosition(musiclist.getCount() - 1));
                    break;
                case START:
                case PAUSE:
                    stop();
                    musiclist.smoothScrollToPosition(musiclist.getCount() - 1);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(musiclist.getCount() - 1, null, null),
                            musiclist.getCount() - 1,
                            musiclist.getItemIdAtPosition(musiclist.getCount() - 1));
                    break;
            }
        }

    }

 

下一首onNextClick():

    //下一首
    public void onNextClick(View v) {
        next();
    }

    private void next() {
        if (currPosition != musiclist.getCount()-1) {
            switch (currState) {
                case IDLE:
                    musiclist.smoothScrollToPosition(currPosition + 1);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(currPosition + 1, null, null),
                            currPosition + 1,
                            musiclist.getItemIdAtPosition(currPosition + 1));
                    break;
                case START:
                case PAUSE:
                    stop();
                    musiclist.smoothScrollToPosition(currPosition + 1);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(currPosition + 1, null, null),
                            currPosition + 1,
                            musiclist.getItemIdAtPosition(currPosition + 1));
                    break;
            }
        //如果currPosition等于musiclist.getCount() - 1,也就是列表最后一个
        } else {
            switch (currState) {
                case IDLE:
                    //那么,设置滚动到列表首,也就是第一首下标为0
                    musiclist.smoothScrollToPosition(0);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(0, null, null),
                            0,
                            musiclist.getItemIdAtPosition(0));
                    break;
                case START:
                case PAUSE:
                    stop();
                    musiclist.smoothScrollToPosition(0);
                    musiclist.performItemClick(
                            musiclist.getAdapter().getView(0, null, null),
                            0,
                            musiclist.getItemIdAtPosition(0));
                    break;
            }
        }
    }

 

performItemClick源码

    public boolean performItemClick(View view, int position, long id) {
        if (mOnItemClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            mOnItemClickListener.onItemClick(this, view, position, id);
            if (view != null) {
                view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            }
            return true;
        }
 
        return false;
    } 

 

点击上一首或者下一首时,如果有其他在播放,终止其mediaPlayer,stop():

    //终止当前播放
    private void stop() {
        initState();
        mediaPlayer.stop();
        currState = IDLE;
    }

 

initState():

    //更新界面
    private void initState() {
        musiclistCurrentTime.setText("00:00");
        musiclistTotalTime.setText("00:00");
        flag = false;
        seekBar.setProgress(0);
        musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause));
    }

 

最终效果展示:

 

posted @ 2020-04-15 22:21  NUMM  阅读(1395)  评论(0编辑  收藏  举报