Android自定义歌词显示

直接代码:

package com.example.musicplayer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.example.utils.Lyric;

import java.util.List;

public class LyricView extends View {
    private int height,width;
    private int offset = 0;
    private float textSize = 31;
    private int LineHeight = 45;
    private int currentLine = 0;
    private int targetOffset = 0;
    private OnChangeProgress onChangeProgress;
    private MediaPlayer mediaPlayer;

    private Context context;
    private Paint hPaint, dPaint,pPaint;
    private List<Lyric> LyricList;
    private boolean isTouch = false;
    private int offset_touch = 0;

    onLyricChangeListener lyricChangeListener;

    public void setLyricChangeListener(onLyricChangeListener lyricChangeListener) {
        this.lyricChangeListener = lyricChangeListener;
    }

    public void setCurrentLine(int currentLine){
        if (currentLine == this.currentLine)return;
        this.currentLine = currentLine;
        targetOffset = currentLine * LineHeight;
        invalidate();
    }

    public void setMediaPlayer(android.media.MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
        mediaPlayer.setOnCompletionListener(listener);
    }
    MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) {
            currentLine = 0;
            setCurrentLine(currentLine);
            mediaPlayer.seekTo(0);
            mediaPlayer.start();
            Log.e("TAG","重新播放");
        }
    };

    public void setLyricList(List<Lyric> LyricList) {
        offset = 0;
        currentLine = 0;
        targetOffset = 0;
        this.LyricList = LyricList;
        invalidate();
    }
    public void reset(){
        currentLine = 0;
        targetOffset = 0;
        offset = 0;
        invalidate();
    }

    public LyricView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public LyricView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    private void init(){
        hPaint = new Paint();
        hPaint.setTextSize(textSize*1.3f);
        hPaint.setColor(context.getResources().getColor(R.color.highlighted_color));
        hPaint.setAntiAlias(true);

        dPaint = new Paint();
        dPaint.setTextSize(textSize);
        dPaint.setColor(context.getResources().getColor(R.color.default_color));
        dPaint.setAntiAlias(true);

        pPaint = new Paint();
        pPaint.setTextSize((float) (textSize*0.8));
        pPaint.setColor(context.getResources().getColor(R.color.play_color));
        pPaint.setAntiAlias(true);
    }

    public void setTextSize(float textSize) {
        this.textSize = textSize;
        hPaint.setTextSize(textSize*1.3f);
        dPaint.setTextSize(textSize);
        LineHeight = (int) (textSize * 1.8);
    }

    int down_y;
    int move_y;
    int preOffset = 0;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                if (isTouch){
                    if (rectF.contains(new RectF(event.getX()-1,event.getY()-1,event.getX()+1,event.getY()+1))){
                        if (onChangeProgress!=null)onChangeProgress.onChanged(Math.abs(offset/LineHeight));

                        currentLine = Math.abs(offset/LineHeight);
                        mediaPlayer.seekTo(LyricList.get(currentLine).getTime());
                        isTouch = false;
                        if (currentLine<LyricList.size())
                            if (lyricChangeListener!=null)lyricChangeListener.onChange(LyricList.get(currentLine).getLyric());
                        invalidate();
                    }
                }
                handler.removeCallbacks(runnable);
                isTouch = true;
                down_y = (int) event.getY();
                preOffset = offset;
                break;
            case MotionEvent.ACTION_MOVE:
                move_y = (int) event.getY();
                offset = preOffset - (move_y - down_y);
                if (offset<=0)offset=0;
                else if (offset>LineHeight*(LyricList.size()-1))offset = LineHeight*(LyricList.size()-1);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                handler.postDelayed(runnable,2000);
                break;
        }
        return true;
    }
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            isTouch = false;
            preOffset = 0;
            invalidate();
        }
    };

    RectF rectF = new RectF();
    Path path = new Path();
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        targetOffset = currentLine * LineHeight;
        canvas.save();
        canvas.translate(0, -offset);
        drawLrc(canvas);
        if (offset != targetOffset && !isTouch) {
            if (offset < targetOffset){
                offset+=(1+Math.abs(offset-targetOffset)/LineHeight);
            }else{
                offset-=(1+Math.abs(offset-targetOffset)/LineHeight);
            }
            invalidate();
        }
        canvas.restore();

        if (isTouch){
            int line = Math.abs(offset/LineHeight);
            if (line>=LyricList.size())return;
            String time = String.format("%02d:%02d",LyricList.get(line).getMinutes(),LyricList.get(line).getSeconds());
            pPaint.setColor(context.getResources().getColor(R.color.play_back_color));
            rectF.set(width-textSize*3, height/2-textSize*1.5f,
                    width-textSize*0.3f, height/2-textSize*0.5f);
            canvas.drawRoundRect(rectF,10,10,pPaint);

            pPaint.setColor(context.getResources().getColor(R.color.play_color));
            canvas.drawText(time,rectF.right-pPaint.getTextSize()*2.5f, rectF.bottom-(rectF.height())*0.3f,pPaint);

            path.moveTo(rectF.left+pPaint.getTextSize()/2,rectF.bottom-(rectF.height())*0.3f-pPaint.getTextSize()*0.8f);
            path.lineTo(rectF.left+pPaint.getTextSize()/2*2,rectF.bottom-(rectF.height())*0.3f-pPaint.getTextSize()*0.4f);
            path.lineTo(rectF.left+pPaint.getTextSize()/2,rectF.bottom-(rectF.height())*0.3f);
            canvas.drawPath(path,pPaint);
        }
        playing();
        invalidate();
    }

    private void playing(){

        if (mediaPlayer != null){
            if (mediaPlayer.isPlaying()&&currentLine < LyricList.size()) {
                if (mediaPlayer.getCurrentPosition() >= LyricList.get(currentLine).getTime()) {
                    if (currentLine<LyricList.size())
                        if (lyricChangeListener!=null)lyricChangeListener.onChange(LyricList.get(currentLine).getLyric());
                    currentLine++;
                }
            }
        }
    }

    private void drawLrc(Canvas canvas){
        if (LyricList==null)return;
        int len = LyricList.size();

        for (int i = 0; i < len; i++) {
            String lrc = LyricList.get(i).getLyric();
            if (i == currentLine-1||(Math.abs(offset/LineHeight)==i&&isTouch)) {
                canvas.drawText(lrc, width / 2 - measureLineSpace(lrc,hPaint.getTextSize()) / 2,
                        height / 2 + i * LineHeight, hPaint);
            }else{
                canvas.drawText(lrc, width / 2 - measureLineSpace(lrc,dPaint.getTextSize()) / 2,
                        height / 2 + i * LineHeight,dPaint);
            }
        }
    }

    private int measureLineSpace(String lrc,float textSize){
        int lineSpace = 0;
        for (char c : lrc.toCharArray()) {
            if (c >= ' ' && c <= '~'){
                lineSpace += textSize/2;
            }else lineSpace += textSize;
        }
        return lineSpace;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();
        height = getHeight();
        textSize = width/20;
        setTextSize(textSize);
    }

    public void setOnChangeProgress(OnChangeProgress onChangeProgress) {
        this.onChangeProgress = onChangeProgress;
    }

    public abstract interface OnChangeProgress{
        public abstract void onChanged(int line);
    }

    public interface onLyricChangeListener{
        void onChange(String lyric);
    }

}

一些歌词类Lyric查看《Android下载并解析Lrc歌词文件》

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android自定义View是指基于Android原生控件的一种扩展,可以根据自己的需求和设计规范来创建更加个性化和独特的控件。而歌词控件是一种针对音乐播放器或者视频播放器等应用场景中的需求,用于显示音乐或者视频的歌词的控件。 Android自定义View歌词控件的实现思路如下: 1. 首先需要自定义一个View,并继承自View或者其子类,如TextView。 2. 在自定义的View中重写onDraw方法,在其中实现绘制歌词的逻辑。 3. 在onDraw方法中,使用Canvas对象进行绘制,可以使用drawText方法绘制歌词文本,也可以使用drawBitmap方法绘制图片背景等。 4. 可以通过自定义属性,如字体大小、字体颜色、歌词滚动速度等,来对歌词控件进行配置。 5. 如果需要实现歌词的滚动效果,可以使用ValueAnimator或者Scroller来实现歌词的平滑滚动。 6. 如果需要实现点击歌词跳转播放进度的功能,可以通过添加点击事件监听器,在触摸事件中判断点击位置对应的歌词行,并根据歌词的时间戳跳转到指定的播放进度。 总结来说,Android自定义View歌词控件的实现需要重写onDraw方法进行绘制,可以通过Canvas对象进行绘制文本或者图像,通过自定义属性进行配置,使用动画或者滚动实现歌词的平滑滚动,通过监听触摸事件实现点击歌词跳转播放进度的功能。通过以上步骤,我们可以创建一个个性化的歌词控件,满足不同应用场景的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dear Mr. Ma

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值