开始前的碎碎念

许久没更新了,由于专升本课程紧张,也没啥好更新的,博客放养了,今天做期末大作业的时候发现有个有趣的小案例,用Android studio来实现一个简单的音乐播放器,虽然说十分简单,但是对我我这种弱鸡来说感觉十分的新鲜,所以想写一篇文章来记录这个案例。OK那我们开始记录吧。


综合运用UI界面设计、数据存储、Activity(活动)、Service(服务)、MusicPlayer、ListView等知识,设计开发一款具有音乐列表的音乐播放器。

  1. 整个项目包含五个类和五个布局文件:
    其中frag1为java文件,
    Music_Activity为Activity文件,
    MusicService为Service文件,
    MainActivity为主类文件。
    layout为MainActivity的布局文件,显示运行APP时的主界面。
    activity_music为Music_Activity的布局文件,显示音乐播放器界面。
    music_list和item_layout一起组成frag1的布局文件,就是音乐列表界面(打开APP默认显示音乐列表界面)。
    所需创建的文件

  2. 搭建主界面布局
    MainActivity类是整个工程的主类里面联系了frag1,显示歌曲列表,展示歌曲菜单,layout布局文件相联系

    package com.example.myapplication;

    import androidx.appcompat.app.AppCompatActivity;
    import androidx.fragment.app.FragmentManager;
    import androidx.fragment.app.FragmentTransaction;

    import android.os.Bundle;
    import android.view.View;
    import android.widget.FrameLayout;
    import android.widget.TextView;

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private FrameLayout content;
    private TextView tv1;
    private FragmentManager fm;
    private FragmentTransaction ft;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    content=(FrameLayout)findViewById(R.id.content);

    tv1=(TextView)findViewById(R.id.menu1);


    tv1.setOnClickListener(this);

    fm=getSupportFragmentManager();//若是继承FragmentActivity,fm=getFragmentManger();
    ft=fm.beginTransaction();
    ft.replace(R.id.content,new frag1());//默认情况下Fragment1
    ft.commit();
    }
    @Override
    public void onClick(View v){
    ft=fm.beginTransaction();
    switch (v.getId()){
    case R.id.menu1:
    ft.replace(R.id.content,new frag1());
    break;

    default:
    break;
    }
    ft.commit();
    }
    }

    Layout是布局文件,显示主界面,上面放置了两个TextView 显示我喜欢和歌曲
    Layout布局效果

    <?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:orientation="vertical">
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <TextView
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:gravity="center"
    android:text="我喜欢"
    android:textSize="35dp"/>
    </LinearLayout>
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <TextView
    android:id="@+id/menu1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:gravity="center"
    android:text="歌曲"
    android:textSize="25dp"/>

    </LinearLayout>
    <FrameLayout
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="9">
    </FrameLayout>
    </LinearLayout>
  3. 创建服务类
    MusicService作为服务文件,目的是为了让app在后台时app还能运行,在后台播放音乐,并不依赖于Activity ,Activity生命周期结束后,Service仍会继续运行,知道自己的生命周期结束为止.

    package com.example.myapplication;

    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 java.util.Timer;
    import java.util.TimerTask;

    public class MusicService extends Service {
    private MediaPlayer player;
    private Timer timer;
    public MusicService() {}
    @Override
    public IBinder onBind(Intent intent){
    return new MusicControl();
    }
    @Override
    public void onCreate(){
    super.onCreate();
    player=new MediaPlayer();//创建音乐播放器对象
    }
    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);
    }
    };
    //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒执行一次
    timer.schedule(task,5,500);
    }
    }
    class MusicControl extends Binder {//Binder是一种跨进程的通信方式
    public void play(int i){//String path
    Uri uri=Uri.parse("android.resource://"+getPackageName()+"/raw/"+"music"+i);
    try{
    player.reset();//重置音乐播放器
    //加载多媒体文件
    player=MediaPlayer.create(getApplicationContext(),uri);
    player.start();//播放音乐
    addTimer();//添加计时器
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    public void pausePlay(){
    player.pause();//暂停播放音乐
    }
    public void continuePlay(){
    player.start();//继续播放音乐
    }
    public void seekTo(int progress){
    player.seekTo(progress);//设置音乐的播放位置
    }
    }
    @Override
    public void onDestroy(){
    super.onDestroy();
    if(player==null) return;
    if(player.isPlaying()) player.stop();//停止播放音乐
    player.release();//释放占用的资源
    player=null;//将player置为空
    }
    }


  4. 搭建音乐播放界面布局
    Music_Activity
    通过onclick方法音乐的播放、暂停、继续播放和退出功能,在音乐播放时显示歌曲总时长,还有歌曲当前播放时长,控制滑动条的移动,
    通过setOnSeekBarChangeListener方法实现拖动进度条的使用及事件监听
    滑动条开始时掉用onStartTrackingTouch
    滑动条停止调用onStopTrackingTouch
    图片通过setDuration(10000)
    ObjectAnimator.ofFloat(iv_music,”rotation”,0f,360.0f);
    setRepeatCount(-1)
    这三个方法来实现图片旋转360度,旋转一周的时间为10秒,并无限循环
    通过Onclick方法实现四个按钮播放,暂停,继续播放,退出按钮功能

    package com.example.myapplication;

    import static java.lang.Integer.parseInt;

    import androidx.annotation.RequiresApi;
    import androidx.annotation.RestrictTo;
    import androidx.appcompat.app.AppCompatActivity;

    import android.animation.ObjectAnimator;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.view.View;
    import android.view.animation.LinearInterpolator;
    import android.widget.ImageView;
    import android.widget.SeekBar;
    import android.widget.TextView;

    public class Music_Activity extends AppCompatActivity implements View.OnClickListener{
    private static SeekBar sb;
    private static TextView tv_progress,tv_total,name_song;
    private ObjectAnimator animator;
    private MusicService.MusicControl musicControl;
    String name;
    Intent intent1,intent2;
    MyServiceConn conn;
    private boolean isUnbind =false;//记录服务是否被解绑


    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_music);
    intent1=getIntent();
    init();
    }
    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);
    name_song=(TextView)findViewById(R.id.song_name);

    findViewById(R.id.btn_play).setOnClickListener(this);
    findViewById(R.id.btn_pause).setOnClickListener(this);
    findViewById(R.id.btn_continue_play).setOnClickListener(this);
    findViewById(R.id.btn_exit).setOnClickListener(this);

    name=intent1.getStringExtra("name");
    name_song.setText(name);
    intent2=new Intent(this,MusicService.class);//创建意图对象
    conn=new MyServiceConn();//创建服务连接对象
    bindService(intent2,conn,BIND_AUTO_CREATE);//绑定服务
    //为滑动条添加事件监听
    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的进度
    musicControl.seekTo(progress);//改变播放进度
    }
    });
    ImageView iv_music=(ImageView)findViewById(R.id.iv_music);
    String position= intent1.getStringExtra("position");
    int i=parseInt(position);
    iv_music.setImageResource(frag1.icons[i]);


    animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f);
    animator.setDuration(10000);//动画旋转一周的时间为10秒
    animator.setInterpolator(new LinearInterpolator());//匀速
    animator.setRepeatCount(-1);//-1表示设置动画无限循环
    }


    public static Handler handler=new Handler(){//创建消息处理器对象
    //在主线程中处理从子线程发送过来的消息
    @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);
    }
    };

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

    }
    }
    private void unbind(boolean isUnbind){
    if(!isUnbind){//判断服务是否被解绑
    musicControl.pausePlay();//暂停播放音乐
    unbindService(conn);//解绑服务
    }
    }
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onClick(View v) {
    switch (v.getId()){
    case R.id.btn_play://播放按钮点击事件
    String position=intent1.getStringExtra("position");
    int i=parseInt(position);
    musicControl.play(i);
    animator.start();
    break;
    case R.id.btn_pause://暂停按钮点击事件
    musicControl.pausePlay();
    animator.pause();
    break;
    case R.id.btn_continue_play://继续播放按钮点击事件
    musicControl.continuePlay();
    animator.start();
    break;
    case R.id.btn_exit://退出按钮点击事件
    unbind(isUnbind);
    isUnbind=true;
    finish();
    break;
    }
    }
    @Override
    protected void onDestroy(){
    super.onDestroy();
    unbind(isUnbind);//解绑服务
    }
    }

    activity_music: 显示音乐播放器界面,包括图片转动,歌曲名,还有播放、暂停播放、继续播放和退出四个控制按钮。效果如图:
    通过ImageView和TextView来显示歌曲的图片和标题,通过SeekBar实现进度条,两个TextView在进度条的一前一后,显示时长的该表,四个按钮对应四个功能,通过Music的Onclick方法实现其功能

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/music_bg"
    tools:context=".Music_Activity"
    android:gravity="center"
    android:orientation="vertical">
    <ImageView
    android:id="@+id/iv_music"
    android:layout_width="240dp"
    android:layout_height="240dp"
    android:layout_gravity="center_horizontal"
    android:layout_margin="15dp"
    android:src="@drawable/music0"/>
    <TextView
    android:id="@+id/song_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="歌曲名"
    android:textSize="20sp"/>
    <SeekBar
    android:id="@+id/sb"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingRight="8dp">
    <TextView
    android:id="@+id/tv_progress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="00:00"/>
    <TextView
    android:id="@+id/tv_total"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:text="00:00"/>
    </RelativeLayout>
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <Button
    android:id="@+id/btn_play"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_margin="8dp"
    android:layout_weight="1"
    android:background="@drawable/btn_bg_selector"
    android:text="播放"/>
    <Button
    android:id="@+id/btn_pause"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_margin="8dp"
    android:layout_weight="1"
    android:background="@drawable/btn_bg_selector"
    android:text="暂停"/>
    <Button
    android:id="@+id/btn_continue_play"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_margin="8dp"
    android:layout_weight="1"
    android:background="@drawable/btn_bg_selector"
    android:text="继续"/>
    <Button
    android:id="@+id/btn_exit"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_margin="8dp"
    android:layout_weight="1"
    android:background="@drawable/btn_bg_selector"
    android:text="退出"/>
    </LinearLayout>

    </LinearLayout>

    显示效果1
    显示效果2

  5. 搭建音乐列表界面布局
    Frag1脚本文件,music_list和item_layout
    显示歌曲列表,与MainActivity联系,显示歌曲信息,Frag1与music-list和item-layout两个布局文件相联系通过定义字符串显示歌曲信息,显示在music-list的ListView上。Item-layout显示歌曲的专辑图片
    frag1:

    package com.example.myapplication;

    import android.content.Intent;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.TextView;

    import androidx.fragment.app.Fragment;

    public class frag1 extends Fragment {
    private View view;
    public String[] name={"RADWIMPS-三葉のテーマ-H","Ephixa—Matches (feat. Aaron Richards) [Acoustic]","Justin Bieber——Stay"};
    public static int[] icons={R.drawable.music0,R.drawable.music1,R.drawable.music2};
    @Override
    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
    view=inflater.inflate(R.layout.music_list,null);
    ListView listView=view.findViewById(R.id.lv);
    MyBaseAdapter adapter=new MyBaseAdapter();
    listView.setAdapter(adapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Intent intent=new Intent(frag1.this.getContext(),Music_Activity.class);//创建Intent对象,启动check
    //将数据存入Intent对象
    intent.putExtra("name",name[position]);
    intent.putExtra("position",String.valueOf(position));
    startActivity(intent);
    }
    });
    return view;
    }
    class MyBaseAdapter extends BaseAdapter {
    @Override
    public int getCount(){return name.length;}
    @Override
    public Object getItem(int i){return name[i];}
    @Override
    public long getItemId(int i){return i;}

    @Override
    public View getView(int i ,View convertView, ViewGroup parent) {
    View view=View.inflate(frag1.this.getContext(),R.layout.item_layout,null);
    TextView tv_name=view.findViewById(R.id.item_name);
    ImageView iv=view.findViewById(R.id.iv);

    tv_name.setText(name[i]);
    iv.setImageResource(icons[i]);
    return view;
    }
    }

    }

    musci_list:

    <?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:orientation="vertical">
    <ListView
    android:id="@+id/lv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

    </LinearLayout>

    item_list

       <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    <ImageView
    android:id="@+id/iv"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_centerVertical="true"/>
    <RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_toRightOf="@+id/iv"
    android:layout_centerVertical="true">
    <TextView
    android:id="@+id/item_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="歌曲"
    android:textSize="15sp"
    android:textColor="#000000"/>
    </RelativeLayout>
    </RelativeLayout>

    到此为止案例完成啦,记得把专辑图片裁剪成圆形哦,再把音乐文件和专辑图片文件按music0开始命名哦

此文章转载至csdn如有侵权,就删帖啦,感觉这案例有意思,像记录一下,给自己做个笔记,嘻嘻.