欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android本地音乐播放器

程序员文章站 2022-05-29 22:19:22
...

一,需要知识点:

1,Drawerlayout侧滑菜单实现。

2,ListView的使用及其点击事件的监听。

3,如何打开文件管理器选择特定文件,并获取被选文件的路径。

4,Service服务的使用。

5,如何使用新线程动态给UI线程发送消息更新Seekbar进度。

6,MediaPlayer的使用。

二,写代码时遇到的坑。

1,在新开的线程中是不能修改UI组件的,否则程序会崩溃,写的时候因为这个程序一直崩溃我也很绝望。

2,找如何打开系统浏览器找了半天。


布局文件(线性布局):

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawerlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg3"
    >
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    >
    <TextView
        android:id="@+id/imageview"
        android:layout_width="match_parent"
        android:layout_height="300dp"
         />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="当前正在播放:无歌曲"
        android:textSize="25sp"
        android:id="@+id/nowplayText"
        android:lines="2"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:layout_marginTop="10dp"
        >
        <TextView
            android:id="@+id/leftText"
            android:text="00:00"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:layout_marginLeft="5dp"
            />
        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="272dp"
            android:layout_height="match_parent"

            />
        <TextView
            android:id="@+id/rightText"
            android:text="99:99"
            android:gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal"
        android:gravity="center"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/open_btn"
            android:text="打开"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/stop_btn"
            android:text="停止"
            />
        <ImageButton
            android:id="@+id/imagebutton_exit"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            android:background="@drawable/exit_btn"
            />


    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/pre_btn"
            android:text="上一曲"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/start_btn"
            android:text="播放"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/next_btn"
            android:text="下一曲"
            />
        <Button
            android:id="@+id/menu_btn"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="菜单" />

    </LinearLayout>


</LinearLayout>
<LinearLayout
    android:layout_width="200dp"
    android:layout_height="match_parent"
    android:layout_gravity="end"
    android:id="@+id/leftview"
    >
    <ListView
        android:id="@+id/listview"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:layout_gravity="end"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="@drawable/bg4"

        >
    </ListView>


</LinearLayout>

</android.support.v4.widget.DrawerLayout>


MainActivity文件:

package com.example.my.musicplayer;

import android.Manifest;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.MediaStore;
import android.support.annotation.RequiresApi;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity implements View.OnClickListener,SeekBar.OnSeekBarChangeListener,AdapterView.OnItemClickListener {
    private Button start_btn,pre_btn,next_btn,open_btn,stop_btn,menu_btn;
    private TextView nowText,leftText,rightText,imageview;
    public SeekBar seekBar;
    private ListView listView=null;
    private ArrayAdapter<String>adapter;
    private MediaPlayer mediaPlayer;
    private MusicService musicService;
    private String path;
    public Handler handler;
    private ImageButton exit_btn;
    private int second;
    private int nowsecond;
    private CFilelist musicfile=new CFilelist();
    private int nowindex=0;
    private LinearLayout left_view=null;
    private DrawerLayout mDrawerLayout=null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        menu_btn= (Button) findViewById(R.id.menu_btn);
        menu_btn.setOnClickListener(this);
        start_btn= (Button) findViewById(R.id.start_btn);
        start_btn.setOnClickListener(this);
        pre_btn= (Button) findViewById(R.id.pre_btn);
        pre_btn.setOnClickListener(this);
        next_btn= (Button) findViewById(R.id.next_btn);
        next_btn.setOnClickListener(this);
        open_btn= (Button) findViewById(R.id.open_btn);
        open_btn.setOnClickListener(this);
        stop_btn= (Button) findViewById(R.id.stop_btn);
        stop_btn.setOnClickListener(this);
        seekBar= (SeekBar) findViewById(R.id.seekbar);
        seekBar.setOnSeekBarChangeListener(this);
        nowText= (TextView) findViewById(R.id.nowplayText);
        leftText= (TextView) findViewById(R.id.leftText);
        rightText= (TextView) findViewById(R.id.rightText);
        exit_btn= (ImageButton) findViewById(R.id.imagebutton_exit);
        exit_btn.setOnClickListener(this);
        listView= (ListView) findViewById(R.id.listview);
        adapter=new ArrayAdapter<String>(this,R.layout.support_simple_spinner_dropdown_item,musicfile.listname);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);
        imageview= (TextView) findViewById(R.id.imageview);
        listView.getBackground().setAlpha(150);
        left_view= (LinearLayout) findViewById(R.id.leftview);
        mDrawerLayout= (DrawerLayout) findViewById(R.id.drawerlayout);

        isGrantExternalRW(this);
        //通过绑定方式启动Service
        bindMusicService();
        handler=new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 0x123) {
                    if(mediaPlayer.isPlaying()) {
                        seekBar.setMax(second);
                        seekBar.setProgress(nowsecond);
                        leftText.setText(change(nowsecond));
                        rightText.setText(change(second));
                    }
                }
            }
        };
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if(mediaPlayer!=null) {
                    second = musicService.mediaPlayer.getDuration();
                    nowsecond = musicService.mediaPlayer.getCurrentPosition();
                    //Toast.makeText(MainActivity.this,second+"",Toast.LENGTH_SHORT).show();
                    handler.sendEmptyMessage(0x123);
                }
            }
        },0,1000);
    }
    private void bindMusicService()
    {
        Intent intent=new Intent(this,MusicService.class);
        //启动服务
        startService(intent);
        //绑定服务
        bindService(intent,serviceConnection,this.BIND_AUTO_CREATE);
    }
private ServiceConnection serviceConnection=new ServiceConnection() {
    //回调函数;1,连接服务时调用2,取消服务连接
    //连接服务时调用
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
       //获取service服务
        musicService=((MusicService.MyBinder)iBinder).getService();
        //获取mediaplayer
        mediaPlayer=musicService.mediaPlayer;

        path=musicService.path;
        //初始化进度条总时间
        int second=musicService.mediaPlayer.getDuration();
        second/=1000;
        rightText.setText("0"+second/60+":"+second%60);
    }
    //取消服务连接时调用
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        musicService=null;
    }
};

    //安卓6.0以上读取内存文件需要手动请求权限
    public static boolean isGrantExternalRW(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            activity.requestPermissions(new String[]{
                    //内存卡读写
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, 1);
            return false;
        }
        return true;
    }
    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1) {
            for (int i = 0; i < permissions.length; i++) {
                String permission = permissions[i];
                int grantResult = grantResults[i];
                if (permission.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) {
                    if (grantResult == PackageManager.PERMISSION_GRANTED) {
                        //授权成功后的逻辑
                    } else {
                        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                    }
                }
            }
        }
    }



    @Override
    public void onClick(View view) {
        switch (view.getId())
        {
            case R.id.start_btn: {
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    //mediaPlayer.prepareAsync();
                    start_btn.setText("播放");
                    //objectAnimator.pause();
                } else {
                    mediaPlayer.start();
                    start_btn.setText("暂停");
                }
                break;
            }
            case R.id.stop_btn:
            {
                if(mediaPlayer!=null) {
                    musicService.tag = false;
                    mediaPlayer.stop();
                    mediaPlayer.prepareAsync();
                    start_btn.setText("播放");
                }
                    mediaPlayer.seekTo(0);
                    seekBar.setProgress(0);
                break;
            }
            case R.id.imagebutton_exit:
            {
                unbindService(serviceConnection);
                Intent intent=new Intent(this,MusicService.class);
                stopService(intent);
                try {
                    MainActivity.this.finish();
                }catch (Exception e)
                {
                    e.printStackTrace();
                }
                break;
            }
            case R.id.pre_btn:
            {
                if(musicfile.pathlist.size()==0)
                {
                    Toast.makeText(this, "文件为空不能播放,请点击打开添加歌曲再进行播放", Toast.LENGTH_SHORT).show();
                    break;
                }
                nowindex--;
                if(nowindex<0)
                {
                    if(musicfile.pathlist.size()!=0)
                   nowindex=musicfile.pathlist.size()-1;
                }
                if(mediaPlayer!=null) {
                    mediaPlayer.stop();
                    mediaPlayer.reset();
                    seekBar.setProgress(0);
                    start_btn.setText("播放");
                    //Toast.makeText(this, "当前播放路径为:"+musicfile.pathlist.get(nowindex), Toast.LENGTH_SHORT).show();
                    try {
                        mediaPlayer.setDataSource(musicfile.pathlist.get(nowindex));
                        mediaPlayer.prepareAsync();
                        nowText.setText("当前正在播放:"+musicfile.listname.get(nowindex));
                        //mediaPlayer.start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            }
            case R.id.next_btn:
            {
                if(musicfile.pathlist.size()==0)
                {
                    Toast.makeText(this, "文件为空不能播放,请点击打开添加歌曲再进行播放", Toast.LENGTH_SHORT).show();
                    break;
                }
                nowindex++;
                if(nowindex>=musicfile.pathlist.size())
                {
                    if(musicfile.pathlist.size()!=0)
                        nowindex=0;
                }
                if(mediaPlayer!=null) {
                    mediaPlayer.stop();
                    mediaPlayer.reset();
                    seekBar.setProgress(0);
                    start_btn.setText("播放");
                    //Toast.makeText(this, "当前播放路径为:"+musicfile.pathlist.get(nowindex), Toast.LENGTH_SHORT).show();
                    try {
                        mediaPlayer.setDataSource(musicfile.pathlist.get(nowindex));
                        mediaPlayer.prepareAsync();
                        nowText.setText("当前正在播放:"+musicfile.listname.get(nowindex));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            }
            case R.id.open_btn:
            {
                //打开系统自带文件浏览器
                Intent i=new Intent(Intent.ACTION_GET_CONTENT);
                i.setType("audio/*");
                i.addCategory(Intent.CATEGORY_OPENABLE);
                this.startActivityForResult(i,1);
                break;
            }
            case R.id.menu_btn:
            {
                if(!mDrawerLayout.isDrawerOpen(left_view))
                {
                    mDrawerLayout.openDrawer(left_view);
                }
                break;
            }

        }
    }
    //重写返回键的功能
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //设置后台继续播放音乐
        if(keyCode==KeyEvent.KEYCODE_BACK)
        {
            moveTaskToBack(false);
        }
        return super.onKeyDown(keyCode, event);
    }

    private String change(int xx)
    {
        xx/=1000;
        String arg=new String();
        int t=xx/60;
        if(t<10){
            arg+="0";
        }
        arg+=t;
        t=xx%60;
        arg+=":";
        if(t<10){
            arg+="0";
        }
        arg+=t;
        return arg;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        leftText.setText(change(seekBar.getProgress()));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        if(mediaPlayer!=null)
        {
            //如果音乐正在播放才更新播放进度
            if(mediaPlayer.isPlaying()) {
                mediaPlayer.seekTo(seekBar.getProgress());
            }
            else
            {
                seekBar.setProgress(0);
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode==1)
        {
            //将Url转换成真实路径
            Uri uri = data.getData();
            String[] proj = { MediaStore.Images.Media.DATA };
            Cursor actualimagecursor = getContentResolver().query(uri,proj,null,null,null);
            int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            actualimagecursor.moveToFirst();
            String img_path = actualimagecursor.getString(actual_image_column_index);
            //Toast.makeText(this, img_path, Toast.LENGTH_LONG).show();
            musicfile.pathlist.add(img_path);
            //从文件末尾提取歌名
            String[] st=img_path.split("/");
            musicfile.listname.add(st[st.length-1]);
            Toast.makeText(this, "歌曲添加成功,点击下一首即可播放", Toast.LENGTH_SHORT).show();
            //更改ListView信息
            adapter.notifyDataSetChanged();
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
        mediaPlayer.stop();
        mediaPlayer.reset();
        try {
            mediaPlayer.setDataSource(musicfile.pathlist.get(i));
            mediaPlayer.prepareAsync();
            nowText.setText("当前正在播放:"+musicfile.listname.get(i));
            start_btn.setText("播放");
            mDrawerLayout.closeDrawer(left_view);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
class CFilelist
{
    public List<String>pathlist=new ArrayList<String>();
    public List<String>listname=new ArrayList<String>();
}


MusicService文件:
package com.example.my.musicplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

public class MusicService extends Service {
    public MediaPlayer mediaPlayer;
    public boolean tag=false;
    public MyBinder binder=new MyBinder();
    //内部储存卡路径
    public String path= Environment.getExternalStorageDirectory().getPath();
    public MusicService() {

        mediaPlayer=new MediaPlayer();
        try
        {
            mediaPlayer.setDataSource(path+"/music.mp3");
            mediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return binder;
        //throw new UnsupportedOperationException("Not yet implemented");
    }

    public class MyBinder extends Binder
    {
        MusicService getService()
        {
            return MusicService.this;
        }
    }
}


播放器功能:

可以添加本地歌曲进行播放,添加的歌曲再右边侧滑栏显示,点击即可播放。

实现效果:


Android本地音乐播放器Android本地音乐播放器