请看效果
简单的音乐播放器
开发工具:Android Studio
涉及到的类:
涉及到的layout(有一个是多余的item_layout.xml)
悄悄说一句:有的命名不规范,勿怪。
登录界面
登录界面 activity_login.xml
登录界面 MyActivityLogin
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
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;
import androidx.core.app.NotificationCompat;
import com.example.mymusic.R;
public class MyActivityLogin 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 Toolbar toolbar;
private Button btn_login;
private SharedPreferences sp;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ActivityCollector.addActivity(this);
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();
ActivityCollector.removeActivity(this);
}
//重写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(MyActivityLogin.this,"注册成功",Toast.LENGTH_SHORT).show();
//捕获数据并封装数据
num1 = num.getText().toString();
password = pw.getText().toString();
intent1 = new Intent(this,MyActivityZhuce.class);
intent1.putExtra("id",num1);
intent1.putExtra("ps",password);
startActivityForResult(intent1,1);
notificationManager.notify(1,notification);
}else {
Toast.makeText(MyActivityLogin.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(MyActivityLogin.this, MainActivity1.class);
startActivity(it1);
Toast.makeText(MyActivityLogin.this,"登录成功",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MyActivityLogin.this,"账号或密码错误",Toast.LENGTH_SHORT).show();
}
}else {
Toast.makeText(MyActivityLogin.this,"账号或密码填写错误",Toast.LENGTH_SHORT).show();
}
}
}
注册页面
注册页面 activity_three.xml (这命名你们可以改一下,我现在才发现,就不改了)
注册页面 MyActivityZhuce
import android.app.alertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.mymusic.R;
public class MyActivityZhuce extends AppCompatActivity {
TextView tv;
private static final String TAG="lzs";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three);
ActivityCollector.addActivity(this);
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();
ActivityCollector.removeActivity(this);
}
//隐式启动
public void three(View view){
Intent intent = new Intent(MyActivityZhuce.this,MyActivityLogin.class);
startActivity(intent);
}
}
音乐播放列表页面
首先解释一下,这个页面的布局情况,它是activity里放了fragment的,实现歌曲和专辑两选项的切换,如下图(左边的是activity,右边是fragment)
左边activity_main_music2.xml
右边music_list.xml
专辑页面 frag2_layout.xml(我就放了一个ListView,并且没有设置样式和内容,有兴趣的可以自己加一些专辑图片什么的)
每首音乐的 item样式item_local_music.xml(可以根据兴趣设置自己的样式)
到此我把音乐播放列表的布局都讲完了,接下来是它们的逻辑。
MainActivity1 类
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.frameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.example.mymusic.R;
import com.synnapps.carouselview.CarouselView;
import com.synnapps.carouselview.ImageClickListener;
import com.synnapps.carouselview.ImageListener;
public class MainActivity1 extends AppCompatActivity implements View.onClickListener {
private frameLayout content;
private TextView tv1,tv2;
private FragmentManager fm;
private FragmentTransaction ft;
CarouselView carouselView;
private int[] images = new int[]{
R.drawable.img11,
R.drawable.img22,
R.drawable.img33,
R.drawable.img44,
R.drawable.img55
};
private com.example.mymusic.mymusic.frag1 mFragmentConversation;
private frag2 mFragmentConversation2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_music2);
ActivityCollector.addActivity(this);
carouselView = findViewById(R.id.carouselView);
//设置轮播图的页面数量
carouselView.setPageCount(images.length);
carouselView.setImageListener(imageListener);
carouselView.setImageClickListener(new ImageClickListener() {
@Override
public void onClick(int position) {
}
});
content=(frameLayout)findViewById(R.id.content);
tv1=(TextView)findViewById(R.id.menu1);
tv2=(TextView)findViewById(R.id.menu2);
tv1.setonClickListener(this);
tv2.setonClickListener(this);
fm=getSupportFragmentManager();//若是继承FragmentActivity,fm=getFragmentManger();
ft=fm.beginTransaction();
ft.replace(R.id.content,new com.example.mymusic.mymusic.frag1());//默认情况下Fragment1
ft.commit();
InitFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.content, mFragmentConversation).commit();
}
private void InitFragment() {
mFragmentConversation = new com.example.mymusic.mymusic.frag1();
mFragmentConversation2 = new frag2();
}
ImageListener imageListener = new ImageListener() {
@Override
public void setImageForPosition(int position, ImageView imageView) {
imageView.setImageResource(images[position]);
}
};
@Override
public void onClick(View v){
ft=fm.beginTransaction();
FragmentTransaction mTransaction = getSupportFragmentManager()
.beginTransaction();
switch (v.getId()){
case R.id.menu1:
if (!getSupportFragmentManager().getFragments().contains(mFragmentConversation)) {
mTransaction.add(R.id.content, mFragmentConversation);
}
mTransaction.show(mFragmentConversation);
HideOtherFragments(mFragmentConversation);
break;
case R.id.menu2:
if (!getSupportFragmentManager().getFragments().contains(mFragmentConversation2)) {
mTransaction.add(R.id.content, mFragmentConversation2);
}
mTransaction.show(mFragmentConversation2);
HideOtherFragments(mFragmentConversation2);
break;
default:
break;
}
mTransaction.commit();
}
private void HideOtherFragments(Fragment fragment) {
if (getSupportFragmentManager().getFragments() == null) {
return;
}
for (Fragment fm : getSupportFragmentManager().getFragments()) {
if (fm != fragment) {
getSupportFragmentManager().beginTransaction().hide(fm)
.commit();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
}
数据源类 localMusicBean
package com.example.mymusic.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;
}
}
适配器类 LocalMusicAdapter2
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 com.example.mymusic.R; import java.util.List; public class LocalMusicAdapter2 extends RecyclerView.Adapter{ Context context; List mDatas; onItemClickListener onItemClickListener; //接口回调 public void setonItemClickListener(onItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public interface OnItemClickListener{ public void onItemClick(View view,int position); } public LocalMusicAdapter2(Context context, List 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); } } }
活动管理类 ActivityCollector(便于管理活动是否销毁)
import android.app.Activity;
import java.util.linkedList;
public class ActivityCollector {
public static linkedList activities = new linkedList();
public static void addActivity(Activity activity)
{
activities.add(activity);
}
public static void removeActivity(Activity activity)
{
activities.remove(activity);
}
public static void finishAll()
{
for (Activity activity:activities ) {
if(!activity.isFinishing())
{
activity.finish();
}
}
}
}
音乐播放列表类 frag1(继承自Fragment)
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.mymusic.R;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class frag1 extends Fragment implements View.OnClickListener{
private static final String TAG = "lzs";
private View view;
public String name;
public static int icons=R.drawable.iuyuantu;
private ImageView btn_music;
RecyclerView musicRv;
// 数据源
private List mDatas;
private LocalMusicAdapter2 adapter;
private MediaPlayer mediaPlayer;
private Intent it;
public String addr=null;
public int position1=-1;
private int idd;
private TextView t1,t2;
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
// 可能初始化一个新实例的默认值的成员
view=inflater.inflate(R.layout.music_list,container,false);
musicRv = view.findViewById(R.id.local_music_rv1);
mediaPlayer = new MediaPlayer();
mDatas = new ArrayList<>();
t1=view.findViewById(R.id.local_music_bottom_tv_song);
t2=view.findViewById(R.id.local_music_bottom_tv_singer);
btn_music=view.findViewById(R.id.local_music_icon);
btn_music.setonClickListener(this);
//创建适配器对象
adapter = new LocalMusicAdapter2(frag1.this.getContext(), mDatas);
musicRv.setAdapter(adapter);
//设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(frag1.this.getContext(), LinearLayoutManager.VERTICAL, false);
musicRv.setLayoutManager(layoutManager);
//加载本地数据源
loadLocalMusicData();
//设置每一项的点击事件
setEventListener();
Log.i(TAG, "onCreateView: "+position1);
return view;
}
private void loadLocalMusicData() {
//1.获取ContentResolver对象
ContentResolver resolver = frag1.this.getContext().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, null);
Log.i(TAG, "loadLocalMusicdata: "+cursor);
//4.遍历Cursor
int id = 0;
while (cursor.moveTonext()) {
String song = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
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));
long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
String time = sdf.format(new Date(duration));
//获取专辑图片主要是通过album_id进行查询
String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
//将一行当中的数据封装到对象当中
localMusicBean bean = new localMusicBean(sid, song, singer, album, time, path,"");
mDatas.add(bean);
}
idd=id;
Log.i(TAG, "loadLocalMusicdata: "+mDatas);
//数据源变化,提示适配器更新
adapter.notifyDataSetChanged();
}
private void setEventListener() {
adapter.setonItemClickListener(new LocalMusicAdapter2.onItemClickListener() {
@Override
public void onItemClick(View view, int position) {
position1 = position;
Log.i(TAG, "OnItemClick: "+position1);
addr=mDatas.get(position).getPath();
t1.setText(mDatas.get(position).getSong());
t2.setText(mDatas.get(position).getSinger());
it = new Intent(frag1.this.getContext(), com.example.mymusic.mymusic.Music_Activity.class);
it.putExtra("position",String.valueOf(1));
it.putExtra("id1",position); //点击歌曲id
it.putExtra("id2",idd); //歌曲总数
it.putExtra("data1", (Serializable) mDatas); //歌曲集合
it.putExtra("musicI",0); //传0表示用户点击的是歌曲列表里的歌曲,而不是返回播放界面
startActivity(it);
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.local_music_icon:
it = new Intent(frag1.this.getContext(), com.example.mymusic.mymusic.Music_Activity.class);
it.putExtra("data1", (Serializable) mDatas);
it.putExtra("musicI",1); //传1表示用户点击的是那个音箱图标,只是想返回播放界面,而不是要换歌
it.putExtra("id2",idd); //歌曲总数
it.putExtra("id1",position1); //传歌曲id
startActivity(it);
break;
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
专辑 类frag2(继承自Fragment)
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import com.example.mymusic.R;
public class frag2 extends Fragment {
private View zj;
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
zj = inflater.inflate(R.layout.frag2_layout, null);
return zj;
}
}
到此音乐播放列表的布局文件和类都写完了,接下来是
音乐播放界面
音乐播放界面 activity_music
音乐播放界面类 Music_Activity
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import com.example.mymusic.R;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class Music_Activity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "lzs";
private static SeekBar sb;
private static TextView tv_progress,tv_total,song_name;
private ObjectAnimator animator;
private String name;
private Intent intent1;
// 数据源
private List mDatas1;
private ImageView play1,last1,next1;
private int num;
private int id;
private ImageView btn_xunhuang;
private boolean aa=true;
private boolean bb=true;
private ImageView btn_shouc;
private int musicI;
private ImageView iv_music;
private MediaPlayer player;
private Timer timer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music);
ActivityCollector.addActivity(this);
//intent 通信
intent1=getIntent();
mDatas1=(List) getIntent().getSerializableExtra("data1");//接收歌曲集合
num = intent1.getIntExtra("id2",0);//歌曲总数
id = intent1.getIntExtra("id1",0);//点击歌曲id
player=new MediaPlayer();//创建音乐播放器对象
init(); //初始化布局控件
Log.i(TAG, "onCreate: ");
}
private void init(){
tv_progress=(TextView)findViewById(R.id.tv_progress);
tv_total=(TextView)findViewById(R.id.tv_total);
sb=(SeekBar)findViewById(R.id.sb);
song_name=findViewById(R.id.song_name);
play1=findViewById(R.id.btn_play);
last1=findViewById(R.id.btn_last);
next1=findViewById(R.id.btn_next);
btn_xunhuang=findViewById(R.id.btn_xuhuang);
btn_shouc=findViewById(R.id.btn_shouc);
play1.setonClickListener(this);
last1.setonClickListener(this);
next1.setonClickListener(this);
btn_xunhuang.setonClickListener(this);
btn_shouc.setonClickListener(this);
if(id!=-1)
{
name=mDatas1.get(id).getSong();
song_name.setText(name);
}
//为滑动条添加事件监听
sb.setonSeekBarChangeListener(new SeekBar.onSeekBarChangeListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//进度条改变时,会调用此方法
if (progress==seekBar.getMax()){//当滑动条到末端时,结束动画
animator.pause();//停止播放动画
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {//滑动条开始滑动时调用
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {//滑动条停止滑动时调用
//根据拖动的进度改变音乐播放进度
int progress=seekBar.getProgress();//获取seekBar的进度
seekTo(progress);//改变播放进度
}
});
iv_music=(ImageView)findViewById(R.id.iv_music);
iv_music.setImageResource(frag1.icons); //设置专辑图片
animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f);
animator.setDuration(10000);//动画旋转一周的时间为10秒
animator.setInterpolator(new LinearInterpolator());//匀速
animator.setRepeatCount(-1);//-1表示设置动画无限循环
if(id!=-1)
animator.start();
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_play://播放按钮点击事件
if(id==-1)
{
id=0;
play1.setImageResource(R.drawable.play1);
song_name.setText(mDatas1.get(id).getSong());
play(mDatas1.get(id).getPath());
animator.start();
}else {
if(isPlay())
{
play1.setImageResource(R.drawable.pause1);
pausePlay();
animator.pause();
}else
{
play1.setImageResource(R.drawable.play1);
continuePlay();
if(animator.isPaused())
animator.resume();
else
{
animator.start();
}
}
}
break;
case R.id.btn_last://上一首
if(id<=0)
{
Toast.makeText(this,"已经是第一首了,没有上一曲!",Toast.LENGTH_SHORT).show();
}else
{
if(isPlay())
{
animator.pause();
animator.clone();
id--;
song_name.setText(mDatas1.get(id).getSong());
play(mDatas1.get(id).getPath());
animator.start();
}else
{
animator.pause();
animator.clone();
play1.setImageResource(R.drawable.play1);
id--;
song_name.setText(mDatas1.get(id).getSong());
play(mDatas1.get(id).getPath());
animator.start();
}
}
break;
case R.id.btn_next://下一首
if(id>=num)
{
Toast.makeText(this,"已经是最后一首了,没有下一曲!",Toast.LENGTH_SHORT).show();
}else
{
if(isPlay())
{
animator.pause();
animator.clone();
id++;
song_name.setText(mDatas1.get(id).getSong());
play(mDatas1.get(id).getPath());
animator.start();
}
else
{
animator.pause();
animator.clone();
id++;
song_name.setText(mDatas1.get(id).getSong());
play1.setImageResource(R.drawable.play1);
play(mDatas1.get(id).getPath());
animator.start();
}
}
break;
case R.id.btn_xuhuang:
btn_xunhuang.setSelected(aa);
if(aa==true)
{
Toast.makeText(Music_Activity.this,"顺序播放",Toast.LENGTH_SHORT).show();
aa=false;
}
else
{
Toast.makeText(Music_Activity.this,"单曲循环",Toast.LENGTH_SHORT).show();
aa=true;
}
break;
case R.id.btn_shouc:
btn_shouc.setSelected(bb);
if(bb==true)
{
Toast.makeText(Music_Activity.this,"收藏成功",Toast.LENGTH_SHORT).show();
bb=false;
}
else
{
Toast.makeText(Music_Activity.this,"取消收藏",Toast.LENGTH_SHORT).show();
bb=true;
}
break;
}
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
}
@Override
protected void onStart() {
Log.i(TAG, "onStart: ");
super.onStart();
}
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
Log.i(TAG, "onWindowFocusChanged: ");
if (hasFocus)
{
Log.i("lsh123", "onWindowFocusChanged: "+isPlay());
if(id!=-1)
{
song_name.setText(mDatas1.get(id).getSong());
musicI=intent1.getIntExtra("musicI",-1);
Log.i(TAG, "onWindowFocusChanged: "+musicI);
if(musicI==0)
{
play1.setImageResource(R.drawable.play1);
play(mDatas1.get(id).getPath());
animator.start();
}
}
}
}
//在Musiv_Activity从后台重新切换到前台时,更新当前intent对象,已达到更新数据的目的
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
intent1 = getIntent();
id = intent1.getIntExtra("id1",-1);
Log.i(TAG, "onNewIntent: "+id);
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.i("lsh123", "onDestroy1: ");
}
//让activity进入后台,不销毁
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.i(TAG, "onKeyDown: ");
if(keyCode == KeyEvent.KEYCODE_BACK){
Intent it = new Intent(Music_Activity.this,MainActivity1.class);
startActivity(it);
return true;
}
return super.onKeyDown(keyCode, event);
}
public void addTimer(){ //添加计时器用于设置音乐播放器中的播放进度条
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=Music_Activity.handler.obtainMessage();//创建消息对象
//将音乐的总时长和播放进度封装至消息对象中
Bundle bundle=new Bundle();
bundle.putInt("duration",duration);
bundle.putInt("currentPosition",currentPosition);
msg.setData(bundle);
//将消息发送到主线程的消息队列
Music_Activity.handler.sendMessage(msg);
Log.i("lsh123", "run: ");
}
};
//开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒执行一次
timer.schedule(task,5,500);
}
}
//播放歌曲
public void play(String str){//String path
Log.i("lsh123", "play: "+str);
Uri uri=Uri.parse(str+"");
try{
player.reset();//重置音乐播放器
//加载多媒体文件
Log.i("lsh123", "play: 123"+str);
player=MediaPlayer.create(getApplicationContext(),uri);
Log.i("lsh123", "play: "+str);
player.start();//播放音乐
addTimer();//添加计时器
}catch(Exception e){
e.printStackTrace();
}
}
public boolean isPlay(){
if(player.isPlaying())
return true;
else return false;
}
public void pausePlay(){
player.pause();//暂停播放音乐
}
public void continuePlay(){
player.start();//继续播放音乐
}
public void seekTo(int progress){
player.seekTo(progress);//设置音乐的播放位置
}
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);
}
};
}
还有一些配置信息
AndroidManifest.xml
模块内的build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.example.mymusic"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
//添加包路径
//开启dataBinding功能支持
dataBinding {
enabled = true
}
// 自定义打包名称
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "ac.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.还有就是,音乐列表和专辑两个页面的切换,其实就是activity里两个fragment的切换,注意
通过add方法添加fragment,再通过hide,show决定显示哪一个fragment,此方式是将fragment隐藏
而非重建,replace:每次都是重新创建fragment,所以为了保存数据,我采用的是add。
3.音乐列表上方的轮播图是我用第三方插件写的,当然你也可以自己写,用Handler消息机制。
4.在音乐列表和音乐播放界面跳转过程中,我让音乐播放界面直接切入后台,不销毁,从而达到数
据的保存,当然你也可以使用Service,进程间的通信解决数据交互问题。
5.音乐播放界面的音乐进度条是通过Handler消息机制实现的,每个几毫秒就向主线程发送消息,
然后修改当前进度条的值,从而达到动态的效果
6.还有很多小知识,我就不一一讲了,相信你们也能看的懂,没有用到特别难的知识,功能也挺简
单的。
GitHub链接:https://github.com/1577643176/Simplemusicplayer.git
联系QQ:1577643176
以上是我个人总结,难免会有不妥之处,欢迎指正,谢谢。



