自定義MediaPlayer音頻播放器

做安卓家用鏡項(xiàng)目,自定義一款播放器,方便以后修改。
包括功能

0、查詢本地所有音樂,篩選小于30s的音頻文件;

1、播放、暫停;

2、上一曲、下一曲;

3、隨機(jī)、順序播放;

4、當(dāng)前播放時(shí)間、總時(shí)間、進(jìn)度條顯示;

5、進(jìn)度條拖動(dòng)播放;

封裝模型

data class MediaEntity (

        var id: Int?,

        var title: String?,

        var displayName: String?,

        var path: String?,

        var duration: Int?,

        var albums: String?,

        var artist: String?,

        var singer: String?,

        var size: Long?

): Serializable

封裝為anko使用

inline fun ViewManager.lfmusicPlayer() = lfmusicPlayer() {}

inline fun ViewManager.lfmusicPlayer(init: LFMusicPlayer.() -> Unit): LFMusicPlayer {

    return ankoView({ LFMusicPlayer(it) },0,init)

}
image.png

播放器

class LFMusicPlayer: FrameLayout {

    object ViewID {

        val dt = 181251758

        val name = dt + 101

        val play = dt + 102

        val progress = dt + 103

        val currentTime = dt + 104

        val totalTime = dt + 105

        val random = dt + 106

    }

    enum class PlayControl {

        PLAY, NEXT, PREVIOUS

    }

    private var progressTimer: Timer? = null

    private var isRecycle = false

    private var mediaPlayer = MediaPlayer()

    private var cnt: Context

    private var currentIndex = -1

    private var currentPosition = 0

    private var musicList: List<MediaEntity> = ArrayList<MediaEntity>()

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {

        this.cnt = context

        //UI

        verticalLayout {

            linearLayout {

                imageView {

                    imageResource = R.mipmap.ic_random

                    id = ViewID.random

                    onClick { randomOrReclycle() }

                }.lparams(dip(0), matchParent) {

                    weight = 1f

                    margin = dip(5)

                }

                imageView {

                    imageResource = R.mipmap.ic_previous

                    onClick {

                        playLocalMusic(PlayControl.PREVIOUS)

                    }

                }.lparams(dip(0), matchParent) {

                    weight = 1f

                    margin = dip(5)

                }

                imageView {

                    imageResource = R.mipmap.ic_play

                    id = ViewID.play

                    onClick {

                        playLocalMusic(PlayControl.PLAY)

                    }

                }.lparams(dip(0), matchParent) {

                    weight = 1f

                    margin = dip(5)

                }

                imageView {

                    imageResource = R.mipmap.ic_next

                    onClick {

                        playLocalMusic(PlayControl.NEXT)

                    }

                }.lparams(dip(0), matchParent) {

                    weight = 1f

                    margin = dip(5)

                }

                imageView {

                    imageResource = R.mipmap.ic_list

                }.lparams(dip(0), matchParent) {

                    weight = 1f

                    margin = dip(5)

                }

            }.lparams(matchParent, dip(0)) {

                weight = 1f

                leftMargin = dip(20)

                rightMargin = dip(20)

            }

            linearLayout {

                textView {

                    id = ViewID.name

                    textSize = 17f

                    textColor = Color.WHITE

//                    text = musicList[currentIndex].displayName.toString()

                }.lparams(wrapContent, wrapContent)

            }.lparams(matchParent, dip(0)) {

                weight = 1f

                leftMargin = dip(20)

                rightMargin = dip(20)

            }

            linearLayout {

                //2020110

                textView {

                    textSize = 12f

                    textColor = Color.WHITE

                    text = "0:0"

                    id = ViewID.currentTime

                }.lparams(dip(0), matchParent) {

                    weight = 2f

                }

                seekBar {

//                    progressDrawable = context.resources.getDrawable(R.drawable.seek_bar_progress, null)

//                    thumb = context.resources.getDrawable(R.drawable.seek_bar_thumb, null)

                    id = ViewID.progress

                    max = 100

                    progress = 0

//                    secondaryProgress = 80

                    isIndeterminate = false

                }.lparams(dip(0), wrapContent) {

                    weight = 11f

                }

                textView {

                    textSize = 12f

                    textColor = Color.WHITE

//                    text = "4:32"

                    id = ViewID.totalTime

                }.lparams(dip(0), matchParent) {

                    weight = 2f

                }

            }.lparams(matchParent, dip(0)) {

                weight = 1f

                leftMargin = dip(20)

                rightMargin = dip(20)

            }

        }

        this.localMusicData()

        this.seekBarChangeAction()

    }

    private fun randomOrReclycle() {

        this.isRecycle = !this.isRecycle

        val iv = find<ImageView>(ViewID.random)

        doAsync { uiThread {

            if (isRecycle) {

                iv.imageResource = R.mipmap.ic_recycle

            }else {

                iv.imageResource = R.mipmap.ic_random

            }

        } }

    }

    private fun seekBarChangeAction() {

        val seekBar = find<SeekBar>(ViewID.progress)

        val tv = find<TextView>(ViewID.currentTime)

        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {

            override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {

            }

            override fun onStartTrackingTouch(p0: SeekBar?) {

            }

            override fun onStopTrackingTouch(p0: SeekBar?) {

                currentPosition = ((p0?.progress!!.toFloat()/100)*mediaPlayer.duration.toFloat()).toInt()

                playLocalMusic(PlayControl.PLAY, true)

            }

        })

    }

    private fun localMusicData() {

        this.musicList = this.getAllMediaList()

        if (this.musicList.count() < 1) {

            currentIndex = -1

        }else {

            currentIndex = 0

        }

        this.displayMusicName(currentIndex)

    }

    //顯示歌名

    private fun displayMusicName(index: Int) {

        val tv = find<TextView>(ViewID.name)

        val totalTv = find<TextView>(ViewID.totalTime)

        if (index < 0) {

            tv.text = "沒有本地音樂"

        }else {

            tv.text = this.musicList[index].displayName.toString()

            val m = (this.musicList[index].duration!!/1000)/60

            val s = (this.musicList[index].duration!!/1000)%60

            totalTv.text = m.toString() + ":" + s.toString()

        }

        //一定要實(shí)現(xiàn)此錯(cuò)誤處理方法,否則會(huì)很多時(shí)候比如reset()調(diào)用OnCompletion方法?。?!

        this.mediaPlayer.setOnErrorListener(object : MediaPlayer.OnErrorListener {

            override fun onError(p0: MediaPlayer?, p1: Int, p2: Int): Boolean {

                return true

            }

        })

        this.mediaPlayer.setOnCompletionListener(object : MediaPlayer.OnCompletionListener {

            override fun onCompletion(p0: MediaPlayer?) {

                playLocalMusic(PlayControl.NEXT)

            }

        })

    }

    //獲取本地(不包括SD卡)的音樂文件

    private fun getAllMediaList(): List<MediaEntity> {

        var cursor: Cursor? = null

        var mediaList = ArrayList<MediaEntity>()

        try {

            cursor = cnt.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null,

                    null, MediaStore.Audio.AudioColumns.IS_MUSIC)

            if (cursor == null) {

                return mediaList

            }

            var count = cursor.count

            if (count <= 0) {

                return mediaList

            }

            mediaList = ArrayList()

            var mediaEntity: MediaEntity

            while (cursor.moveToNext()) {

                mediaEntity = MediaEntity(null, null, null, null, null, null, null, null, null)

                mediaEntity.id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media._ID))

                mediaEntity.title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE))

                mediaEntity.displayName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))

                mediaEntity.duration = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION))

                mediaEntity.size = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE))

                if (!checkIsMusic(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)), cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)))) {

                    continue

                }

                mediaEntity.artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))

                mediaEntity.path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))

                mediaList.add(mediaEntity)

            }

        }catch (e: Exception) {

        }finally {

            if (cursor != null) {

                cursor.close()

            }

        }

        return mediaList

    }

    fun checkIsMusic(time: Int, size: Long): Boolean {

        if (time <= 0 || size <= 0) {

            return false

        }

        var time = time/1000

        var minute = time/60

        var second = time%60

        minute %= 60

        if (minute <= 0 && second <= 30) {

            return false

        }

        if (size <= 1024 * 1024) {

            return false

        }

        return true

    }

    //判斷本地是否有音樂,再進(jìn)行播放選擇

    private fun playLocalMusic(control: PlayControl, isSeekBar: Boolean? = false) {

        if (this.musicList.count() < 1) {

            val builder = AlertDialog.Builder(this.cnt)

            builder.setTitle("沒有本地音樂").create().show()

        }else {

            var index: Int

            when (control) {

                PlayControl.PLAY -> {

                    //是否第一次播放

                    if (currentIndex < 0) currentIndex = 0

                    //判斷是暫停還是播放操作

                    if (this.mediaPlayer.isPlaying && (!isSeekBar!!)) {

                        this.mediaPlayer.pause()

                        currentPosition = this.mediaPlayer.currentPosition

                        this.playStateChange()

                    }else {

                        this.playAssignMusic(currentIndex)

                    }

                }

                PlayControl.NEXT -> {

                    if (isRecycle) {

                        currentIndex += 1

                    }else {

                        //隨機(jī)

                        currentIndex = (0..(this.musicList.count()-1)).shuffled().last()

                    }

                    index = currentIndex%this.musicList.count()

                    currentPosition = 0

                    this.playAssignMusic(index)

                }

                PlayControl.PREVIOUS -> {

                    currentIndex -= 1

                    if (currentIndex < 0) {

                        currentIndex += this.musicList.count()

                    }

                    index = currentIndex%this.musicList.count()

                    currentPosition = 0

                    this.playAssignMusic(index)

                }

            }

        }

    }

    //播放指定音樂

    private fun playAssignMusic(index: Int) {

//        AlertDialog.Builder(this.cnt).setTitle("播放開始" + index.toString()).show()

        try {

            // 切歌之前先重置,釋放掉之前的資源。注意:reset會(huì)觸發(fā)OnCompletion監(jiān)聽方法?。?!

            this.mediaPlayer.reset()

            this.mediaPlayer.setDataSource(this.musicList[index].path)

            this.mediaPlayer.prepareAsync()

            //裝載完畢,開始播放

            this.mediaPlayer.setOnPreparedListener(object : MediaPlayer.OnPreparedListener {

                override fun onPrepared(p0: MediaPlayer?) {

                    //記憶播放

                    mediaPlayer.seekTo(currentPosition)

                    mediaPlayer.start()

                    //變更歌名

                    doAsync {

                        uiThread {

                            displayMusicName(index)

                            playStateChange()

                        }

                    }

                }

            })

            currentIndex = index

        }catch (e: Exception) {

            doAsync { uiThread {

                AlertDialog.Builder(cnt).setTitle("播放錯(cuò)誤").create().show()

            } }

        }

    }

    private fun playStateChange() {

        val img = find<ImageView>(ViewID.play)

        if (this.mediaPlayer.isPlaying) {

            img.imageResource = R.mipmap.ic_stop

            //進(jìn)度設(shè)置

            playingProgress()

        }else {

            img.imageResource = R.mipmap.ic_play

            //暫停顯示操作

            stopProgressTimer()

        }

        //總時(shí)長(zhǎng)只設(shè)置一次

        val tv = find<TextView>(ViewID.totalTime)

        val m = (this.mediaPlayer.duration/1000)/60

        val s = (this.mediaPlayer.duration/1000)%60

        tv.text = m.toString() + ":" + s.toString()

    }

    private fun playingProgress() {

        val tv = find<TextView>(ViewID.currentTime)

        val seekBar = find<SeekBar>(ViewID.progress)

        //定時(shí)器,1秒獲取一次

        if (progressTimer == null) {

            progressTimer = Timer()

        }

        val task = object : TimerTask() {

            override fun run() {

                //獲取當(dāng)前播放位置

                val m = (mediaPlayer.currentPosition/1000)/60

                val s = (mediaPlayer.currentPosition/1000)%60

                val progress = (mediaPlayer.currentPosition.toFloat()/mediaPlayer.duration.toFloat())

                doAsync { uiThread {

                    tv.text = m.toString() + ":" + s.toString()

                    seekBar.progress = (progress*100).toInt()

                } }

            }

        }

        progressTimer?.schedule(task, 0, 1000)

    }

    private fun stopProgressTimer() {

        progressTimer?.cancel()

        progressTimer = null

    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容