Android 音乐APP(三)播放音乐、自定义进度条、自动下一曲

android:layout_margin=“@dimen/dp_36”

android:insetLeft=“@dimen/dp_0”

android:insetTop=“@dimen/dp_0”

android:insetRight=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:onClick=“onClick”

android:textSize=“@dimen/sp_14”

android:theme=“@style/Theme.MaterialComponents.Light.NoActionBar”

android:visibility=“gone”

app:backgroundTint=“@color/white”

app:cornerRadius=“@dimen/dp_20”

app:icon=“@drawable/music_location”

app:iconGravity=“textStart”

app:iconPadding=“0dp”

app:iconTint=“@color/black” />

<LinearLayout

android:id=“@+id/lay_bottom”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_alignParentBottom=“true”

android:background=“@color/bottom_bg_color”

android:gravity=“center_vertical”

android:paddingLeft=“@dimen/dp_8”

android:paddingTop=“@dimen/dp_8”

android:paddingRight=“@dimen/dp_16”

android:paddingBottom=“@dimen/dp_8”>

<RelativeLayout

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”>

<com.google.android.material.imageview.ShapeableImageView

android:id=“@+id/iv_logo”

android:layout_width=“@dimen/dp_48”

android:layout_height=“@dimen/dp_48”

android:padding=“1dp”

android:src=“@mipmap/icon_music”

app:shapeAppearanceOverlay=“@style/circleImageStyle”

app:strokeColor=“@color/white”

app:strokeWidth=“@dimen/dp_2” />

<com.llw.goodmusic.view.MusicRoundProgressView

android:id=“@+id/music_progress”

android:layout_width=“@dimen/dp_48”

android:layout_height=“@dimen/dp_48”

app:radius=“20dp”

app:strokeColor=“@color/black”

app:strokeWidth=“2dp” />

<com.google.android.material.textview.MaterialTextView

android:id=“@+id/tv_song_name”

android:layout_width=“0dp”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:ellipsize=“marquee”

android:focusable=“true”

android:focusableInTouchMode=“true”

android:marqueeRepeatLimit=“marquee_forever”

android:paddingLeft=“@dimen/dp_12”

android:paddingRight=“@dimen/dp_12”

android:singleLine=“true”

android:text=“Good Music”

android:textColor=“@color/white”

android:textSize=“@dimen/sp_16” />

<com.google.android.material.button.MaterialButton

android:id=“@+id/btn_play”

android:layout_width=“@dimen/dp_36”

android:layout_height=“@dimen/dp_36”

android:insetLeft=“@dimen/dp_0”

android:insetTop=“@dimen/dp_0”

android:insetRight=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:onClick=“onClick”

android:theme=“@style/Theme.MaterialComponents.Light.NoActionBar”

app:backgroundTint=“@color/transparent”

app:cornerRadius=“@dimen/dp_18”

app:icon=“@mipmap/icon_play”

app:iconGravity=“textStart”

app:iconPadding=“@dimen/dp_0”

app:iconSize=“@dimen/dp_36” />

里面用到三个图标一个自定义View。图标你可以去我的源码里面拿,源码图标,自定义View我会写出来。在com.llw.goodmusic下新建一个view包,然后新建一个MusicRoundProgressView。

在这里插入图片描述

代码如下:

package com.llw.goodmusic.view;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.view.View;

import androidx.annotation.Nullable;

import com.llw.goodmusic.R;

/**

  • 圆形进度条

  • @author llw

*/

public class MusicRoundProgressView extends View {

/**

  • 画笔

*/

private Paint mPaint;

/**

  • 画笔颜色

*/

private int mPaintColor;

/**

  • 半径

*/

private float mRadius;

/**

  • 圆环半径

*/

private float mRingRadius;

/**

  • 圆环宽度

*/

private float mStrokeWidth;

/**

  • 圆心 X 轴坐标

*/

private int mCenterX;

/**

  • 圆心 Y 轴坐标

*/

private int mCenterY;

/**

  • 总进度

*/

private int mTotalProgress;

/**

  • 当前进度

*/

private int mProgress;

public MusicRoundProgressView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressView);

//半径

mRadius = typedArray.getDimension(R.styleable.RoundProgressView_radius, 40);

//宽度

mStrokeWidth = typedArray.getDimension(R.styleable.RoundProgressView_strokeWidth, 5);

//颜色

mPaintColor = typedArray.getColor(R.styleable.RoundProgressView_strokeColor, 0xFFFFFFFF);

//圆环半径 = 半径 + 圆环宽度的1/2

mRingRadius = mRadius + mStrokeWidth / 2;

//画笔

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setColor(mPaintColor);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(mStrokeWidth);

}

@Override

protected void onDraw(Canvas canvas) {

mCenterX = getWidth() / 2;

mCenterY = getHeight() / 2;

if (mProgress > 0) {

RectF rectF = new RectF();

rectF.left = (mCenterX - mRingRadius);

rectF.top = (mCenterY - mRingRadius);

rectF.right = mRingRadius * 2 + (mCenterX - mRingRadius);

rectF.bottom = mRingRadius * 2 + (mCenterY - mRingRadius);

canvas.drawArc(rectF, -90, ((float) mProgress / mTotalProgress) * 360, false, mPaint);

}

}

/**

  • 设置进度

*/

public void setProgress(int progress, int totalProgress) {

mProgress = progress;

mTotalProgress = totalProgress;

//重绘

postInvalidate();

}

}

里面涉及到的样式如下:

在这里插入图片描述

在styles.xml中增加如下代码:

现在你的自定义从理论上来说就不会报错了。当然可能需要改一下包名之类,下面就是回到LocalMusicActivty。

② 初始化数据


首先在当前定位按钮后面加上这些变量

/**

  • 底部logo图标,点击之后弹出当前播放歌曲详情页

*/

private ShapeableImageView ivLogo;

/**

  • 底部当前播放歌名

*/

private MaterialTextView tvSongName;

/**

  • 底部当前歌曲控制按钮, 播放和暂停

*/

private MaterialButton btnPlay;

/**

  • 音频播放器

*/

private MediaPlayer mediaPlayer;

/**

  • 记录当前播放歌曲的位置

*/

public int mCurrentPosition = -1;

/**

  • 自定义进度条

*/

private MusicRoundProgressView musicProgress;

/**

  • 音乐进度间隔时间

*/

private static final int INTERNAL_TIME = 1000;

/**

  • 图片动画

*/

private ObjectAnimator logoAnimation;

每一个都有注释,然后绑定相关的控件

在这里插入图片描述

同样点击事件必不可少

在这里插入图片描述

③ 播放音乐


常规的操作是通过点击音乐列表中的某一首歌之后播放歌曲。还记得列表的点击事件在哪里吗?当然是在**showLocalMusicData()**方法里面,之前在这个方法中设置适配器和列表的一些相关属性和数据,当然还有点击事件。

回顾一下这个代码:

//item的点击事件

mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {

@Override

public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {

if (view.getId() == R.id.item_music) {

if (oldPosition == -1) {

//未点击过 第一次点击

oldPosition = position;

mList.get(position).setCheck(true);

} else {

//大于 1次

if (oldPosition != position) {

mList.get(oldPosition).setCheck(false);

mList.get(position).setCheck(true);

//重新设置位置,当下一次点击时position又会和oldPosition不一样

oldPosition = position;

}

}

mAdapter.changeState();

}

}

});

我不想这里面的代码太多,所以我新写了一个方法。

/**

  • 控制播放位置

  • @param position

*/

private void playPositionControl(int position) {

if (oldPosition == -1) {

//未点击过 第一次点击

oldPosition = position;

mList.get(position).setCheck(true);

} else {

//大于 1次

if (oldPosition != position) {

mList.get(oldPosition).setCheck(false);

mList.get(position).setCheck(true);

//重新设置位置,当下一次点击时position又会和oldPosition不一样

oldPosition = position;

}

}

mAdapter.changeState();

}

当点击这个item时将position传递给全局变量mCurrentPosition。

在这里插入图片描述

然后通过changeSong(mCurrentPosition);方法来播放歌曲

/**

  • 切换歌曲

*/

private void changeSong(int position) {

if (mediaPlayer == null) {

mediaPlayer = new MediaPlayer();

}

try {

//切歌前先重置,释放掉之前的资源

mediaPlayer.reset();

BLog.i(TAG, mList.get(position).path);

//设置播放音频的资源路径

mediaPlayer.setDataSource(mList.get(position).path);

//设置播放的歌名和歌手

tvSongName.setText(mList.get(position).song + " - " + mList.get(position).singer);

//如果内容超过控件,则启用跑马灯效果

tvSongName.setSelected(true);

//开始播放前的准备工作,加载多媒体资源,获取相关信息

mediaPlayer.prepare();

//开始播放音频

mediaPlayer.start();

//播放按钮控制

if (mediaPlayer.isPlaying()) {

btnPlay.setIcon(getDrawable(R.mipmap.icon_pause));

btnPlay.setIconTint(getColorStateList(R.color.gold_color));

} else {

btnPlay.setIcon(getDrawable(R.mipmap.icon_play));

btnPlay.setIconTint(getColorStateList(R.color.white));

}

} catch (IOException e) {

e.printStackTrace();

}

}

相信代码都能够看懂,播放歌曲之前先实例化,然后重置mediaPlayer,设置相关的信息之后就开始播放,这个时候也要处理一下按钮的状态。那么现在你再列表中就可以随意点击了,点击那一首就播放哪一首。现在的确是有播放音乐了,但是我也需要暂停啊。

④ 暂停音乐


在底部播放按钮btn_play的点击事件中进行处理。

case R.id.btn_play:

//控制音乐 播放和暂停

if (mediaPlayer == null) {

//没有播放过音乐 ,点击之后播放第一首

oldPosition = 0;

mCurrentPosition = 0;

mList.get(mCurrentPosition).setCheck(true);

mAdapter.changeState();

changeSong(mCurrentPosition);

} else {

//播放过音乐 暂停或者播放

if (mediaPlayer.isPlaying()) {

mediaPlayer.pause();

btnPlay.setIcon(getDrawable(R.mipmap.icon_play));

btnPlay.setIconTint(getColorStateList(R.color.white));

} else {

mediaPlayer.start();

btnPlay.setIcon(getDrawable(R.mipmap.icon_pause));

btnPlay.setIconTint(getColorStateList(R.color.gold_color));

}

}

break;

也要考虑到用户一进入这个页面直接点击底部播放按钮的因素,这样就直接播放列表中的第一首,至于记录当前歌曲的位置和播放进度,下一次进入时继续这个进度,这个功能放到后面来实现,先考虑这个页面的。

⑤ 自动下一曲


说道自动下一曲,就是没有人为干涉的情况下,当前歌曲播放完毕之后自行播放下一首。这里需要实现MediaPlayer的OnCompletionListener,r方法如下:

/**

  • 播放完成之后自动下一曲

  • @param mp

*/

@Override

public void onCompletion(MediaPlayer mp) {

int position = -1;

if (mList != null) {

if (mCurrentPosition == mList.size() - 1) {

//当前为最后一首歌时,则切换到列表的第一首歌

position = mCurrentPosition = 0;

} else {

position = ++mCurrentPosition;

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-F2ZpwFZO-1713771902989)]

[外链图片转存中…(img-k4PaD6mJ-1713771902990)]

[外链图片转存中…(img-M6Ao20yZ-1713771902991)]

[外链图片转存中…(img-XWUyeH3J-1713771902992)]

[外链图片转存中…(img-ULbZaMKv-1713771902993)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-7qBC1g8l-1713771902994)]

总结:

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。

  • BAT大厂面试题、独家面试工具包,

  • 资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter
    [外链图片转存中…(img-BiUZ0PSN-1713771902995)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值