简单的音乐播放器(老师布置的小作业)

我收到许多小伙伴的私信,所以花了点时间重新整理了一下供大家学习,完整项目在博客底部获取

请看效果 

music演示视频

开发工具:Android Studio 

涉及到的类和涉及到的layout

 

悄悄说一句:有的命名不规范,勿怪。

登录界面

登录界面 activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/indianred"
    android:fitsSystemWindows="true"
    android:gravity="center"
    android:orientation="vertical">


    <LinearLayout
        android:id="@+id/ll1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="250dp"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@+id/ll2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">


        <ImageView
            android:id="@+id/iv_music4"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@drawable/erji"
            android:textColor="#FFFFFF"
            android:textSize="20sp" />


        <TextView
            android:id="@+id/tvDoThing"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:alpha="1"
            android:text="我爱听歌"
            android:textColor="#FFFFFF"
            android:textSize="28sp"
            android:textStyle="bold" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@+id/ll3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ll1">

        <ImageView
            android:id="@+id/textView4"
            android:layout_width="22dp"
            android:layout_height="22dp"
            android:layout_marginEnd="20dp"
            android:src="@drawable/touxiang" />

        <EditText
            android:id="@+id/num"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="请输入用户名"
            android:inputType="textPersonName"
            android:textColor="#FFFFFF"
            android:textColorHint="#50FFFFFF"
            android:theme="@style/MyEditText" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@+id/ll4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ll2">

        <ImageView
            android:id="@+id/textView5"
            android:layout_width="22dp"
            android:layout_height="22dp"
            android:layout_marginEnd="20dp"
            android:src="@drawable/mima" />

        <EditText
            android:id="@+id/pw"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="请输入密码"
            android:inputType="textPassword"
            android:textColor="#FFFFFF"
            android:textColorHint="#50FFFFFF"
            android:theme="@style/MyEditText" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="300dp"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ll3">

        <Button
            android:id="@+id/button11"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:layout_marginEnd="50dp"
            android:background="@drawable/btn1"
            android:onClick="two6"
            android:text="登录"
            android:textColor="#FFFFFF" />

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:background="@drawable/btn1"
            android:onClick="two5"
            android:text="注册"
            android:textColor="#FFFFFF" />
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

登录界面 LoginActivity 

package com.example.mymusic;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.widget.Toolbar;

public class LoginActivity extends Activity {

    private static final String TAG = "lzs";
    Button btn;
    EditText num;
    EditText pw;
    String num1;
    String password;
    Intent intent1;
    private NotificationManager notificationManager;
    private Notification notification;
    private SharedPreferences sp;


    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        num = findViewById(R.id.num);
        pw = findViewById(R.id.pw);
        sp = getSharedPreferences("mrsoft",MODE_PRIVATE);
    }

    @Override
    public void onStart()
    {
        super.onStart();
    }

    @Override
    public void onResume()
    {
        super.onResume();
    }

    @Override
    public void onPause()
    {
        super.onPause();
    }

    @Override
    public void onStop()
    {
        super.onStop();
    }

    @Override
    public void onRestart()
    {
        super.onRestart();
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }



    //重写onActivityResult
    @Override
    protected void onActivityResult(int requestCode,int resultCode,Intent data){
        super.onActivityResult(requestCode,resultCode,data);
        if(requestCode == 1 && resultCode == 1){
            String returnData = data.getStringExtra("data");
            TextView tvDo = (TextView) findViewById(R.id.tvDoThing);
            tvDo.setText(returnData);
        }
    }
    //注册
    public void two5(View view)
    {
        boolean b = num.getText().toString()==null;
        boolean c= pw.getText()==null;
        Log.i(TAG, "two5: " + b);
        Log.i(TAG, "two5: "+c);
        Log.i(TAG, "two5: "+num.getText());
        Log.i(TAG, "two5: "+pw.getText());
        if(!TextUtils.isEmpty(num.getText()) && !TextUtils.isEmpty(pw.getText()))
        {
            String in_username = num.getText().toString(); //获取输入的账号
            String in_password = pw.getText().toString(); //获取输入的密码
            SharedPreferences.Editor editor = sp.edit(); //获取Editer对象
            editor.putString("username",in_username); //保存账号
            editor.putString("password",in_password); //保存密码
            editor.commit();
            Toast.makeText(LoginActivity.this,"注册成功",Toast.LENGTH_SHORT).show();
            //捕获数据并封装数据
            num1 = num.getText().toString();
            password = pw.getText().toString();
            intent1 = new Intent(this, RegisteredActivity.class);
            intent1.putExtra("id",num1);
            intent1.putExtra("ps",password);
            startActivityForResult(intent1,1);
        }else {
            Toast.makeText(LoginActivity.this,"账号或密码填写错误",Toast.LENGTH_SHORT).show();
        }

    }

    //登录
    public void two6(View view) {

        if(!TextUtils.isEmpty(num.getText()) && !TextUtils.isEmpty(pw.getText())){
            String username = sp.getString("username","null");//获取账号
            String password = sp.getString("password","null");//获取密码
            String in_username = num.getText().toString(); //获取输入的账号
            String in_password = pw.getText().toString(); //获取输入的密码
            SharedPreferences.Editor editor = sp.edit(); //获取Editer对象
            if(in_username.equals(username) && in_password.equals(password)){
                //跳转
                Intent it1 = new Intent(LoginActivity.this, MusicActivity.class);
                startActivity(it1);
                Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_SHORT).show();
                finish();
            }else {
                Toast.makeText(LoginActivity.this,"账号或密码错误",Toast.LENGTH_SHORT).show();
            }
        }else {
            Toast.makeText(LoginActivity.this,"账号或密码填写错误",Toast.LENGTH_SHORT).show();
        }

    }


}

注册页面

注册页面 activity_registered.xml 

<?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:background="@color/indianred"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="13"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tishi"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/white"
                android:text="123"
                android:textSize="14sp" />
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="13"
        android:gravity="center"
        android:orientation="horizontal">


        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/btn1"
            android:textColor="@color/white"
            android:onClick="three"
            android:text="现在登录" />


    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="13"
        android:gravity="center"
        android:orientation="horizontal" />


</LinearLayout>

注册页面 RegisteredActivity 

package com.example.mymusic;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class RegisteredActivity extends AppCompatActivity {

    TextView tv;
    private static final String TAG="lzs";

    @SuppressLint("SetTextI18n")
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_registered);
        tv=findViewById(R.id.tishi);

        Intent intent = getIntent();

        String id = intent.getStringExtra("id");
        String ps = intent.getStringExtra("ps");
        tv.setText("您好,欢迎您!"+id+"您的密码是:"+ps);


    }

    @Override
    public void onStart()
    {
        super.onStart();
    }

    @Override
    public void onResume()
    {
        super.onResume();
    }

    @Override
    public void onPause()
    {
        super.onPause();
    }

    @Override
    public void onStop()
    {
        super.onStop();
    }

    @Override
    public void onRestart()
    {
        super.onRestart();
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }

    //隐式启动
    public void three(View view){
        Intent intent = new Intent(RegisteredActivity.this, LoginActivity.class);
        startActivity(intent);
        finish();
    }


}

音乐播放列表页面

  

 music_list.xml

<?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:background="@color/indianred"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:gravity="center"
        android:layout_height="50dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="本地音乐"
            android:textColor="@color/white"
            android:textSize="25sp" />

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:id="@+id/local_music_bottomlayout"
            android:layout_width="match_parent"
            android:layout_height="70dp"
            android:layout_alignParentBottom="true">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="0.5dp" />

            <ImageView
                android:id="@+id/local_music_icon"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_centerVertical="true"
                android:layout_marginStart="10dp"
                android:src="@drawable/yinxiang" />

            <TextView
                android:id="@+id/local_music_bottom_tv_song"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:layout_toEndOf="@id/local_music_icon"
                android:text="歌曲名称"
                android:textSize="16sp"
                android:textColor="@color/white"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/local_music_bottom_tv_singer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/local_music_bottom_tv_song"
                android:layout_alignStart="@id/local_music_bottom_tv_song"
                android:layout_marginTop="10dp"
                android:textColor="@color/white"
                android:text="歌曲名称"
                android:textSize="12sp" />

        </RelativeLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/local_music_rv1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/local_music_bottomlayout">

        </androidx.recyclerview.widget.RecyclerView>
    </RelativeLayout>


</LinearLayout>

 每首音乐的 item样式item_local_music.xml(可以根据兴趣设置自己的样式)

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginRight="10dp"
    android:layout_marginLeft="10dp"
    android:layout_marginTop="10dp"
    app:contentPadding="10dp"
    app:cardCornerRadius="10dp"
    app:cardElevation="1dp"
    app:cardBackgroundColor="@color/pink">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp">
        <TextView
            android:id="@+id/item_local_music_num"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="1"
            android:layout_centerVertical="true"
            android:textSize="24sp"
            android:textStyle="bold"/>
        <TextView
            android:id="@+id/item_local_music_song"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_toRightOf="@id/item_local_music_num"
            android:singleLine="true"
            android:layout_marginLeft="20dp"/>

        <TextView
            android:id="@+id/item_local_music_singer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:layout_below="@id/item_local_music_song"
            android:layout_alignLeft="@id/item_local_music_song"
            android:layout_marginTop="10dp"
            android:textSize="14sp"
            android:textColor="#888"/>

        <TextView
            android:id="@+id/item_local_music_line"
            android:layout_width="2dp"
            android:layout_height="18dp"
            android:background="#888"
            android:layout_toRightOf="@id/item_local_music_singer"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_alignTop="@id/item_local_music_singer"/>

        <TextView
            android:id="@+id/item_local_music_album"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:layout_toRightOf="@id/item_local_music_line"
            android:layout_alignTop="@id/item_local_music_singer"
            android:textSize="14sp"
            android:textColor="#888"
            android:ellipsize="end"
            android:singleLine="true"/>
        <TextView
            android:id="@+id/item_local_music_durtion"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/item_local_music_singer"
            android:layout_alignParentRight="true"
            android:text=""
            android:textSize="14sp"
            android:textColor="#888"/>
    </RelativeLayout>
</androidx.cardview.widget.CardView>

 到此我把音乐播放列表的布局都讲完了,接下来是它们的逻辑。

MusicActivity 
package com.example.mymusic;


import android.Manifest;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
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.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MusicActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "Test";
    public static int icons = R.drawable.iuyuantu;
    private ImageView btn_music;
    RecyclerView musicRv;
    //数据源
    private List<LocalMusicBean> mDatas;
    private LocalMusicAdapter adapter;
    private MediaPlayer mediaPlayer;
    public int id = -1;
    private int num = -2;
    private TextView t1, t2;
    private MusicPlayService.MusicControl musicControl;
    MyServiceConn conn;
    Intent intent;
    private boolean isUnbind = false;//记录服务是否被解绑


    private int mStartY = 0;
    private int mDiffY = 0;
    private int mDiffY2 = 0;
    private VelocityTracker mVelocityTracker;
    private int initialVelocity;

    private PopupWindow mPop;

    private static SeekBar sb;
    private ObjectAnimator animator;
    private ImageView iv_music;
    private static TextView tv_progress, tv_total, song_name, songer_name;
    private ImageView play, last, next, pull_down, btn_xuhuang;
    private final int mRequestCode = 100;
    //需要申请两个权限,SD读写
    //1、首先声明一个数组permissions,将需要的权限都放在里面
    String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
    //2、创建一个mPermissionList,逐个判断哪些权限未授予,未授予的权限存储到mPerrrmissionList中
    List<String> mPermissionlist = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.music_list);

        //一般情况下在触发某个事件的时候再请求动态权限,比如点击事件!
        if (Build.VERSION.SDK_INT >= 23) {//6.0才用动态权限
            initPermission();
        }

        intent = new Intent(this, MusicPlayService.class);//创建意图对象
        conn = new MyServiceConn();                       //创建服务连接对象
        bindService(intent, conn, BIND_AUTO_CREATE);  //绑定服务
        // 可能初始化一个新实例的默认值的成员
        musicRv = findViewById(R.id.local_music_rv1);
        mediaPlayer = new MediaPlayer();
        mDatas = new ArrayList<>();
        t1 = findViewById(R.id.local_music_bottom_tv_song);
        t2 = findViewById(R.id.local_music_bottom_tv_singer);
        btn_music = findViewById(R.id.local_music_icon);
        btn_music.setOnClickListener(this);
        //创建适配器对象
        adapter = new LocalMusicAdapter(MusicActivity.this, mDatas);
        musicRv.setAdapter(adapter);
        //设置布局管理器
        LinearLayoutManager layoutManager = new LinearLayoutManager(MusicActivity.this, LinearLayoutManager.VERTICAL, false);
        musicRv.setLayoutManager(layoutManager);
        //加载本地数据源
        loadLocalMusicData();
        //设置每一项的点击事件
        setEventListener();


    }


    //权限判断和申请
    private void initPermission() {
        mPermissionlist.clear();//清空没有通过的权限

        //逐个判断你要的权限是否已经通过
        for (int i = 0; i < permissions.length; i++) {
            if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
                mPermissionlist.add(permissions[i]);//添加还未授予的权限
            }
        }

        //申请权限
        if (mPermissionlist.size() > 0) {//有权限没有通过,需要申请
            ActivityCompat.requestPermissions(this, permissions, mRequestCode);
        }

    }

    //请求权限后回调的方法
    //参数: requestCode  是我们自己定义的权限请求码
    //参数: permissions  是我们请求的权限名称数组
    //参数: grantResults 是我们在弹出页面后是否允许权限的标识数组,数组的长度对应的是权限名称数组的长度,数组的数据0表示允许权限,-1表示我们点击了禁止权限
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        boolean hasPermissionDismiss = false;//有权限没有通过
        if (mRequestCode == requestCode) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] == -1) {
                    hasPermissionDismiss = true;
                }
            }
            //如果有权限没有被允许
            if (hasPermissionDismiss) {
                showPermissionDialog();//跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问
            }
        }
    }

    /**
     * 不再提示权限时的展示对话框
     */
    AlertDialog mPermissionDialog;

    private void showPermissionDialog() {
        if (mPermissionDialog == null) {
            mPermissionDialog = new AlertDialog.Builder(this)
                    .setMessage("已禁用权限,请手动授予")
                    .setCancelable(false)
                    .setPositiveButton("设置", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            cancelPermissionDialog();

                            //跳转到设置权限界面
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            Uri uri = Uri.fromParts("package", MusicActivity.this.getPackageName(), null);
                            intent.setData(uri);
                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                            overridePendingTransition(0, 0);
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //关闭页面或者做其他操作
                            finish();
                        }
                    })
                    .create();
        }
        mPermissionDialog.show();
    }

    //关闭对话框
    private void cancelPermissionDialog() {
        mPermissionDialog.cancel();
    }


    @SuppressLint({"ClickableViewAccessibility", "SuspiciousIndentation"})
    private void initPopupWindow() {
        mPop = new PopupWindow();
        mPop.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
        mPop.setHeight(WindowManager.LayoutParams.MATCH_PARENT);
        mPop.setBackgroundDrawable(null);
        View mContentView = LayoutInflater.from(this).inflate(R.layout.touchable_pop, null);
        ConstraintLayout mIvTop = mContentView.findViewById(R.id.ct_ly);
        mPop.setAnimationStyle(R.style.popwin_anim_style);
        mPop.setContentView(mContentView);
        mPop.setClippingEnabled(false);
        mPop.setFocusable(true);
        mPop.showAtLocation(
                getWindow().getDecorView(), Gravity.BOTTOM, WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT);

        play = mContentView.findViewById(R.id.btn_play);
        last = mContentView.findViewById(R.id.btn_last);
        next = mContentView.findViewById(R.id.btn_next);
        song_name = mContentView.findViewById(R.id.song_name);
        songer_name = mContentView.findViewById(R.id.songer_name);
        btn_xuhuang = mContentView.findViewById(R.id.btn_xuhuang);

        tv_progress = mContentView.findViewById(R.id.tv_progress);
        tv_total = mContentView.findViewById(R.id.tv_total);
        pull_down = mContentView.findViewById(R.id.pull_down);

        iv_music = mContentView.findViewById(R.id.iv_music);
        iv_music.setImageResource(MusicActivity.icons); //设置专辑图片

        animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
        animator.setDuration(10000);//动画旋转一周的时间为10秒
        animator.setInterpolator(new LinearInterpolator());//匀速
        animator.setRepeatCount(-1);//-1表示设置动画无限循环;
        musicControl.setLooping(false);
        if (musicControl.isPlay()) {
            play.setImageResource(R.drawable.play1);
            song_name.setText(mDatas.get(id).getSong());
            songer_name.setText(mDatas.get(id).getSinger());
            animator.start();
        } else {
            animator.pause();
        }

        btn_xuhuang.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (musicControl.isLooping()) {
                    btn_xuhuang.setImageResource(R.drawable.bofan1);
                    musicControl.setLooping(false);
                } else {
                    btn_xuhuang.setImageResource(R.drawable.single);
                    musicControl.setLooping(true);
                }
            }
        });

        pull_down.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPop.dismiss();
                animator.pause();
            }
        });

        play.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (id == -1) {
                    if (num >= 0) {
                        id = 0;
                        play.setImageResource(R.drawable.play1);
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        musicControl.play(mDatas.get(id).getPath());
                        animator.start();


                    } else {
                        Toast.makeText(MusicActivity.this, "没有可播放的歌曲!", Toast.LENGTH_SHORT).show();
                    }

                } else {
                    if (musicControl.isPlay()) {
                        play.setImageResource(R.drawable.pause1);
                        musicControl.pausePlay();
                        animator.pause();
                    } else {
                        play.setImageResource(R.drawable.play1);
                        musicControl.continuePlay();
                        if (animator.isPaused())
                            animator.resume();
                        else {
                            animator.start();
                        }

                    }
                }
            }
        });

        last.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (id == 0) {
                    Toast.makeText(MusicActivity.this, "已经是第一首了,没有上一曲!", Toast.LENGTH_SHORT).show();
                } else if (id < 0) {
                    Toast.makeText(MusicActivity.this, "没有可播放的歌曲!", Toast.LENGTH_SHORT).show();
                } else {
                    if (musicControl.isPlay()) {
                        animator.pause();
                        animator.clone();
                        id--;
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        musicControl.play(mDatas.get(id).getPath());
                        animator.start();
                    } else {
                        animator.pause();
                        animator.clone();
                        play.setImageResource(R.drawable.play1);
                        id--;
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        musicControl.play(mDatas.get(id).getPath());
                        animator.start();

                    }
                }
            }
        });

        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (id >= 0 && id >= num) {
                    Toast.makeText(MusicActivity.this, "已经是最后一首了,没有下一曲!", Toast.LENGTH_SHORT).show();
                } else if (id < 0) {
                    Toast.makeText(MusicActivity.this, "没有可播放的歌曲!", Toast.LENGTH_SHORT).show();
                } else {
                    if (musicControl.isPlay()) {
                        animator.pause();
                        animator.clone();
                        id++;
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        musicControl.play(mDatas.get(id).getPath());
                        animator.start();
                    } else {
                        animator.pause();
                        animator.clone();
                        id++;
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        play.setImageResource(R.drawable.play1);
                        musicControl.play(mDatas.get(id).getPath());
                        animator.start();
                    }
                }
            }
        });

        sb = mContentView.findViewById(R.id.sb);
        //为滑动条添加事件监听
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                //进度条改变时,会调用此方法
                if (progress == seekBar.getMax()) {//当滑动条到末端时,结束动画
                    animator.pause();//停止播放动画
                }

                if (progress == seekBar.getMax() && !musicControl.isLooping()) {
                    animator.start();
                    Log.e("Test", id + " ======== " + num);
                    if (id >= 0 && id >= num) {
                        id = 0;
                        play.setImageResource(R.drawable.play1);
                        song_name.setText(mDatas.get(id).getSong());
                        songer_name.setText(mDatas.get(id).getSinger());
                        t1.setText(mDatas.get(id).getSong());
                        t2.setText(mDatas.get(id).getSinger());
                        musicControl.play(mDatas.get(id).getPath());
                    } else if (id < 0) {
                        Toast.makeText(MusicActivity.this, "没有可播放的歌曲!", Toast.LENGTH_SHORT).show();
                    } else if (id != 0) {
                        if (musicControl.isPlay()) {
                            id++;
                            song_name.setText(mDatas.get(id).getSong());
                            songer_name.setText(mDatas.get(id).getSinger());
                            t1.setText(mDatas.get(id).getSong());
                            t2.setText(mDatas.get(id).getSinger());
                            musicControl.play(mDatas.get(id).getPath());

                        } else {
                            id++;
                            song_name.setText(mDatas.get(id).getSong());
                            songer_name.setText(mDatas.get(id).getSinger());
                            t1.setText(mDatas.get(id).getSong());
                            t2.setText(mDatas.get(id).getSinger());
                            play.setImageResource(R.drawable.play1);
                            musicControl.play(mDatas.get(id).getPath());

                        }
                    }
                } else if (progress == seekBar.getMax() && musicControl.isPlay()) {
                    play.setImageResource(R.drawable.play1);
                    song_name.setText(mDatas.get(id).getSong());
                    songer_name.setText(mDatas.get(id).getSinger());
                    t1.setText(mDatas.get(id).getSong());
                    t2.setText(mDatas.get(id).getSinger());
                    animator.start();
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {//滑动条开始滑动时调用
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {//滑动条停止滑动时调用
                //根据拖动的进度改变音乐播放进度
                int progress = seekBar.getProgress();//获取seekBar的进度
                musicControl.seekTo(progress);//改变播放进度
            }
        });

        mIvTop.setOnTouchListener((v, event) -> {
            int sign = event.getAction();

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(event);

            switch (sign) {
                case MotionEvent.ACTION_DOWN:
                    mStartY = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mDiffY = (int) event.getRawY() - mStartY;
                    mDiffY2 = mDiffY;


                    // 限制方向
                    if (mDiffY < 0) mDiffY = 0;
                    mPop.update(0, -mDiffY, -1, -1, true);
                    break;
                case MotionEvent.ACTION_UP:
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, ViewConfiguration.get(MusicActivity.this).getScaledMaximumFlingVelocity());
                    initialVelocity = Math.abs((int) velocityTracker.getYVelocity());

                    Log.e("Test", (int) event.getRawY() + "");
                    Log.e("Test", mStartY + "");
                    Log.e("Test", Math.abs((int) event.getRawY() - mStartY) + "");
                    Log.e("Test", mDiffY2 + "aaaaa");
                    Log.e("Test", initialVelocity + "fffff");
                    // click
                    if (Math.abs((int) event.getRawY() - mStartY) < 1000 && initialVelocity < 100) {
                        Toast.makeText(MusicActivity.this, "clicked", Toast.LENGTH_SHORT).show();
                        mPop.update(0, 0, -1, -1, true);
                    } else {
                        if (mDiffY2 > 0)
                            mPop.dismiss();
                    }
                    break;
            }
            return true;
        });
    }


    @SuppressLint("NotifyDataSetChanged")
    private void loadLocalMusicData() {
        /* 加载本地存储当中的音乐mp3文件到集合当中*/
        //1.获取ContentResolver对象
        ContentResolver resolver = MusicActivity.this.getContentResolver();
        //2.获取本地音乐存储的Uri地址
        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Log.i(TAG, "loadLocalMusicData: " + uri);
        //3 开始查询地址
        Cursor cursor = resolver.query(uri, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
        Log.i(TAG, "loadLocalMusicData: " + cursor);
        //4.遍历Cursor
        int id = 0;
        while (cursor.moveToNext()) {
            String song = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
            String type = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE));
            Log.i(TAG, "loadLocalMusicData:123   " + type);

            String singer = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
            String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
            id++;
            String sid = String.valueOf(id);
            String path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
            Log.i(TAG, "loadLocalMusicData:456   " + path);
            long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
            @SuppressLint("SimpleDateFormat") SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
            String time = sdf.format(new Date(duration));
            //将一行当中的数据封装到对象当中
            LocalMusicBean bean = new LocalMusicBean(sid, song, singer, album, time, path, "");
            mDatas.add(bean);
        }
        num = id > 0 ? id - 1 : -2;
        Log.i(TAG, "loadLocalMusicData: " + mDatas);
        //数据源变化,提示适配器更新
        adapter.notifyDataSetChanged();
    }


    private void setEventListener() {
        /* 设置每一项的点击事件*/
        adapter.setOnItemClickListener(new LocalMusicAdapter.OnItemClickListener() {
            @Override
            public void OnItemClick(View view, int position) {
                id = position;
                t1.setText(mDatas.get(position).getSong());
                t2.setText(mDatas.get(position).getSinger());
                musicControl.play(mDatas.get(id).getPath());
                initPopupWindow();
            }
        });
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.local_music_icon:
                initPopupWindow();
                break;

        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unbind(isUnbind); //解绑服务
    }


    class MyServiceConn implements ServiceConnection { //用于实现连接服务
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            musicControl = (MusicPlayService.MusicControl) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    private void unbind(boolean isUnbind) {
        if (!isUnbind) {                  //判断服务是否被解绑
            musicControl.pausePlay();//暂停播放音乐
            unbindService(conn);      //解绑服务
            stopService(intent);      //停止服务
        }
    }

    @SuppressLint("HandlerLeak")
    public static Handler handler = new Handler() {//创建消息处理器对象
        //在主线程中处理从子线程发送过来的消息
        @SuppressLint("HandlerLeak")
        @Override
        public void handleMessage(Message msg) {
            Bundle bundle = msg.getData();//获取从子线程发送过来的音乐播放进度
            int duration = bundle.getInt("duration");
            int currentPosition = bundle.getInt("currentPosition");
            sb.setMax(duration);
            sb.setProgress(currentPosition);
            //歌曲总时长
            int minute = duration / 1000 / 60;
            int second = duration / 1000 % 60;
            String strMinute = null;
            String strSecond = null;
            if (minute < 10) {//如果歌曲的时间中的分钟小于10
                strMinute = "0" + minute;//在分钟的前面加一个0
            } else {
                strMinute = minute + "";
            }
            if (second < 10) {//如果歌曲中的秒钟小于10
                strSecond = "0" + second;//在秒钟前面加一个0
            } else {
                strSecond = second + "";
            }
            tv_total.setText(strMinute + ":" + strSecond);
            //歌曲当前播放时长
            minute = currentPosition / 1000 / 60;
            second = currentPosition / 1000 % 60;
            if (minute < 10) {//如果歌曲的时间中的分钟小于10
                strMinute = "0" + minute;//在分钟的前面加一个0
            } else {
                strMinute = minute + " ";
            }
            if (second < 10) {//如果歌曲中的秒钟小于10
                strSecond = "0" + second;//在秒钟前面加一个0
            } else {
                strSecond = second + " ";
            }
            tv_progress.setText(strMinute + ":" + strSecond);
        }
    };


}



数据源类    LocalMusicBean

package com.example.mymusic;

import java.io.Serializable;

public class LocalMusicBean implements Serializable {
    private String id; //歌曲id
    private String song; //歌曲名称
    private String singer; //歌手名称
    private String album; //专辑名称
    private String duration; //歌曲时长
    private String path; //歌曲路径
    private String albumArt;  //专辑地址

    public LocalMusicBean() {
    }

    public LocalMusicBean(String id, String song, String singer, String album, String duration, String path, String albumArt) {
        this.id = id;
        this.song = song;
        this.singer = singer;
        this.album = album;
        this.duration = duration;
        this.path = path;
        this.albumArt = albumArt;
    }

    public String getAlbumArt() {
        return albumArt;
    }

    public void setAlbumArt(String albumArt) {
        this.albumArt = albumArt;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getSong() {
        return song;
    }

    public void setSong(String song) {
        this.song = song;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public String getDuration() {
        return duration;
    }

    public void setDuration(String duration) {
        this.duration = duration;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

适配器类  LocalMusicAdapter 

package com.example.mymusic;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;


import java.util.List;

public class LocalMusicAdapter extends RecyclerView.Adapter<LocalMusicAdapter.LocalMusicViewHolder>{
    Context context;
    List<LocalMusicBean> mDatas;

    OnItemClickListener onItemClickListener;

    //接口回调
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public interface OnItemClickListener{
        public void OnItemClick(View view,int position);
    }

    public LocalMusicAdapter(Context context, List<LocalMusicBean> mDatas) {
        this.context = context;
        this.mDatas = mDatas;
    }

    @NonNull
    @Override
    public LocalMusicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_local_music,parent,false);
        LocalMusicViewHolder holder = new LocalMusicViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull LocalMusicViewHolder holder, @SuppressLint("RecyclerView") final int position) {

        LocalMusicBean musicBean = mDatas.get(position);
        holder.idTv.setText(musicBean.getId());
        holder.songTv.setText(musicBean.getSong());
        holder.singerTv.setText(musicBean.getSinger());
        holder.albumTv.setText(musicBean.getAlbum());
        holder.timeTv.setText(musicBean.getDuration());

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onItemClickListener.OnItemClick(v,position);
            }
        });
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }


    class LocalMusicViewHolder extends RecyclerView.ViewHolder{
        TextView idTv,songTv,singerTv,albumTv,timeTv;
        public LocalMusicViewHolder(View itemView) {
            super(itemView);
            idTv = itemView.findViewById(R.id.item_local_music_num);
            songTv = itemView.findViewById(R.id.item_local_music_song);
            singerTv = itemView.findViewById(R.id.item_local_music_singer);
            albumTv = itemView.findViewById(R.id.item_local_music_album);
            timeTv = itemView.findViewById(R.id.item_local_music_durtion);
        }
    }
}
 

到此音乐播放列表的布局文件和类都写完了,接下来是

音乐播放界面

音乐播放界面 touchable_pop.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/ct_ly"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/indianred">

    <ImageView
        android:id="@+id/pull_down"
        android:layout_width="25dp"
        android:layout_height="25dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="25dp"
        android:layout_marginStart="25dp"
        android:src="@drawable/pull_down" />


    <ImageView
        android:id="@+id/iv_music"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:layout_constraintTop_toBottomOf="@+id/pull_down"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="100dp"
        android:src="@drawable/iuyuantu" />


    <TextView
        android:id="@+id/song_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/iv_music"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:text="歌曲名称"
        android:textColor="@color/white"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/songer_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/song_name"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:text="歌手姓名"
        android:textColor="@color/silver"
        android:textSize="10sp" />


    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/songer_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="80dp"
        android:layout_marginRight="20dp"
        android:progressTint="@color/white"
        android:thumbTint="@color/white" />


    <TextView
        android:id="@+id/tv_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/sb"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="30dp"
        android:text="00:00"
        android:textColor="@color/silver" />

    <TextView
        android:id="@+id/tv_total"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/sb"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="30dp"
        android:text="00:00"
        android:textColor="@color/silver" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/tv_total"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/btn_xuhuang"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_centerVertical="true"
            android:background="@drawable/bofan1" />

        <ImageView
            android:id="@+id/btn_last"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="35dp"
            android:layout_marginRight="35dp"
            android:src="@drawable/left" />

        <ImageView
            android:id="@+id/btn_play"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_centerVertical="true"
            android:src="@drawable/pause1" />

        <ImageView
            android:id="@+id/btn_next"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="35dp"
            android:layout_marginRight="35dp"
            android:src="@drawable/right" />

        <ImageView
            android:id="@+id/btn_bofan"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:src="@drawable/bofan" />

    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

音乐播放界面类 MusicPlayService 

package com.example.mymusic;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
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 MusicPlayService extends Service {
    private MediaPlayer player;
    private Timer timer;

    public MusicPlayService() {
    }

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

    @Override
    public void onCreate() {
        super.onCreate();
        player = new MediaPlayer();//创建音乐播放器对象
        Log.i("lzs", "456  " + player);
    }

    class MusicControl extends Binder {

        //播放歌曲
        public boolean play(String str) {//String path
            try {
                Uri uri = Uri.parse(str);
                player.reset();//重置音乐播放器
                //加载多媒体文件
                player = MediaPlayer.create(getApplicationContext(), uri);
                //player.prepare();
                player.start();//播放音乐
                addTimer();//添加计时器
            } catch (Exception e) {
                e.printStackTrace();
                player = new MediaPlayer();//创建音乐播放器对象
                Toast.makeText(getApplicationContext(), "解析该歌曲失败,请换一首歌曲!", Toast.LENGTH_SHORT).show();
            }

            return player != null;

        }

        public boolean isPlay() {
            try {
                return player.isPlaying();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        public boolean isLooping() {
            try {
                return player.isLooping();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;

        }

        public void setLooping(boolean b) {
            try {
                player.setLooping(b);
            } catch (Exception e) {
                e.printStackTrace();
            }


        }

        public void pausePlay() {
            try {
                player.pause();//暂停播放音乐
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        public void continuePlay() {
            try {
                player.start();//继续播放音乐
            } catch (Exception e) {
                e.printStackTrace();
            }


        }

        public void seekTo(int progress) {
            try {
                player.seekTo(progress);//设置音乐的播放位置
            } catch (Exception e) {
                e.printStackTrace();
            }

        }


        public void addTimer() { //添加计时器用于设置音乐播放器中的播放进度条
            try {
                if (timer == null) {
                    timer = new Timer();//创建计时器对象
                    TimerTask task = new TimerTask() {
                        @Override
                        public void run() {
                            if (player == null) return;
                            int duration = player.getDuration();//获取歌曲总时长
                            int currentPosition = player.getCurrentPosition();//获取播放进度
                            Message msg = MusicActivity.handler.obtainMessage();//创建消息对象
                            //将音乐的总时长和播放进度封装至消息对象中
                            Bundle bundle = new Bundle();
                            bundle.putInt("duration", duration);
                            bundle.putInt("currentPosition", currentPosition);
                            msg.setData(bundle);
                            //将消息发送到主线程的消息队列
                            MusicActivity.handler.sendMessage(msg);
                        }
                    };
                    //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒执行一次
                    timer.schedule(task, 5, 500);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (player == null) return;
        if (player.isPlaying()) player.stop();//停止播放音乐
        player.release();                         //释放占用的资源
        player = null;                            //将player置为空
    }
}

还有一些配置信息

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.mymusic">

    <!-- 有的权限是其他的 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyMusic">
        <activity
            android:name=".MusicActivity"
            android:exported="false">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <activity
            android:name=".LoginActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".RegisteredActivity" />

        <service
            android:name=".MusicPlayService"
            android:enabled="true"
            android:exported="true" />
    </application>

</manifest>

模块内的build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdk 28

    defaultConfig {
        applicationId "com.example.mymusic"
        minSdk 21
        targetSdk 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
//添加包路径

    //开启dataBinding功能支持
    dataBinding {
        enabled = true
    }
    // 自定义打包名称
    android.applicationVariants.all { variant ->
        variant.outputs.all {
            outputFileName = "lzs.apk"
        }
    }


    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.preference:preference:1.1.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    //添加 RecyclerView 依赖包
    implementation "androidx.recyclerview:recyclerview:1.2.1"
    //添加 ViewPager2 依赖包
    implementation 'androidx.viewpager2:viewpager2:1.0.0'

    implementation'com.synnapps:carouselview:0.1.5'

    implementation 'org.greenrobot:eventbus:3.1.1'

}

到此,所有布局文件,类和配置信息都写完了,当然里面的逻辑思路就不具体介绍了,我就大概讲讲用到了哪些方法:

1.首先是登录界面,我是用了Shared Preferences 存储(对于注册少量用户来说,建议使用

sqlite);

2.在音乐列表和音乐播放界面跳转过程中,我使用Service来实现可以后台播放;

3.音乐播放界面的音乐进度条是通过Handler消息机制实现的,每个几毫秒就向主线程发送消息,

然后修改当前进度条的值,从而达到动态的效果。

如何导入音乐文件到模拟器,因为我是去MediaStore中去查询相关音乐信息的,这里涉及到了内部存储和外部存储,大家可以去学习一下,我们无法直接将本地音乐导入到MediaStore中,只能导入到SD卡中,然后系统会帮我们把音乐信息记录到MediaStore中,所以我们可以按如下操作完成音乐文件的导入:

1.选择 sdcard 目录下的 Music 目录

 2.选择音乐文件

3.导入成功

4.重启模拟器(注意:如果重启进入安全模式的话需要再次重启)

5.重启中

6.重启后再次登录app,如下所示表示导入成功

 7.如果不显示音乐,可以再次尝试重启模拟器并卸载app重新安装

8.本项目会出现有些音乐解析不了,即 MediaPlayer.create() 返回为空,感兴趣的可以去研究或者知道的可以分享一下

Gitee链接:
https://gitee.com/lzongsh/MyMusic

链接:https://pan.baidu.com/s/14sSsvimx52qnucneSKRyew?pwd=1234 
提取码:1234

以上是我个人总结,难免会有不妥之处,欢迎指正,谢谢。

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值