Mt.Log

編入、編入後の勉強

【Kotlin】端末内の音楽をListViewに表示してみる

はじめに

今回はAndroid StudioでKotlinを使って端末内に入っている音楽をListViewに表示させたいと思います。
個人的に苦労したのでメモとして残しておきたいと思います。
コードが冗長になっているところもあると思いますが、使用する際には適宜修正を加えてください。
参考にしたサイトは最後にも載せています。

準備

アプリを作成し、EmptyActivityで開始します。
今回はToastしか使用していませんが、anko-comonを導入します。
build.gradleにそれぞれを追記します。

build.gradle(Project: ...)

ext.anko_version = "0.10.5"

build.gradle(Module: app)

implementation"org.jetbrains.anko:anko-common:$anko_version"

ストレージの許可

ストレージを利用するので、アプリの最初で許可を促す必要があります。
まず、マニフェストに下記を追加します。

AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

MainActivityに下記を追加します。
ここで使用しているgetSongs()はのちに作成しますので安心してください。

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= 23) {
            checkPermission()
        } else {
            //あとで作成する
            getSongs()
        }
}

fun checkPermission() {
        // 既に許可している
        if (ActivityCompat.checkSelfPermission(this,
                        Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            getSongs()
        } else {
            requestReadPermission()
        }// 拒否していた場合
    }

    private fun requestReadPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.READ_EXTERNAL_STORAGE)) {
            ActivityCompat.requestPermissions(this@MainActivity,
                    arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_PERMISSION)

        } else {
            toast("アプリ実行に許可が必要です")

            ActivityCompat.requestPermissions(this,
                    arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                    REQUEST_PERMISSION)
        }
    }

画面作成

今回はListViewを使用するのでMainActivityに加えてAdapter用のEmptyActivityを追加してください。ここではSongAdapterという名前で作成しましたのでこれから先はその名前で説明します。
まず、MainActivityですがListView(id: listView)さえあれば大丈夫です。あえていうならばlayout_widthとlayout_heightはmatch_constraintにした程度です。
続いて、SongAdapterの方ですが今回は曲のタイトルとURLを表示するリストを作成します。
マージンは適当にバランスを見てとっています。

adapter_song.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".SongAdapter">

    <TextView
        android:id="@+id/titleText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginLeft="32dp"
        android:layout_marginRight="16dp"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:text="TextView"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/uriText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginLeft="32dp"
        android:layout_marginRight="16dp"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:paddingBottom="16dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/titleText" />
</android.support.constraint.ConstraintLayout>

曲を取得する

ここまできたら早速曲を取得していきます。
まず、曲を取得したときに格納するためのデータ型を作成します。
New/Kotlin File,ClassでSong.ktという名前のクラスを作成します。内容は以下のとおりです。

Song.kt

class Song(var title: String, var uri: String) {

}

getter、setterがいらないのでこのままで大丈夫です。
表示したい要素を増やしたい場合はこの引数を増やしてください。
では、MainActivityにgetSong()を追加します。

MainActivity.kt

private fun getSongs(){
       //contentResolverの初期化
        val cr = contentResolver
        val items = arrayOf(
                MediaStore.Audio.Media._ID,
                MediaStore.Audio.Media.DATA,
                MediaStore.Audio.Media.ARTIST,
                MediaStore.Audio.Media.ALBUM,
                MediaStore.Audio.Media.DURATION,
                MediaStore.Audio.Media.TRACK,
                MediaStore.Audio.Media.TITLE
        )
        //格納するArrayListの宣言
        val songs = ArrayList<Song>()
        val cursor = cr.query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                items,
                null,
                null,
                null
        )
        cursor.moveToFirst()
        
        //データをsongsに格納する
        do{
            songs.add(
                    Song(
                    cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)),
                    cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))))
        }while(cursor.moveToNext())

        //タイトルでソート
        songs.sortBy { it.title }
    }

ListViewに格納

AdapterをArrayAdapterを継承して作成します。

SongAdapter.kt

class SongAdapter(context: Context , songs: ArrayList<Song>): ArrayAdapter<Song>(context, 0, songs) {
     //データを保持するホルダーのデータ型を宣言
    data class ViewHolder(val titleText: TextView, val uriText: TextView)
    
    //出力するためのgetView()をオーバーライド
    //※正直ほとんど決まり文句的な感じなのでListViewに関してはほかを参考にしてください
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
        var view = convertView
        var holder: ViewHolder

        val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

        if (view == null) {
            view = inflater.inflate(R.layout.adapter_song, parent, false)
            holder = ViewHolder(
                    view.titleText,
                    view.uriText
            )
            view.tag = holder
        } else {
            holder = view.tag as ViewHolder
        }

        //取得したpositionにタイトルとURLを表示
        val song = getItem(position) as Song
        holder.titleText.text = song.title
        holder.uriText.text = song.uri

        return view
    }
}

Adapterを作成できたので、getSongs()で取得した曲のリストをadapterにセットします。

MainActivity.kt

    //getSongs()の最後の行に追加(ソートの下)
    val adapter = SongAdapter(this, songs)
    listView.adapter = adapter

まとめ

これで作成できたと思います。
次回、これを再生するシンプルなMediaPlayerを作成したいと思います。
では次回........

参考サイト

code.tutsplus.com
www.usaco-pg.com