自定義MediaPlayer音頻播放器

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

0、查詢本地所有音樂(lè),篩選小于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 = "沒(méi)有本地音樂(lè)"

        }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卡)的音樂(lè)文件

    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

    }

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

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

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

            val builder = AlertDialog.Builder(this.cnt)

            builder.setTitle("沒(méi)有本地音樂(lè)").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)

                }

            }

        }

    }

    //播放指定音樂(lè)

    private fun playAssignMusic(index: Int) {

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

        try {

            // 切歌之前先重置,釋放掉之前的資源稍浆。注意:reset會(huì)觸發(fā)OnCompletion監(jiān)聽(tīng)方法T芈怠!衅枫!

            this.mediaPlayer.reset()

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

            this.mediaPlayer.prepareAsync()

            //裝載完畢嫁艇,開(kāi)始播放

            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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弦撩,隨后出現(xiàn)的幾起案子步咪,更是在濱河造成了極大的恐慌,老刑警劉巖益楼,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猾漫,死亡現(xiàn)場(chǎng)離奇詭異点晴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)悯周,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門粒督,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人禽翼,你說(shuō)我怎么就攤上這事屠橄。” “怎么了闰挡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵锐墙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我长酗,道長(zhǎng)溪北,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任夺脾,我火速辦了婚禮之拨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劳翰。我一直安慰自己敦锌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布佳簸。 她就那樣靜靜地躺著乙墙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪生均。 梳的紋絲不亂的頭發(fā)上听想,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音马胧,去河邊找鬼汉买。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佩脊,可吹牛的內(nèi)容都是我干的蛙粘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼威彰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼出牧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歇盼,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舔痕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伯复,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慨代,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啸如。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侍匙。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖组底,靈堂內(nèi)的尸體忽然破棺而出丈积,到底是詐尸還是另有隱情筐骇,我是刑警寧澤债鸡,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站铛纬,受9級(jí)特大地震影響厌均,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜告唆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一棺弊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擒悬,春花似錦模她、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至僧凤,卻和暖如春畜侦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躯保。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工旋膳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人途事。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓验懊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親尸变。 傳聞我的和親對(duì)象是個(gè)殘疾皇子义图,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355