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

请看效果 

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"><LinearLayoutandroid: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"><ImageViewandroid:id="@+id/iv_music4"android:layout_width="40dp"android:layout_height="40dp"android:src="@drawable/erji"android:textColor="#FFFFFF"android:textSize="20sp" /><TextViewandroid: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><LinearLayoutandroid: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"><ImageViewandroid:id="@+id/textView4"android:layout_width="22dp"android:layout_height="22dp"android:layout_marginEnd="20dp"android:src="@drawable/touxiang" /><EditTextandroid: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><LinearLayoutandroid: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"><ImageViewandroid:id="@+id/textView5"android:layout_width="22dp"android:layout_height="22dp"android:layout_marginEnd="20dp"android:src="@drawable/mima" /><EditTextandroid: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><LinearLayoutandroid: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"><Buttonandroid: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" /><Buttonandroid: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;@Overridepublic 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);}@Overridepublic void onStart(){super.onStart();}@Overridepublic void onResume(){super.onResume();}@Overridepublic void onPause(){super.onPause();}@Overridepublic void onStop(){super.onStop();}@Overridepublic void onRestart(){super.onRestart();}@Overridepublic void onDestroy(){super.onDestroy();}//重写onActivityResult@Overrideprotected 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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="13"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:orientation="horizontal"></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="4"android:gravity="center"android:orientation="horizontal"><TextViewandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="13"android:gravity="center"android:orientation="horizontal"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/btn1"android:textColor="@color/white"android:onClick="three"android:text="现在登录" /></LinearLayout><LinearLayoutandroid: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")@Overridepublic 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);}@Overridepublic void onStart(){super.onStart();}@Overridepublic void onResume(){super.onResume();}@Overridepublic void onPause(){super.onPause();}@Overridepublic void onStop(){super.onStop();}@Overridepublic void onRestart(){super.onRestart();}@Overridepublic 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"><LinearLayoutandroid:layout_width="match_parent"android:gravity="center"android:layout_height="50dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="本地音乐"android:textColor="@color/white"android:textSize="25sp" /></LinearLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:id="@+id/local_music_bottomlayout"android:layout_width="match_parent"android:layout_height="70dp"android:layout_alignParentBottom="true"><ImageViewandroid:layout_width="match_parent"android:layout_height="0.5dp" /><ImageViewandroid: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" /><TextViewandroid: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" /><TextViewandroid: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.RecyclerViewandroid: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.CardViewxmlns: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"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"><TextViewandroid: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"/><TextViewandroid: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"/><TextViewandroid: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"/><TextViewandroid: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"/><TextViewandroid: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"/><TextViewandroid: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<>();@Overrideprotected 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表示我们点击了禁止权限@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onClick(View v) {mPop.dismiss();animator.pause();}});play.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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();}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {//滑动条开始滑动时调用}@Overridepublic 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");// clickif (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.遍历Cursorint 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() {@Overridepublic 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();}});}@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.local_music_icon:initPopupWindow();break;}}@Overridepublic void onDestroy() {super.onDestroy();unbind(isUnbind); //解绑服务}class MyServiceConn implements ServiceConnection { //用于实现连接服务@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {musicControl = (MusicPlayService.MusicControl) service;}@Overridepublic 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")@Overridepublic 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) {//如果歌曲的时间中的分钟小于10strMinute = "0" + minute;//在分钟的前面加一个0} else {strMinute = minute + "";}if (second < 10) {//如果歌曲中的秒钟小于10strSecond = "0" + second;//在秒钟前面加一个0} else {strSecond = second + "";}tv_total.setText(strMinute + ":" + strSecond);//歌曲当前播放时长minute = currentPosition / 1000 / 60;second = currentPosition / 1000 % 60;if (minute < 10) {//如果歌曲的时间中的分钟小于10strMinute = "0" + minute;//在分钟的前面加一个0} else {strMinute = minute + " ";}if (second < 10) {//如果歌曲中的秒钟小于10strSecond = "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; //歌曲idprivate 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@Overridepublic 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;}@Overridepublic 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() {@Overridepublic void onClick(View v) {onItemClickListener.OnItemClick(v,position);}});}@Overridepublic 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"><ImageViewandroid: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" /><ImageViewandroid: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" /><TextViewandroid: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" /><TextViewandroid: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" /><SeekBarandroid: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" /><TextViewandroid: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" /><TextViewandroid: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" /><LinearLayoutandroid: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"><ImageViewandroid:id="@+id/btn_xuhuang"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerVertical="true"android:background="@drawable/bofan1" /><ImageViewandroid: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" /><ImageViewandroid:id="@+id/btn_play"android:layout_width="70dp"android:layout_height="70dp"android:layout_centerVertical="true"android:src="@drawable/pause1" /><ImageViewandroid: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" /><ImageViewandroid: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() {}@Overridepublic IBinder onBind(Intent intent) {return new MusicControl();}@Overridepublic void onCreate() {super.onCreate();player = new MediaPlayer();//创建音乐播放器对象Log.i("lzs", "456  " + player);}class MusicControl extends Binder {//播放歌曲public boolean play(String str) {//String pathtry {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() {@Overridepublic 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();}}}@Overridepublic 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" /><applicationandroid: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"><activityandroid:name=".MusicActivity"android:exported="false"><meta-dataandroid:name="android.app.lib_name"android:value="" /></activity><activityandroid: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" /><serviceandroid:name=".MusicPlayService"android:enabled="true"android:exported="true" /></application></manifest>

模块内的build.gradle

plugins {id 'com.android.application'
}android {compileSdk 28defaultConfig {applicationId "com.example.mymusic"minSdk 21targetSdk 28versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
//添加包路径//开启dataBinding功能支持dataBinding {enabled = true}// 自定义打包名称android.applicationVariants.all { variant ->variant.outputs.all {outputFileName = "lzs.apk"}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility 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

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

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

  1. 一个基于Android开发的简单的音乐播放器

    一个基于Android开发的简单的音乐播放器 记得当时老师让我们写因为播放器时,脑子一头雾水,网上杂七杂八的资料也很少有用,因此索性就自己写一篇,希望对有缘人有用. 因为有好多人问我要源码,所以附上g ...

  2. android 简单的音乐播放器实现播放模式的切换

    以前写过一篇简单的音乐播放器,但是这个播放器没有实现播放模式的切换,在项目中要实现两个播放模式,循环播放和随机播放,经过这两天的努力搞定了,界面比较粗糙.可以先看一下前面的简单音乐播放器,详细的就不说 ...

  3. android 简单的音乐播放器

    在项目开发过程中需要一个简单的音频播放的功能,需求很简单,只需要能够播放一个指定文件夹的全部mp3和wav音频文件就可以,谷歌给我们提供了一套比较完整的API,使得我们可以很简单的写出一个简易的音乐播 ...

  4. 好程序员前端分享使用JS开发简单的音乐播放器

    好程序员前端分享使用JS开发简单的音乐播放器,最近,我们在教学生使用JavaScript,今天就带大家开发一款简单的音乐播放器.首先,最终效果如图所示: 首先,我们来编写html界面index.htm ...

  5. 自制一个简单的音乐播放器

    这两天刚学完了contentprovider和service组件,就综合下所学的,自制了一个简单的音乐播放器. 代码如下: 主activity代码 public class MainActivity ...

  6. 用Qt写一个简单的音乐播放器(三):增加界面(播放跳转与音量控制)

    一.前言 在用Qt写一个简单的音乐播放器(一):使用QMediaPlayer播放音乐中,我们已经知道如何去使用QMediaPlayer播放音乐. 在用Qt写一个简单的音乐播放器(二):增加界面(开始和 ...

  7. uniapp实现简单的音乐播放器功能

    uniapp实现简单的音乐播放器功能 问题描述: 创建并返回内部audio上下文来控制音乐播放 我是直接用scroll-into-view来实现的这个 <scroll-view scroll-y ...

  8. Js简单实现音乐播放器

    Js简单实现音乐播放器 HTML部分 CSS部分 js代码部分 这段时间正好是寒假,闲来无事把大二学的web再温习了一遍,在学习到Js时,想找一些小玩意来练练手,于是我就用原生Js做了一个简单音乐播放 ...

  9. 深圳java培训:使用原生JS重构简单的音乐播放器

    深圳java培训:使用原生JS重构简单的音乐播放器 上次,我们使用Jquery开发了一款简单的音乐播放器(如下图), 后来学生希望能够用原生的JS重构一次, 那么,下面就来看看如何使用原生的JS重构吧 ...

  10. html简单的音乐播放器

    html <!DOCTYPE html> <html><head><meta charset="UTF-8" /><title ...

最新文章

  1. “重构”黑洞:26岁MIT研究生的新算法 | 人物志
  2. win10搭建python环境_win10系统搭建python环境的还原方法
  3. wxWidgets编程笔记二(samples使用设置)
  4. java linux文件夹大小_Linux_Linux文件夹大小查看办法, 1. du -sh查看当前文件 - phpStudy...
  5. Beaglebone Black USB联机
  6. iPhone 7卖点不多出货量下降?剧透分析师又发报告
  7. python使用openpyxl操作excel总结
  8. [SHELL]备份系统文件并邮件发送
  9. js设置全局变量ajax中赋值
  10. boa php5.5 移植,BOA+PHP在Tiny6410上的移植
  11. JVM优化系列-对象内存分配和回收的细节
  12. 【元胞自动机】基于matlab元胞自动机收费站交通问题【含Matlab源码 357期】
  13. spss登录本地计算机,SPSS基本使用
  14. NVIDIA之Triton Inference Server环境部署安装
  15. [ linux ] vim 编辑器的三种模式介绍
  16. VS2008假死点击无反应解决办法
  17. 斑马打印机(zebraZT410R-203dpi)打印RFID标签流程
  18. svm公式推导及理解
  19. 酒店wifi全覆盖解决移动上网方案
  20. linux 移动硬盘 machine,MacBook使用一块移动硬盘做Win To Go及Time Machine备份

热门文章

  1. BeanUtils制作自定义的转换器
  2. 企业4A架构--TOGAF
  3. MySQL执行查询语句时出现停滞状态,出现Waiting for table metadata lock的问题
  4. 浙江移动面试二面(2009)
  5. Centos6.5离线安装中文输入法
  6. Python包 - networkx
  7. linux E325: 注意 发现交换文件 “*.swp“ 解决方法
  8. 谷歌验证码reCAPTCHA的运用
  9. 教你如何在软文中设置关键词
  10. 古剑奇谭二服务器维护,《古剑奇谭网络版》2月28日更新维护公告