美文网首页
自定义MediaPlayer音频播放器

自定义MediaPlayer音频播放器

作者: 丿沧海一粟丿 | 来源:发表于2018-12-26 16:54 被阅读0次

做安卓家用镜项目,自定义一款播放器,方便以后修改。
包括功能

0、查询本地所有音乐,筛选小于30s的音频文件;

1、播放、暂停;

2、上一曲、下一曲;

3、随机、顺序播放;

4、当前播放时间、总时间、进度条显示;

5、进度条拖动播放;

封装模型

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()

        }

        //一定要实现此错误处理方法,否则会很多时候比如reset()调用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

    }

    //判断本地是否有音乐,再进行播放选择

    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 {

                        //随机

                        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会触发OnCompletion监听方法!!!

            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("播放错误").create().show()

            } }

        }

    }

    private fun playStateChange() {

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

        if (this.mediaPlayer.isPlaying) {

            img.imageResource = R.mipmap.ic_stop

            //进度设置

            playingProgress()

        }else {

            img.imageResource = R.mipmap.ic_play

            //暂停显示操作

            stopProgressTimer()

        }

        //总时长只设置一次

        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)

        //定时器,1秒获取一次

        if (progressTimer == null) {

            progressTimer = Timer()

        }

        val task = object : TimerTask() {

            override fun run() {

                //获取当前播放位置

                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

    }

}

相关文章

  • 自定义MediaPlayer音频播放器

    做安卓家用镜项目,自定义一款播放器,方便以后修改。包括功能 0、查询本地所有音乐,筛选小于30s的音频文件; 1、...

  • 多媒体工程师必备技能

    1.Android 平台多媒体框架; MediaPlayer 播放器全面剖析(一)MediaPlayer 播放器全...

  • 视频播放

    视频播放器的实现有多种方式,此处主要针对主流方式实现一个播放器 MediaPlayer框架

  • IJKPlayer编译实践

    最近在做音频相关的项目,起初,音乐播放器使用的是Android系统本身的MediaPlayer,在开发中使用的本来...

  • Android音视频<第三篇>:简单音频播放实现

    播放音频需要借助MediaPlayer对象,MediaPlayer可以播放本地音频和网络音频。文本的目的是为了尝试...

  • Android MediaPlayer 播放音频

    本文链接: Android MediaPlayer 播放音频 主要介绍使用MediaPlayer播放音频的方式。关...

  • Android中流媒体和传感器

    一、音频播放 1)MediaPlayer Tips:该播放器同时只能播放一个音乐文件,文件大小并没有限制。 Tip...

  • Android 音频倍速探究

    系统自带播放器MediaPlayer,虽支持格式较少,但我们项目的mp3音频格式刚好满足,随着时间流失需求变迁,要...

  • 音频播放AudioTrack之入门篇

    音频播放 音频播放声音分为MediaPlayer和AudioTrack两种方案的。MediaPlayer可以播放多...

  • Android Audio系统笔记

    一、Audio框架 APP层: 音乐播放器,视频播放器。播放器一般使用MediaPlayer,MediaRecor...

网友评论

      本文标题:自定义MediaPlayer音频播放器

      本文链接:https://www.haomeiwen.com/subject/nctklqtx.html