Android音乐播放器4.0(列表播放底部增加歌曲信息展示)

200 阅读3分钟

这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

上一节实现了播放的功能,下面实现播放音乐时底部显示对应歌曲信息的功能。

【效果图】

在这里插入图片描述

【开发步骤】 1、xml 界面 2、当点击 ListView 中的 item 时,把播放列表和 position 存入 application 3、每当 service 开始播放音乐时,发送自定义广播 4、在Activity 中编写广播接收器接受该广播,获取当前正在播放的歌曲信息,更新界面(注意在 onCreate 及 onDestroy中注册以及取消注册) 5、更新 UI 时,我们需要通过 path 路径,获取 Bitmap

BitmapUtils.loadBitmap(String path,Callback){
	异步发送http请求
	调用 callback 回调方法 执行后续业务
}

按照开发步骤写代码:

主要布局中 activity_test.xml 中增加

<androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ffffff" />
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="#2894FF"
        android:gravity="center_vertical"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/img_music_thumb"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginRight="10dp"
            android:src="@mipmap/ic_launcher" />

        <TextView
            android:id="@+id/tv_music_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="歌名"
            android:textColor="@android:color/white" />

    </LinearLayout>

这里的圆形头像用的是 CircleImageView

需要在 build.gradle 中引入依赖

implementation 'de.hdodenhof:circleimageview:3.1.0'

在点击播放歌曲后,底部显示对应歌曲信息,需要知道当前播放的歌曲是哪一首,我们可以存歌曲列表和点击的 position 来确定歌曲,可以用Application 实现

创建 MusicApplication

public class MusicApplication extends Application {
    private List<MusicItem> musicList;
    private int position;
    public static MusicApplication app;

    @Override
    public void onCreate() {
        super.onCreate();
        app = this;
    }

    public static MusicApplication getApp() {
        return app;
    }

    public List<MusicItem> getMusicList() {
        return musicList;
    }

    public void setMusicList(List<MusicItem> musicList) {
        this.musicList = musicList;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    /**
     * 获取当前正在播放的音乐
     *
     * @return
     */
    public MusicItem getCurrentMusic() {
        return musicList.get(position);
    }
}

AndroidManifest 中注册

<application
       android:name=".application.MusicApplication"
        ......
        android:theme="@android:style/Theme.Light.NoTitleBar">
        <service android:name=".service.PlayMusicService"/>
        ......
    </application>

NewMusicListFragment 中,在点击 listview 时,在 application 中存入音乐播放列表和 position

private void setListeners() {
        listView.setOnItemClickListener((adapterView, view, i, l) -> {
            final MusicItem music = musics.get(i);
            String songId = music.id;
            Toast.makeText(getActivity(), "position:" + i, Toast.LENGTH_SHORT).show();
            //执行业务层 加载这首歌曲的基本信息
            model.loadMusicInfoBySongId(songId, new MusicInfoCallback() {
                @Override
                public void onMusicInfoLoaded(MusicItem musicItem) {
                    music.url = musicItem.url;
                    Log.d("TTT", music.url);
                    //把当前播放列表和position都存入application
                    MusicApplication app = MusicApplication.getApp();
                    app.setMusicList(musics);
                    app.setPosition(i);
                    //准备通过url地址执行播放业务
                    //播放音乐
                    binder.playMusic(music.url);
                }
            });
        });
    }

当音乐播放开始时,service 通知 activity 播放开始,因此我们需要发送广播,PlayMusicService 中修改:

 /**
     * 当service实例创建时执行
     */
    @Override
    public void onCreate() {
        super.onCreate();
        //给mediaPlayer加监听
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            //音乐已经准备好
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                mediaPlayer.start();
                //发送广播,通知activity音乐已经开始播放
                Intent intent = new Intent (Globalconsts.ACTION_MUSIC_STARTED);
                sendBroadcast(intent);
            }
        });
    }

创建常量类 GlobalConsts

/**
 * 常量类
 */
public class Globalconsts {
    //音乐开始播放 广播action
    public static final String ACTION_MUSIC_STARTED = "ACTION_MUSIC_STARTED";
}

BitmapUtils 中增加根据图片路径加载 bitmap 的方法

public static void loadBitmap(final String path, final BitmapCallback bitmapCallback){
        AsyncTask<String,String,Bitmap> task = new AsyncTask<String, String, Bitmap>() {
            @Override
            protected Bitmap doInBackground(String... strings) {
                try {
                    //先从内存缓存中读
                    //再从文件中读
                    String filename = path.substring(path.lastIndexOf("/")+1);
                    File file = new File(MusicApplication.getApp().getCacheDir(),"images/"+filename);
                    Bitmap b = bitmap(file);
                    if(b!=null){
                        return b;
                    }
                    InputStream is = HttpUtils.getInputStream(path);
                    b = BitmapFactory.decodeStream(is);
                    //下载下来的图片,存入缓存中
                    save(b,file);
                    return b;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            //主线程中执行回调方法
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                bitmapCallback.onBitmapLoaded(bitmap);
            }
        };
        task.execute();
    }

增加接口 BitmapCallback

public interface BitmapCallback {
    /**
     * 当图片下载成功之后的回调方法
     * @param bitmap
     */
    void onBitmapLoaded(Bitmap bitmap);
}

Activity 中注册接收器

    MusicInfoBroadCastReceiver receiver;
    private ImageView imgMusicThumb;
    private TextView tvMusicTitle;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		......
        //绑定Service
        bindMusicService();
        //注册广播接收器
        registMusicReceiver();
    }
    
  private void setViews() {
       	......
        viewPager = findViewById(R.id.viewpager);
        imgMusicThumb = findViewById(R.id.img_music_thumb);
        tvMusicTitle = findViewById(R.id.tv_music_title);
    }

    /**
     * 注册广播接收器
     */
    private void registMusicReceiver() {
        receiver = new MusicInfoBroadCastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Globalconsts.ACTION_MUSIC_STARTED);
        this.registerReceiver(receiver,intentFilter);
    }

   class MusicInfoBroadCastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals(Globalconsts.ACTION_MUSIC_STARTED)){
                //开始播放音乐,获取当前音乐对象
                MusicApplication app = MusicApplication.getApp();
                MusicItem music = app.getCurrentMusic();
                String pic = music.albumPic;
                final String title = music.name;
                BitmapUtils.loadBitmap(pic, new BitmapCallback() {
                    @Override
                    public void onBitmapLoaded(Bitmap bitmap) {
                        if(bitmap != null){
                            imgMusicThumb.setImageBitmap(bitmap);
                        }
                    }
                });
                tvMusicTitle.setText(title);
            }
        }
    }

    @Override
    protected void onDestroy() {
        this.unbindService(conn);
        this.unregisterReceiver(receiver);
        super.onDestroy();
    }

现在让图片转起来,MusicInfoBroadCastReceiver 中在设置完图片后,设置动画:

BitmapUtils.loadBitmap(pic, new BitmapCallback() {
                    @Override
                    public void onBitmapLoaded(Bitmap bitmap) {
                        if(bitmap != null){
                           imgMusicThumb.setImageBitmap(bitmap);
                           //执行旋转动画
                            RotateAnimation animation = new RotateAnimation(0,360,imgMusicThumb.getWidth()/2,imgMusicThumb.getWidth()/2);
                            animation.setDuration(10000);
                            animation.setRepeatCount(Animation.INFINITE);
                            animation.setInterpolator(new LinearInterpolator());//匀速旋转
                            imgMusicThumb.startAnimation(animation);
                        }
                        tvMusicTitle.setText(title);
                    }
                });