使用Anko创建音乐播放器应用

最终产品图片
您将要创造的

精通新的编程语言或库的一个好方法是尝试创建有用的东西。 在有关使用Anko简化Android开发的教程中,我向您介绍了Anko的特定于域的语言和帮助程序功能。 尽管我敢肯定您发现它们令人印象深刻,但是您可能仍然对在大型复杂应用中使用它们感到不安,因为它们与传统的Android类和方法有很大不同。

因此,今天,我们将使用Kotlin和Anko创建适用于Android的音乐播放器应用程序,该应用程序可以自动从用户设备中挑选和播放随机歌曲。 它的用户界面相当复杂,将具有多个相互影响的小部件,可以帮助您更好地了解Anko应用程序的工作方式。

先决条件

要遵循此分步教程,您需要:

  • 最新版本的Android Studio
  • 运行Android 5.0或更高版本的手机或平板电脑
  • 和一些MP3专辑

1.创建一个新项目

启动Android Studio,然后按“ 启动新的Android Studio项目”按钮以启动项目创建向导。 在下一个屏幕中,为您的应用命名,并确保选中了包括Kotlin支持字段。

Project creation wizard

接下来,定位API级别21或更高级别,然后选择清空活动模板。 因为我们不需要任何布局XML文件,所以请确保取消选择“ 生成布局文件”字段。

活动配置屏幕

最后,按Finish创建项目。

2.添加依赖项

要将Anko添加到项目中,请在app模块的build.gradle文件中添加以下implementation依赖项:

implementation 'org.jetbrains.anko:anko:0.10.1'

我们将使用Kotlin的协程异步执行一些操作,因此接下来为其添加依赖项。

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3'

我们的音乐播放器由于随机播放歌曲,因此需要该设备上所有可用歌曲的列表。 为了避免实现用于创建此类列表的逻辑,请添加DroidMelody (我专门为此教程创建的库)作为最后一个依赖项。 由于它是由Android Studio的默认存储库jcenter批准并发布的,因此添加它与添加任何其他依赖项没有什么不同。

implementation 'com.progur.droidmelody:droidmelody:1.0.2'

此外,我们将需要一些与媒体相关的图标,因此接下来请打开Vector Asset Studio。 在其中,导航到AV类别,然后选择带有播放,暂停和随机播放符号的图标。

AV section of the Vector Asset Studio

此时,项目的res / drawable文件夹中应存在以下文件:

  • ic_play_arrow_black_24dp.xml
  • ic_pause_black_24dp.xml
  • ic_shuffle_black_24dp.xml

3.请求权限

大多数用户将其歌曲存储在外部存储媒体上。 因此,在运行Android Marshmallow或更高版本的设备上,我们将需要在运行时显式请求READ_EXTERNAL_STORAGE权限。

但是,在请求权限之前,必须检查用户是否已授予它。 您可以通过在活动的onCreate()方法内调用ContextCompat.checkSelfPermission()方法来实现。 如果尚未授予该权限,则可以通过调用ActivityCompat.requestPermissions()方法来请求它。

相应地,添加以下代码:

if(ContextCompat.checkSelfPermission(this, 
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
    
    // Ask for the permission
    ActivityCompat.requestPermissions(this,
            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
            0)

} else {
    // Start creating the user interface
    createPlayer()
}

请注意,如果已授予权限,则上面的代码将调用名为createPlayer()的方法。 我们将在下一步中创建该方法。

在请求权限之后,您必须重写活动的onRequestPermissionsResult()方法以确定用户是否已接受您的请求。 如果这样做,则必须再次调用createPlayer()方法。 如果没有,请使用longToast()longToast()帮助器显示错误消息,然后关闭应用程序。

override fun onRequestPermissionsResult(requestCode: Int,
                            permissions: Array<out String>,
                            grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, 
                            permissions, grantResults)

    if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        createPlayer()
    } else {
        longToast("Permission not granted. Shutting down.")
        finish()
    }
}

4.提取所有歌曲

现在是时候定义createPlayer()方法了,它将负责我们应用程序的外观和功能。

private fun createPlayer() {
    // More code here
}

在该方法内部,您需要做的第一件事是生成设备上所有可用歌曲的列表。 如您所料,这是一个可能长时间运行的操作,因此可以使用async()函数在新的协程中启动它。

在协程内部,创建DroidMelody的SongFinder类的新实例,并调用其prepare()方法,该方法使用您活动的内容解析器实际查找所有歌曲并将它们放置在名为allSongs的列表中。 方法完成后,您可以简单地从协程返回allSongs 。 以下代码向您展示了如何:

val songsJob = async {
    val songFinder = SongFinder(contentResolver)
    songFinder.prepare()
    songFinder.allSongs
}

上面的协程异步运行。 要等待其结果,必须在使用launch()函数创建的另一个协程中调用await()方法。 因为我们将使用结果来创建应用程序的用户界面,所以新协程应在UI线程上运行。 通过将kotlinx.coroutines.experimental.android.UI作为参数传递给launch()

launch(kotlinx.coroutines.experimental.android.UI) {
    val songs = songsJob.await()

    // More code here
}

现在,您有了Song对象的列表。 每个Song对象将具有与其引用的歌曲有关的几个重要细节,例如其标题,艺术家,专辑封面和URI。

5.创建Anko组件

默认情况下,Anko的DSL仅在活动的onCreate()方法内直接可用。 为了能够在createPlayer()方法中使用它,您可以依赖UI()函数或创建一个新的Anko组件。 在本教程中,我们将使用后一种方法,因为它更可重用。

要创建新的Anko组件,必须扩展抽象的AnkoComponent类并覆盖其createView()方法,在该方法中您可以访问DSL。

val playerUI = object:AnkoComponent<MainActivity> {
    override fun createView(ui: AnkoContext<MainActivity>) 
        = with(ui) {
            // DSL code here
        }
}

6.定义用户界面

由于我们的应用是随机播放的音乐播放器,而不是可以与播放列表配合使用的音乐播放器,因此它的用户界面会有所不同。 这是它将包含的可见小部件:

  • ImageView小部件以显示当前播放的歌曲的专辑封面
  • 一个ImageButton小部件,允许用户暂停或恢复歌曲
  • 一个ImageButton小部件,允许用户选择另一首随机歌曲
  • 一个TextView小部件以显示歌曲的标题
  • 一个TextView小部件以显示歌曲的艺术家的姓名

因此,将以下字段添加到Anko组件:

var albumArt: ImageView? = null

var playButton: ImageButton? = null
var shuffleButton:ImageButton? = null

var songTitle: TextView? = null
var songArtist: TextView? = null

另外,我们将需要一个RelativeLayout和几个LinearLayout容器来LinearLayout上述小部件并在它们之间建立关系。 下图显示了我们接下来将创建的视图层次结构:

View hierarchy of the music player

由于RelativeLayout小部件位于视图层次结构的根部,因此必须首先通过在Anko组件的createView()方法内添加以下代码来创建它:

relativeLayout {
    backgroundColor = Color.BLACK

    // More code here
}

请注意,我们已经使用了小部件的backgroundColor属性为其赋予黑色。 在本教程中,我们将使用几个这样的属性来使我们的应用程序看起来更好。 您可以自由更改它们的值以匹配您的首选项。

RelativeLayout小部件内,添加用于专辑封面的ImageView小部件。 它应该占据屏幕上所有可用的空间,因此在添加它之后使用lparams()方法,并将matchParent常量两次传递给它,一次用于宽度,一次用于高度。 这是如何做:

albumArt = imageView {
    scaleType = ImageView.ScaleType.FIT_CENTER
}.lparams(matchParent, matchParent)

顾名思义, lparams()方法允许您指定应与小部件关联的布局参数。 使用它,您可以快速指定详细信息,例如小部件应具有的页边距,其尺寸和位置。

接下来,通过调用verticalLayout()函数创建具有垂直方向的LinearLayout小部件,并将TextView小部件添加到其中。 布局必须放置在屏幕的底部,因此必须在指定布局参数的同时调用alignParentBottom()函数。

verticalLayout {
    backgroundColor = Color.parseColor("#99000000")

    songTitle = textView {
        textColor = Color.WHITE
        typeface = Typeface.DEFAULT_BOLD
        textSize = 18f
    }

    songArtist = textView {
        textColor = Color.WHITE
    }

    // More code here

}.lparams(matchParent, wrapContent) {
    alignParentBottom()
}

同样,通过调用linearLayout()函数以水平方向创建LinearLayout小部件,并向其中添加两个ImageButton小部件。 确保使用按钮的imageResource属性指定应显示的图标。 以下代码向您展示了如何:

linearLayout {

    playButton = imageButton {
        imageResource = R.drawable.ic_play_arrow_black_24dp
        onClick {
            playOrPause()
        }
    }.lparams(0, wrapContent, 0.5f)
    
    shuffleButton = imageButton {
        imageResource = R.drawable.ic_shuffle_black_24dp
        onClick {
            playRandom()
        }
    }.lparams(0, wrapContent, 0.5f)
    
}.lparams(matchParent, wrapContent) {
    topMargin = dip(5)
}

您可以看到上面的代码具有两个ImageButton小部件的click事件处理程序。 在处理程序内部,调用了两个直观命名的方法: playOrPause()playRandom() 。 我们将在接下来的几个步骤中创建它们。

至此,我们已经完成了应用外观的定义。

7.播放歌曲

我们的应用仍然无法播放任何音乐。 让我们通过创建playRandom()方法来解决此问题。

fun playRandom() {
    // More code here
}

我们将使用MediaPlayer类的实例来播放音乐。 这是一个相当昂贵的资源,应在用户关闭应用程序时将其释放。 因此,必须将其定义为活动的字段(而不是Anko组件onDestroy() ,并在活动的onDestroy()方法内发布。

private var mediaPlayer: MediaPlayer? = null

override fun onDestroy() {
    mediaPlayer?.release()
    super.onDestroy()
}

现在,在playRandom()方法内部,我们可以从前面生成的歌曲列表中选择一首随机歌曲,只需对列表进行混洗并选择第一个元素即可。 这种方法不是很有效,但是非常简洁。

Collections.shuffle(songs)
val song = songs[0]

现在,您可以使用新选择的歌曲的URI初始化媒体播放器。 另外,使用setOnCompletionListener()方法来确保新的随机歌曲在当前歌曲完成后立即开始播放。

mediaPlayer?.reset()
mediaPlayer = MediaPlayer.create(ctx, song.uri)
mediaPlayer?.setOnCompletionListener {
    playRandom()
}

ImageViewTextView小部件的内容也必须更新,以显示与新歌曲关联的详细信息。

albumArt?.imageURI = song.albumArt
songTitle?.text = song.title
songArtist?.text = song.artist

最后,要真正开始播放歌曲,您可以调用媒体播放器的start()方法。 现在也是更新ImageButton小部件以将“播放”图标更改为“暂停”图标的合适时机。

mediaPlayer?.start()
playButton?.imageResource = R.drawable.ic_pause_black_24dp

8.暂停和恢复

在较早的步骤中,我们在ImageButton小部件之一的单击处理程序内调用了一个名为playOrPause()的方法。 将其定义为Anko组件的另一种方法。

fun playOrPause() {
    // More code here
}

您需要在此方法内实现的逻辑应该很明显。 如果媒体播放器已经在播放歌曲,可以使用isPlaying属性进行检查,则调用其pause()方法并在ImageButton显示“播放”图标。 否则,调用start()方法并显示“暂停”图标。

val songPlaying: Boolean? = mediaPlayer?.isPlaying

if(songPlaying == true) {
    mediaPlayer?.pause()
    playButton?.imageResource = R.drawable.ic_play_arrow_black_24dp
} else {
    mediaPlayer?.start()
    playButton?.imageResource = R.drawable.ic_pause_black_24dp
}

我们的Anko组件现已准备就绪。

9.显示Anko组件

仅创建Anko组件是不够的。 您还必须确保通过调用setContentView()方法并将活动传递给它来呈现它。 现在,您可以将主要活动传递给它。

(可选)如果您希望应用程序在用户打开它后立即开始播放歌曲,则可以立即再次调用playRandom()方法。

playerUI.setContentView(this@MainActivity)
playerUI.playRandom()

我们的应用程序已准备就绪。 如果在具有一个或多个带有正确格式的ID3标签和嵌入​​式专辑封面的MP3文件的设备上运行它,则应该看到类似以下内容:

A screenshot of the app

结论

在本教程中,您学习了如何仅使用Anko和Kotlin创建具有复杂视图层次的音乐播放器应用。 在执行此操作时,您同时使用了两项高级功能,例如协程和布局参数助手。 您还学习了如何通过创建Anko组件使Anko代码更具模块化和可重用性。

随时为该应用程序添加更多功能或修改其外观以使其具有个人风格!

翻译自: https://code.tutsplus.com/tutorials/create-a-music-player-app-with-anko--cms-30200

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值