<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android實(shí)現(xiàn)音樂歌詞變色效果

          共 4648字,需瀏覽 10分鐘

           ·

          2021-09-17 04:01

          效果圖


          我們?yōu)檫@個(gè)自定義控件起名為:SongLyricTextView。要想實(shí)現(xiàn)這個(gè)效果唯一要解決的問題是如何計(jì)算播放文字變色的進(jìn)度,我就直接拋磚引玉,說一下自己的實(shí)現(xiàn)方案。?

          1. 指定播放開始索引和結(jié)束索引的坐標(biāo),例如從0到10;

          2. 截取子字符串從0到10,計(jì)算子字符串的寬度playWidth;

          3. 開啟差值器,從0到playWidth,設(shè)置duration;

          4. 通過差值器不停返回的寬度,計(jì)算canvas中需要繪制變色區(qū)域;

          5. 在原本黑色的文字上,繪制一遍變色的字;


          接下來就按照這個(gè)步驟實(shí)現(xiàn)demo中的效果,首先我們定義基本的屬性:
              /**     * 文字未播放的顏色     * */    private val normalColor = Color.parseColor("#333333")
          /** * 文字播放的顏色 * */ private val playColor = Color.parseColor("#fec403")
          /** * 要變色的寬度 * */ private var consumeWidth: Float = 0f
          /** * 是否正在播放中 * */ private var isPlaying = false
          private val mPaint = paint
          /** * 要變色的區(qū)域 * */ private val path = Path()
          /** * 負(fù)責(zé)變色差值器 * */ private val animator by lazy { val animator = ValueAnimator.ofFloat(0f, 1f) animator.interpolator = LinearInterpolator() animator.addUpdateListener { // 開始重繪 consumeWidth = it.animatedValue as Float invalidate() } animator }

          接下來,把剛才提到的前三步整合為一個(gè)開始播放的方法:

          1. 指定播放開始索引和結(jié)束索引的坐標(biāo),例如從0到10;

          2. 截取子字符串從0到10,計(jì)算子字符串的寬度playWidth;

          3. 開啟差值器,從0到playWidth,設(shè)置duration;

          /**     * 開始播放     *     * @param startIndex 開始的文字的索引     * @param endIndex 結(jié)束的文字的索引     * @param duration 播放時(shí)長     * */    fun startPlayLine(startIndex: Int, endIndex: Int, duration: Long) {        isPlaying = true        if (startIndex == -1) return        // 計(jì)算從開始位置到開始的文字寬度,可以理解為已經(jīng)播放完畢的文字寬度        val startWidth = mPaint.measureText(this.text.substring(0, startIndex))        // 計(jì)算即將播放截止的文字的寬度,其實(shí)也可以直接this.text.substring(0, endIndex),問題不大        val endWidth = startWidth + mPaint.measureText(this.text.substring(startIndex, endIndex))        // 啟動(dòng)差值器        animator.setFloatValues(startWidth, endWidth)        animator.duration = if (duration > 0) duration else 1000        animator.start()    }
          /** * 停止播放 * */ fun stopPlay() { isPlaying = false animator.cancel() invalidate() }

          有開始播放的方法,那就肯定要有停止播放,代碼也非常的簡單。因?yàn)橹肮ぷ鞯男枨笫菑牡谝痪溟_始播放,如果你想要播放中間某一段文字,可以對(duì)demo中的代碼自己做一些修改。

          最后是計(jì)算canvas繪制的區(qū)域:
           override fun onDraw(canvas: Canvas) {        // 調(diào)用一遍super,把黑色的字寫一遍        mPaint.color = normalColor        super.onDraw(canvas)
          if (layout == null) { invalidate() return } // 是否是播放狀態(tài) if (isPlaying) { path.reset() // 因?yàn)樾枨笫钦胁シ牛钥梢酝ㄟ^行數(shù)來遍歷 val lineCount = layout.lineCount val content = text.toString() for (i in 0 until lineCount) { // 計(jì)算一行文字的寬度 val lineWidth = mPaint.measureText( content.substring(layout.getLineStart(i), layout.getLineEnd(i)) ) // 如果已經(jīng)播放過了 if (lineWidth <= consumeWidth) { // 如果是之前已經(jīng)變色區(qū)域,直接添加到path中 // 減去這一行的寬度 consumeWidth -= lineWidth path.addRect( layout.getLineLeft(i), layout.getLineTop(i).toFloat(), layout.getLineRight(i), layout.getLineBottom(i).toFloat(), Path.Direction.CCW ) } else { // 如果該行正好是要變色的行,直接改變顏色 // 把需要的consumeWidth放入path中 path.addRect( layout.getLineLeft(i), layout.getLineTop(i).toFloat(), layout.getLineLeft(i) + consumeWidth, layout.getLineBottom(i).toFloat(), Path.Direction.CCW ) break } } // 設(shè)置需要繪制的區(qū)域,繪制一遍變色的字 canvas.clipPath(path) mPaint.color = playColor layout.draw(canvas) } }

          最后看一下demo中具體使用SongLyricTextView的用法:
          // 開始播放start.setOnClickListener {     val lyricList = lyric.split("\n")     var startIndex = 0     var delayTime = 0L     lyricList.forEach {         val startIndexTemp = startIndex         val duration = (500 + Math.random() * 500).toLong()         text.postDelayed(             {                 text.startPlayLine(                     startIndexTemp,                     min(lyric.length, startIndexTemp + it.length + 1), // 因?yàn)橛袀€(gè)換行符,所以 + 1                     duration                 )             },             delayTime         )         delayTime += duration + 50         startIndex += it.length + 1     }}// 停止播放end.setOnClickListener {    text.stopPlay()}

          源碼地址:
          https://github.com/li504799868/SongLyricTextView

          到這里就結(jié)束啦。
          瀏覽 99
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩国产中文精品字幕自在 | 亚洲天堂一区二区三区在线观看 | 免费观看黄色毛片 | 欧美老妇人性爱网站 | 亚洲欧美福利 |