• Android简易音乐重构MVVM Java版-新增歌曲播放界面+状态栏黑科技(十七)


    关于

    本篇主要实现播放界面包括,歌曲暂停、播放下一首、上一首、播放模式、歌词展示,实现根据歌曲背景深浅实现状态栏文字、标题文字黑白切换适配。
    音乐播放封装使用《开源库MusicPlayManager - 封装StarrySky音乐库》
    简易音乐app仅作为学习用,禁止用于商业及非法用途,如产生法律纠纷与本人无关

    效果

    在这里插入图片描述

    新增歌曲播放界面

      新增activity_current_song_play.xml

    
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
        <data>
            <variable
                name="vm"
                type="com.tobery.personalmusic.ui.song.SongPlayViewModel" />
        data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.song.CurrentSongPlayActivity">
    
        <ImageView
            android:id="@+id/img_bc"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/bg_default_song"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="52dp" />
    
        <View
            android:id="@+id/view_title_bg"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_45"
            app:layout_constraintTop_toTopOf="parent"
            />
    
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="@id/view_title_bg"
            app:layout_constraintBottom_toBottomOf="@id/view_title_bg"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:textColor="@color/white"
            />
    
        <View
            android:id="@+id/view_body"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_0"
            app:layout_constraintTop_toBottomOf="@id/view_title_bg"
            app:layout_constraintBottom_toTopOf="@id/view_bottom"
            />
    
        <ImageView
            android:id="@+id/iv_music_cover"
            android:layout_width="@dimen/dp_200"
            android:layout_height="@dimen/dp_200"
            imSrc="@{vm.currentSongUrl}"
            error="@{@drawable/shape_music_record}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@id/view_title_bg"
            app:layout_constraintBottom_toBottomOf="@id/view_bottom"
            />
    
        <com.tobery.personalmusic.widget.LyricView
            android:id="@+id/lrc"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_0"
            app:current_color="#ffffff"
            app:default_label="暂无歌词,我也不知道怎么上传OvO"
            app:lrc_padding="@dimen/dp_10"
            app:normal_color="#808080"
            app:text_divider="@dimen/dp_15"
            app:text_gravity="center"
            app:text_size="@dimen/sp_18"
            app:time_color="#c5c3c2"
            app:time_text_size="@dimen/sp_13"
            app:timeline_color="#4d4948"
            android:visibility="gone"
            app:layout_constraintTop_toBottomOf="@id/view_title_bg"
            app:layout_constraintBottom_toTopOf="@id/view_bottom"
            />
    
    
        <View
            android:id="@+id/view_bottom"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_90"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    
        <TextView
            android:id="@+id/tv_past_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/white"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@id/view_bottom"
            android:layout_marginStart="@dimen/dp_8"
            android:textSize="@dimen/sp_12" />
    
        <SeekBar
            android:id="@+id/seek_bar"
            android:layout_width="@dimen/dp_0"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:max="100"
            android:maxHeight="@dimen/dp_3"
            android:minHeight="@dimen/dp_3"
            android:progressDrawable="@drawable/seekbar_bg"
            android:thumb="@drawable/seekbar_thumb"
            app:layout_constraintTop_toTopOf="@id/view_bottom"
            app:layout_constraintStart_toEndOf="@id/tv_past_time"
            app:layout_constraintEnd_toStartOf="@id/tv_total_time"
            android:layout_marginStart="@dimen/dp_5"
            />
    
        <TextView
            android:id="@+id/tv_total_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/white"
            app:layout_constraintTop_toTopOf="@id/view_bottom"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="@dimen/dp_8"
            android:textSize="@dimen/sp_12" />
    
        <ImageView
            android:id="@+id/iv_play_mode"
            android:layout_width="@dimen/dp_45"
            android:layout_height="@dimen/dp_45"
            android:padding="@dimen/dp_15"
            android:layout_marginEnd="@dimen/dp_10"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_play_list_loop_white"
            app:layout_constraintTop_toTopOf="@id/seek_bar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/iv_pre"
            />
        <ImageView
            android:id="@+id/iv_pre"
            android:layout_width="@dimen/dp_45"
            android:layout_height="@dimen/dp_45"
            android:padding="@dimen/dp_15"
            android:scaleType="centerCrop"
            android:src="@drawable/shape_pre"
            android:onClick="@{() -> vm.previous()}"
            app:layout_constraintTop_toTopOf="@id/seek_bar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/iv_play"
            />
    
        <ImageView
            android:id="@+id/iv_play"
            android:layout_width="@dimen/dp_50"
            android:layout_height="@dimen/dp_50"
            android:padding="@dimen/dp_5"
            android:src="@drawable/shape_play_white"
            app:layout_constraintTop_toTopOf="@id/seek_bar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:onClick="@{() -> vm.musicPlayOrPause()}"
            />
    
        <ImageView
            android:id="@+id/iv_next"
            android:layout_width="@dimen/dp_45"
            android:layout_height="@dimen/dp_45"
            android:padding="@dimen/dp_15"
            android:scaleType="centerCrop"
            android:src="@drawable/shape_next"
            android:onClick="@{() -> vm.next()}"
            app:layout_constraintStart_toEndOf="@id/iv_play"
            app:layout_constraintTop_toTopOf="@id/seek_bar"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    
        <ImageView
            android:id="@+id/iv_list"
            android:layout_width="@dimen/dp_45"
            android:layout_height="@dimen/dp_45"
            android:layout_marginStart="@dimen/dp_10"
            android:padding="@dimen/dp_10"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_song_play_list"
            app:layout_constraintStart_toEndOf="@id/iv_next"
            app:layout_constraintTop_toTopOf="@id/seek_bar"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    
    androidx.constraintlayout.widget.ConstraintLayout>
    layout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190

      新增bg_default_song.xml:

    
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle" >
    
        <gradient
            android:endColor="#221B1A"
            android:gradientRadius="120%p"
            android:startColor="#4b3832"
            android:type="radial" />
    
    shape>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    增加歌词view LyricView

    public class LyricView extends View {
        private static final String TAG = "LyricView";
        //播放图片的宽高
        private static final float PLAY_DRAWABLE_WIDTH = ContextProvider.get().getContext().getResources().getDimension(R.dimen.dp_30);
        //时间 也就是 [XX:XX] 的宽度
        private static final float TIME_TEXT_WIDTH = ContextProvider.get().getContext().getResources().getDimension(R.dimen.dp_40);
        //时间线从一行 滑到另一行的时间
        private static final int ANIMATION_DURATION = 1000;
        //时间线从当前行 的某处 移到中间的时间
        private static final int ADJUST_DURATION = 100;
    
        private List<LrcEntry> mLrcEntryList = new ArrayList<>();
        //歌词画笔
        private TextPaint mLrcPaint = new TextPaint();
        //时间线 画笔
        private TextPaint mTimePaint = new TextPaint();
        //时间线 画笔属性
        private Paint.FontMetrics mTimeFontMetrics;
        //播放按钮
        private Drawable mPlayDrawable;
        //普通字体的颜色
        private int mNormalTextColor;
        //字体的大小(普通和选中的都一样大
        private float mTextSize;
        //选中行 字体颜色
        private int mCurrentTextColor;
        //时间线 所在的一行歌词 已经时间的颜色
        private int mTimelineTextColor;
        //时间线的颜色
        private int mTimelineColor;
        //padding值
        private float mPadding;
        //没有加载完歌词的 默认显示text
        private String mDefaultLabel;
        //滑动的动画
        private ValueAnimator mAnimator;
        //手势监听器
        private GestureDetector mGestureDetector;
        //播放监听器
        private OnPlayClickListener mListener;
        //滑动类
        private Scroller mScroller;
        //是否正在滑动/正在点击/显示时间线
        private boolean isFling, isTouching, isShowTimeline;
        //歌词位置
        private int mTextGravity;
        //手指在 屏幕上滑动的 偏移量
        private float mOffset;
        //每句歌词之间的间隔
        private float mDividerHeight = 0;
        //选中的歌词,即时间线所在的歌词
        private int mCurrentLine;
        //单击该view,如果不是点击播放按钮,则歌词界面消失
        private OnCoverChangeListener coverChangeListener;
    
        /**
         * 播放按钮的监听器,如果成功消费该事件,则更新Ui
         */
        public interface OnPlayClickListener {
            boolean onPlayClick(long time);
        }
    
        public LyricView(Context context) {
            this(context, null);
        }
    
        public LyricView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public LyricView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context, attrs);
    
            mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener);
            mGestureDetector.setIsLongpressEnabled(false);
            mScroller = new Scroller(context);
        }
    
        private void initView(Context context, AttributeSet attrs) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.LyricView);
            mTextSize = ta.getDimension(R.styleable.LyricView_text_size, getResources().getDimension(R.dimen.sp_16));
    
            mDividerHeight = ta.getDimension(R.styleable.LyricView_text_divider, getResources().getDimension(R.dimen.dp_10));
    
            mNormalTextColor = ta.getColor(R.styleable.LyricView_normal_color, Color.parseColor("#ccffffff"));
            mCurrentTextColor = ta.getColor(R.styleable.LyricView_current_color, Color.parseColor("#ffffff"));
            mTimelineTextColor = ta.getColor(R.styleable.LyricView_time_color, Color.parseColor("#ccffffff"));
            mDefaultLabel = ta.getString(R.styleable.LyricView_default_label);
            mPadding = ta.getDimension(R.styleable.LyricView_lrc_padding, 0);
            mTimelineColor = ta.getColor(R.styleable.LyricView_timeline_color, Color.parseColor("#f0f0f0"));
            mPlayDrawable = ResourcesCompat.getDrawable(getResources(),R.drawable.ic_lrc_play,null);
    
            float timeTextSize = ta.getDimension(R.styleable.LyricView_time_text_size, getResources().getDimension(R.dimen.sp_10));
            mTextGravity = ta.getInteger(R.styleable.LyricView_text_gravity, LrcEntry.GRAVITY_CENTER);
    
            ta.recycle();
    
            mLrcPaint.setAntiAlias(true);
            mLrcPaint.setTextSize(mTextSize);
            mLrcPaint.setTextAlign(Paint.Align.LEFT);
            mTimePaint.setAntiAlias(true);
            mTimePaint.setTextSize(timeTextSize);
            mTimePaint.setTextAlign(Paint.Align.CENTER);
            mTimePaint.setStrokeWidth(getResources().getDimension(R.dimen.dp_2));
            mTimePaint.setStrokeCap(Paint.Cap.ROUND);
            mTimeFontMetrics = mTimePaint.getFontMetrics();
        }
    
        /**
         * 加载双语歌词
         */
        public void loadLrc(String mainLrcText, String secondLrcText) {
            Log.d(TAG,"mainLrcText : "+mainLrcText+" ");
            reset();
    
            String[] lrc = new String[2];
            lrc[0] = mainLrcText;
            lrc[1] = secondLrcText;
    
            List<LrcEntry> parseList = LrcUtils.parseLrc(lrc);
            if (parseList != null && !parseList.isEmpty()) {
                mLrcEntryList.addAll(parseList);
            }
    
            Collections.sort(mLrcEntryList);
            initEntryList();
            invalidate();
        }
    
        private void reset() {
            endAnimation();
            mScroller.forceFinished(true);
            isShowTimeline = false;
            isTouching = false;
            isFling = false;
            removeCallbacks(hideTimelineRunnable);
            mLrcEntryList.clear();
            mOffset = 0;
            mCurrentLine = 0;
            invalidate();
        }
    
        private Runnable hideTimelineRunnable = () -> {
            if (lrcNotEmpty() && isShowTimeline) {
                isShowTimeline = false;
                smoothScrollTo(mCurrentLine, ANIMATION_DURATION);
            }
        };
    
        private void endAnimation() {
            if (mAnimator != null && mAnimator.isRunning()) {
                mAnimator.end();
            }
        }
    
        /**
         * 传入时间刷新歌词
         */
        public void updateTime(long time) {
            if (!lrcNotEmpty()) {
                return;
            }
    
            runOnUi(() -> {
                int line = findShowLine(time);
                if (line != mCurrentLine) {
                    mCurrentLine = line;
                    if (!isShowTimeline) {
                        smoothScrollTo(line, ANIMATION_DURATION);
                    } else {
                        invalidate();
                    }
                }
            });
        }
    
        /**
         * 用二分查找 对应的时间应该显示第几行歌词
         */
        private int findShowLine(long time) {
            int left = 0;
            int right = mLrcEntryList.size();
            while (left <= right) {
                int middle = (left + right) / 2;
                long middleTime = mLrcEntryList.get(middle).getTime();
    
                if (time < middleTime) {
                    right = middle - 1;
                } else {
                    if (middle + 1 >= mLrcEntryList.size() || time < mLrcEntryList.get(middle + 1).getTime()) {
                        return middle;
                    }
    
                    left = middle + 1;
                }
            }
    
            return 0;
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            //一有变化就重新布局子View
            if (changed) {
                initPlayDrawable();
                initEntryList();
                if (lrcNotEmpty()) {
                    smoothScrollTo(mCurrentLine, 0);
                }
            }
        }
    
        /**
         * 滑动到指定的行
         */
        private void smoothScrollTo(int line, int duration) {
    //        if (!isShowTimeline) {
    //        mCurrentLine = line;
    //        }
            float offset = getOffset(line);
            endAnimation();
    
            mAnimator = ValueAnimator.ofFloat(mOffset, offset);
            mAnimator.setDuration(duration);
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.addUpdateListener(animation -> {
                mOffset = (float) animation.getAnimatedValue();
                invalidate();
            });
            LrcUtils.resetDurationScale();
            mAnimator.start();
        }
    
        /**
         * 获取 改行歌词到顶部的 offset
         */
        private float getOffset(int line) {
            if (mLrcEntryList.get(line).getOffset() == Float.MIN_VALUE) {
                float offset = getHeight() / 2;
                for (int i = 1; i <= line; i++) {
                    offset -= (mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) / 2 + mDividerHeight;
                }
                mLrcEntryList.get(line).setOffset(offset);
            }
            return mLrcEntryList.get(line).getOffset();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int centerY = getHeight() / 2;
    
            //没有歌词 则显示默认
            if (!lrcNotEmpty()) {
                mLrcPaint.setColor(mCurrentTextColor);
                @SuppressLint("DrawAllocation")
                StaticLayout staticLayout = new StaticLayout(mDefaultLabel, mLrcPaint, (int) getLrcWidth(), Layout.Alignment.ALIGN_CENTER, 1f, 0f, false);
                drawText(canvas, staticLayout, centerY);
            }
    
            int centerLine = getCenterLine();
    
            //画时间线、时间和播放图标
            if (isShowTimeline) {
                mPlayDrawable.draw(canvas);
    
                mTimePaint.setColor(mTimelineColor);
                canvas.drawLine(TIME_TEXT_WIDTH, centerY, getWidth() - TIME_TEXT_WIDTH, centerY, mTimePaint);
    
                mTimePaint.setColor(mTimelineTextColor);
                String timeText = LrcUtils.formatTime(mLrcEntryList.get(centerLine).getTime());
                //高度居中显示
                float timeX = getWidth() - TIME_TEXT_WIDTH / 2;
                float timeY = centerY - (mTimeFontMetrics.descent + mTimeFontMetrics.ascent) / 2;
                canvas.drawText(timeText, timeX, timeY, mTimePaint);
            }
    
            //画布偏移offset 位置
            canvas.translate(0, mOffset);
    
            float y = 0;
            mLrcPaint.setTextSize(mTextSize);
            for (int i = 0; i < mLrcEntryList.size(); i++) {
                if (i > 0) {
                    y += (mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) / 2 + mDividerHeight;
                }
                if (i == mCurrentLine) {
                    mLrcPaint.setColor(mCurrentTextColor);
                } else if (isShowTimeline && i == centerLine) {
                    mLrcPaint.setColor(mTimelineTextColor);
                } else {
                    mLrcPaint.setColor(mNormalTextColor);
                }
                drawText(canvas, mLrcEntryList.get(i).getStaticLayout(), y);
            }
        }
    
        /**
         * 获取当前在视图中央的行数
         */
        private int getCenterLine() {
            int centerLine = 0;
            float minDistance = Float.MAX_VALUE;
            for (int i = 0; i < mLrcEntryList.size(); i++) {
                if (Math.abs(mOffset - getOffset(i)) <= minDistance) {
                    minDistance = Math.abs(mOffset - getOffset(i));
                    centerLine = i;
                }
            }
            return centerLine;
        }
    
        private void drawText(Canvas canvas, StaticLayout staticLayout, float y) {
            canvas.save();
            canvas.translate(mPadding, y - staticLayout.getHeight() / 2);
            staticLayout.draw(canvas);
            canvas.restore();
        }
    
        private void initPlayDrawable() {
            int l = (int) ((TIME_TEXT_WIDTH - PLAY_DRAWABLE_WIDTH) / 2);
            int t = (int) (getHeight() / 2 - PLAY_DRAWABLE_WIDTH / 2);
            int r = (int) (l + PLAY_DRAWABLE_WIDTH);
            int b = (int) (t + PLAY_DRAWABLE_WIDTH);
            mPlayDrawable.setBounds(l, t, r, b);
        }
    
        /**
         * 在布局的时候或者 解析完歌词的时候需要初始化list里面每一句歌词
         */
        private void initEntryList() {
            if (getWidth() == 0 || !lrcNotEmpty()) {
                return;
            }
            for (LrcEntry lrcEntry : mLrcEntryList) {
                lrcEntry.init(mLrcPaint, (int) getLrcWidth(), mTextGravity);
            }
            //以view高度 1/2 为中轴线
            mOffset = getHeight() / 2f;
        }
    
        /**
         * 获取歌词宽度
         */
        private float getLrcWidth() {
            return getWidth() - 2 * mPadding;
        }
    
        /**
         * 歌词 是否 不为空
         */
        private boolean lrcNotEmpty() {
            return !mLrcEntryList.isEmpty();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                isTouching = false;
                if (lrcNotEmpty() && !isFling) {
                    smoothScrollTo(getCenterLine(), ADJUST_DURATION);
                }
            }
            return mGestureDetector.onTouchEvent(event);
        }
    
        /**
         * 手势监听器
         */
        private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                if (lrcNotEmpty() && mListener != null) {
                    mScroller.forceFinished(true);
                    removeCallbacks(hideTimelineRunnable);
                    isTouching = true;
                    invalidate();
                }
                return true;
            }
    
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                if (lrcNotEmpty()) {
                    isShowTimeline = true;
                    mOffset += -distanceY;
                    //不能超出边界
                    mOffset = Math.min(mOffset, getOffset(0));
                    mOffset = Math.max(mOffset, getOffset(mLrcEntryList.size() - 1));
                    invalidate();
                }
                return super.onScroll(e1, e2, distanceX, distanceY);
            }
    
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (lrcNotEmpty()) {
                    //在 fling开始滑动
                    mScroller.fling(0, (int) mOffset, 0, (int) velocityY, 0, 0,
                            (int) getOffset(mLrcEntryList.size() - 1), (int) getOffset(0));
                    isFling = true;
                }
                return super.onFling(e1, e2, velocityX, velocityY);
            }
    
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                Log.d(TAG, "onSingleTapConfirmed");
                if (lrcNotEmpty() && isShowTimeline && mPlayDrawable.getBounds().contains((int) e.getX(), (int) e.getY())) {
                    int centerLine = getCenterLine();
                    long centerLineTime = mLrcEntryList.get(centerLine).getTime();
                    //按了播放才回去更新UI
                    if (mListener != null && mListener.onPlayClick(centerLineTime)) {
                        postDelayed(hideTimelineRunnable, 400);
                        mCurrentLine = centerLine;
                        invalidate();
                    }
                } else {
                    if (coverChangeListener != null) {
                        coverChangeListener.onCoverChange();
                        postDelayed(hideTimelineRunnable, 400);
                    }
                }
                return super.onSingleTapConfirmed(e);
            }
        };
    
        public interface OnCoverChangeListener {
            void onCoverChange();
        }
    
        public void setCoverChangeListener(OnCoverChangeListener coverChangeListener) {
            this.coverChangeListener = coverChangeListener;
        }
    
        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                mOffset = mScroller.getCurrY();
                invalidate();
            }
    
            if (isFling && mScroller.isFinished()) {
                isFling = false;
                if (lrcNotEmpty() && !isTouching) {
                    smoothScrollTo(getCenterLine(), ADJUST_DURATION);
                }
            }
        }
    
        @Override
        protected void onDetachedFromWindow() {
            removeCallbacks(hideTimelineRunnable);
            super.onDetachedFromWindow();
        }
    
        /**
         * 主线程中运行
         */
        private void runOnUi(Runnable r) {
            if (Looper.getMainLooper() == Looper.myLooper()) {
                r.run();
            } else {
                post(r);
            }
        }
    
        public void setListener(OnPlayClickListener mListener) {
            this.mListener = mListener;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473

      修改attrs.xml

    
    <resources>
        <declare-styleable name="LyricView">
            <attr name="text_size" format="dimension"/>
            <attr name="text_divider" format="dimension"/>
            <attr name="normal_color" format="reference|color"/>
            <attr name="current_color" format="reference|color"/>
            <attr name="default_label" format="string"/>
            <attr name="lrc_padding" format="dimension"/>
            <attr name="timeline_color" format="reference|color"/>
            <attr name="time_color" format="reference|color"/>
            <attr name="time_text_size" format="dimension"/>
            <attr name="text_gravity">
                <enum name="center" value="0" />
                <enum name="left" value="1" />
                <enum name="right" value="2" />
            attr>
        declare-styleable>
    resources>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

      添加行歌词实体类LrcEntry

    /**
     * 一行歌词的实体类,因为要按照时间排序,所以需要实现Comparable
     * 用它来画到View上
     */
    public class LrcEntry implements Comparable<LrcEntry> {
        //歌词所对应的时间
        private long time;
        //第一种语言的歌词的内容
        private String text;
        //第二种语言的歌词内容,一开始是空的,需要设置
        private String secondText;
        //StaticLayout,可以自动换行
        private StaticLayout staticLayout;
        //这一行歌词距离视图顶部的距离
        private float offset = Float.MIN_VALUE;
        //歌词的居中/左/右显示
        public static final int GRAVITY_CENTER = 0;
        public static final int GRAVITY_LEFT = 1;
        public static final int GRAVITY_RIGHT = 2;
    
        public LrcEntry(long time, String text) {
            this.time = time;
            this.text = text;
        }
    
        /**
         * 绘制时每一行都要初始化其内容、宽度、绘制格式
         */
        public void init(TextPaint paint, int width, int gravity) {
            Layout.Alignment align;
            switch (gravity) {
                case GRAVITY_LEFT:
                    align = Layout.Alignment.ALIGN_NORMAL;
                    break;
                default:
                case GRAVITY_CENTER:
                    align = Layout.Alignment.ALIGN_CENTER;
                    break;
                case GRAVITY_RIGHT:
                    align = Layout.Alignment.ALIGN_OPPOSITE;
                    break;
            }
            staticLayout = new StaticLayout(getShowText(), paint, width, align, 1f, 0f, false);
            offset = Float.MIN_VALUE;
        }
    
        public StaticLayout getStaticLayout() {
            return staticLayout;
        }
    
        public void setOffset(float offset) {
            this.offset = offset;
        }
    
        /**
         * 获取歌词高度
         */
        public int getHeight() {
            if (staticLayout == null) {
                return 0;
            }
            return staticLayout.getHeight();
        }
    
        private String getShowText() {
            if (!TextUtils.isEmpty(secondText)) {
                return text + "\n" + secondText;
            }
            return text;
        }
    
        public void setSecondText(String secondText) {
            this.secondText = secondText;
        }
    
        public String getText() {
            return text;
        }
    
        public String getSecondText() {
            return secondText;
        }
    
        public float getOffset() {
            return offset;
        }
    
        public long getTime() {
            return time;
        }
    
        /**
         * 根据歌词时间来比较大小
         */
        @Override
        public int compareTo(LrcEntry entry) {
            if (entry == null) {
                return -1;
            }
            return (int) (time - entry.getTime());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    修改ApiService

    @GET("lyric") //获取歌词
        LiveData<ApiResponse<LyricEntity>> getLyric(@Query("id") long songId);
    
    • 1
    • 2

      新增歌词实体类LyricEntity

    @NoArgsConstructor
    @Data
    public class LyricEntity {
    
        private boolean sgc;
        private boolean sfy;
        private boolean qfy;
        private LrcEntity lrc;
        private KlyricEntity klyric;
        private TlyricEntity tlyric;
        private RomalrcEntity romalrc;
        private int code;
    
        @NoArgsConstructor
        @Data
        public static class LrcEntity {
            private int version;
            private String lyric;
        }
    
        @NoArgsConstructor
        @Data
        public static class KlyricEntity {
            private int version;
            private String lyric;
        }
    
        @NoArgsConstructor
        @Data
        public static class TlyricEntity {
            private int version;
            private String lyric;
        }
    
        @NoArgsConstructor
        @Data
        public static class RomalrcEntity {
            private int version;
            private String lyric;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    添加引用

    //提取颜色
        implementation 'androidx.palette:palette:1.0.0'
    
    • 1
    • 2

    添加SongPlayViewModel

    public class SongPlayViewModel extends ViewModel {
    
        private SavedStateHandle state;
    
        public UserInfoUi ui;
    
        private String userInfo;
    
        public Boolean isShowLrc = false;
    
        public ObservableField<String> currentSongUrl = new ObservableField<>("");
    
        public ObservableLong currentSongId = new ObservableLong();
    
        public SongPlayViewModel(SavedStateHandle savedStateHandle) {
            this.state = savedStateHandle;
        }
    
    
        public LiveData<ApiResponse<LyricEntity>> getLyric() {
            return RetrofitUtils.getmApiUrl().getLyric(currentSongId.get());
        }
    
        public void musicPlayOrPause(){
            if (MusicPlay.isPlaying()){
                MusicPlay.pauseMusic();
            }else {
                MusicPlay.restoreMusic();
            }
        }
    
        public void next(){
            MusicPlay.skipToNext();
        }
    
        public void previous(){
            MusicPlay.skipToPrevious();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    修改CurrentSongPlayActivity.java

    public class CurrentSongPlayActivity extends BaseActivity {
    
        private ActivityCurrentSongPlayBinding binding;
        private ObjectAnimator rotationAnim;
        private MusicInfo musicInfo;
        private SongPlayViewModel viewModel;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = ActivityCurrentSongPlayBinding.inflate(getLayoutInflater());
            viewModel = new ViewModelProvider(this).get(SongPlayViewModel.class);
            binding.setVm(viewModel);
            binding.setLifecycleOwner(this);
            setContentView(binding.getRoot());
            initView();
            initAnim();
            initObserver();
        }
    
        private void initObserver() {
            //加载歌词
            viewModel.getLyric()
                    .observe(this, lyricEntityApiResponse -> {
                        ViewExtensionKt.printLog("当前"+lyricEntityApiResponse.getMessage());
                        if (lyricEntityApiResponse.getStatus() == Status.SUCCESS){
                            if (lyricEntityApiResponse.getData().getLrc() != null){
                                if (lyricEntityApiResponse.getData().getTlyric() != null){
                                    binding.lrc.loadLrc(lyricEntityApiResponse.getData().getLrc().getLyric(),lyricEntityApiResponse.getData().getTlyric().getLyric());
                                }else {
                                    binding.lrc.loadLrc(lyricEntityApiResponse.getData().getLrc().getLyric(),"");
                                }
                            }else {
                                binding.lrc.loadLrc("","");
                            }
                        }
                    });
        }
    
        private void initView() {
            musicInfo = getIntent().getParcelableExtra(MUSIC_INFO);
            initImageBg(musicInfo);
            initListener();
            binding.viewBody.setOnClickListener(view -> {
                if (ClickUtil.enableClick()){
                    viewModel.isShowLrc = !viewModel.isShowLrc;
                    showLyrics(viewModel.isShowLrc);
                }
            });
            //移动歌曲进度
            binding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {}
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    MusicPlay.seekTo(seekBar.getProgress(),true);
                    binding.lrc.updateTime(seekBar.getProgress());
                }
            });
            //修改模式
            binding.ivPlayMode.setOnClickListener(v -> {
                if (ClickUtil.enableClick()){
                    changeRepeatMode();
                }
            });
            binding.lrc.setListener(time -> {
                MusicPlay.seekTo(time,true);
                return true;
            });
            binding.lrc.setCoverChangeListener(()->{
                viewModel.isShowLrc = false;
                showLyrics(false);
            });
        }
    
        private void initImageBg(MusicInfo musicInfo) {
            viewModel.currentSongUrl.set(musicInfo.getSongCover());
            RequestOptions options = new RequestOptions()
                    .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
                    .bitmapTransform(new BlurTransformation(25, 30));
            viewModel.currentSongId.set(Long.parseLong(musicInfo.getSongId()));
            initObserver();
            Glide.with(this)
                    .asBitmap()
                    .load(musicInfo.getSongCover())
                    .transition(BitmapTransitionOptions.withCrossFade(1500))
                    .apply(options)
                    .into(new SimpleTarget<Bitmap>() {
                        @Override
                        public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                            binding.imgBc.setImageBitmap(resource);
                            StatusBarUtil.setTranslucentForImageView(CurrentSongPlayActivity.this,0,binding.viewTitleBg);
                            Palette.from(resource)
                                    .setRegion(0,0,getScreenWidth(),getStatusBarHeight())
                                    .maximumColorCount(6)
                                    .generate(palette -> {
                                        Palette.Swatch mostPopularSwatch = null;
                                        for (Palette.Swatch swatch: palette.getSwatches()){
                                            if (mostPopularSwatch == null
                                            || swatch.getPopulation() > mostPopularSwatch.getPopulation()){
                                                mostPopularSwatch = swatch;
                                            }
                                        }
                                        if (mostPopularSwatch!= null){
                                            double luminance =ColorUtils.calculateLuminance(mostPopularSwatch.getRgb());
                                            // 当luminance小于0.5时,我们认为这是一个深色值.
                                            if (luminance < 0.5){
                                                 setDarkStatusBar();
                                            }else {
                                                setLightStatusBar();
                                            }
                                        }
                                    });
                        }
                    });
            binding.tvTitle.setText(musicInfo.getSongName());
            binding.tvSinger.setText(musicInfo.getArtist());
        }
    
        private int getScreenWidth(){
            DisplayMetrics displayMetrics =new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            return displayMetrics.widthPixels;
        }
    
        private int getStatusBarHeight(){
            int result = 0;
            int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0){
                result = getResources().getDimensionPixelSize(resourceId);
            }
            return result;
        }
    
        private void initListener() {
            MusicPlay.onPlayStateListener(this, new OnMusicPlayStateListener() {
                @Override
                public void onPlayState(@NonNull PlayManger playManger) {
                    switch (playManger.getStage()){
                        case PlayManger.PAUSE:
                        case PlayManger.IDLE:
                            rotationAnim.pause();
                            binding.ivPlay.setImageResource(R.drawable.shape_play_white);
                            break;
                        case PlayManger.PLAYING:
                            rotationAnim.start();
                            binding.ivPlay.setImageResource(R.drawable.shape_pause_white);
                            break;
                        case PlayManger.BUFFERING:
                            ViewExtensionKt.printLog("缓冲");
                            break;
                        case PlayManger.SWITCH:
                            //切歌
                            if (playManger.getSongInfo() != null){
                                initImageBg(playManger.getSongInfo());
                            }
                            break;
                    }
                }
            });
            MusicPlay.onPlayProgressListener(new OnMusicPlayProgressListener() {
                @Override
                public void onPlayProgress(long curP, long duration) {
                    if (binding.seekBar.getMax() != duration){
                        binding.seekBar.setMax((int) duration);
                        binding.tvTotalTime.setText(TimeUtil.getTimeNoYMDH(duration));
                    }
                    binding.tvPastTime.setText(TimeUtil.getTimeNoYMDH(curP));
                    binding.lrc.updateTime(curP);
                    binding.seekBar.setProgress((int) curP);
                }
            });
        }
    
        //根据isShowLyrics来判断是否展示歌词
        private void showLyrics(boolean isShowLyrics) {
            binding.ivMusicCover.setVisibility(isShowLyrics ? View.GONE : View.VISIBLE);
            binding.lrc.setVisibility(isShowLyrics ? View.VISIBLE : View.GONE);
        }
    
        //使状态栏图标黑
        private void setLightStatusBar(){
            binding.tvTitle.setTextColor(getColor(R.color.black80));
            binding.tvSinger.setTextColor(getColor(R.color.black70));
            int flags = getWindow().getDecorView().getSystemUiVisibility();
            getWindow().getDecorView().setSystemUiVisibility(flags | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        //使状态栏图标白
        private void setDarkStatusBar(){
            binding.tvTitle.setTextColor(getColor(R.color.white));
            binding.tvSinger.setTextColor(getColor(R.color.white80));
            int flags = getWindow().getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            getWindow().getDecorView().setSystemUiVisibility(flags^View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
    
        private void changeRepeatMode(){
            int currentModel = MusicPlay.getRepeatMode();
            switch (currentModel){
                case SpConstant.REPEAT_MODE_NONE:
                    MusicPlay.setRepeatMode(SpConstant.REPEAT_MODE_ONE,true);
                    ToastUtils.show(getString(R.string.repeat_one));
                    break;
                case SpConstant.REPEAT_MODE_ONE:
                    MusicPlay.setRepeatMode(SpConstant.REPEAT_MODE_SHUFFLE,false);
                    ToastUtils.show(getString(R.string.repeat_random));
                    break;
                case SpConstant.REPEAT_MODE_SHUFFLE:
                    MusicPlay.setRepeatMode(SpConstant.REPEAT_MODE_NONE,true);
                    ToastUtils.show(getString(R.string.repeat_none));
                    break;
            }
        }
    
        private void initAnim() {
            rotationAnim = ObjectAnimator.ofFloat(binding.ivMusicCover, "rotation", 360f);
            rotationAnim.setDuration(25 * 1000);
            rotationAnim.setInterpolator(new LinearInterpolator());
            rotationAnim.setRepeatCount(100000);
            rotationAnim.setRepeatMode(ValueAnimator.RESTART);
            rotationAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation, boolean isReverse) {
                    super.onAnimationEnd(animation, isReverse);
                    rotationAnim.start();
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            rotationAnim.cancel();
            rotationAnim.removeAllListeners();
            rotationAnim = null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240

    修改Constant.java

    public static final String MUSIC_INFO = "MUSIC_INFO";
    
    • 1

    修改日推界面 DailySongsActivity

      实现日推界面播放歌曲,传歌曲信息给播放界面:

     public class DailySongsActivity extends BaseActivity {
    
        private ActivityDailySongsBinding binding;
        private DailySongsAdapter adapter;
        private DailySongsViewModel viewModel;
        private ArrayList<MusicInfo> songList = new ArrayList<>(); //增加歌曲信息列表
        public String date = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = ActivityDailySongsBinding.inflate(getLayoutInflater());
            viewModel = new ViewModelProvider(this).get(DailySongsViewModel.class);
            setContentView(binding.getRoot());
            initRecycle();
            initView();
            initObserver();
        }
    
        private void initView() {
            StatusBarUtil.setColor(this,getResources().getColor(R.color.colorPrimary,null),0);
            binding.tvDay.setText(Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "");
            binding.tvMonth.setText("/"+(Calendar.getInstance().get(Calendar.MONTH)+1)+"");
            binding.title.ivBack.setOnClickListener(view -> {
                if (ClickUtil.enableClick()){
                    finish();
                }
            });
            binding.rvPlayTop.setOnClickListener(v -> {
                if (ClickUtil.enableClick()){
                    //播放歌曲列表,从第一个开始
                    MusicPlay.playMusicByList(songList,0);
                    startActivity(new Intent(this, CurrentSongPlayActivity.class)
                            .putExtra(MUSIC_INFO,songList.get(0)));
                }
            });
        }
    
        private void initObserver() {
            viewModel.getDailySongs().observe(this, dailySongsEntityApiResponse -> {
                if (dailySongsEntityApiResponse.getStatus() == Status.SUCCESS){
                    adapter.setDataList(dailySongsEntityApiResponse.getData().getData().getDailySongs(),dailySongsEntityApiResponse.getData().getData().getRecommendReasons());
                    for (DailySongsEntity.DataEntity.SongsEntity data: dailySongsEntityApiResponse.getData().getData().getDailySongs()){
                        MusicInfo musicInfo = new MusicInfo();
                        musicInfo.setSongUrl(Constant.SONG_URL+data.getId());
                        musicInfo.setSongId(String.valueOf(data.getId()));
                        String songName = data.getName();
                        if (data.getTns() != null){ //外语翻译歌名
                            songName += "("+data.getTns().get(0)+")";
                        }
                        musicInfo.setSongName(songName);
                        musicInfo.setArtist(data.getAr().get(0).getName());
                        musicInfo.setSongCover(data.getAl().getPicUrl());
                        songList.add(musicInfo);
                    }
                }
            });
        }
    
        private void initRecycle() {
            adapter = new DailySongsAdapter(this);
            LinearLayoutManager manager = new LinearLayoutManager(this);
            manager.setOrientation(LinearLayoutManager.VERTICAL);
            binding.rvDaily.setLayoutManager(manager);
            binding.rvDaily.setAdapter(adapter);
            binding.rvDaily.setHasFixedSize(true);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    修改日推适配器DailySongsAdapter

      新增单个item点击播放功能

    @Override
        public void onBindViewHolder(@NonNull DailySongsViewHolder holder, int position) {
            DailySongsEntity.DataEntity.SongsEntity bean = dataList.get(position);
            holder.tvSongName.setText(bean.getName());
            holder.tvSinger.setText(bean.getAr().get(0).getName()+"-"+bean.getAl().getName());
            BindingAdapter.loadRadiusImage(holder.imgSong,bean.getAl().getPicUrl());
            MusicInfo musicInfo = new MusicInfo();
            musicInfo.setSongUrl(Constant.SONG_URL+bean.getId());
            musicInfo.setSongId(String.valueOf(bean.getId()));
            String songName = bean.getName();
            if (bean.getTns() != null){ //外语翻译歌名
                songName += "("+bean.getTns().get(0)+")";
            }
            musicInfo.setSongName(songName);
            musicInfo.setArtist(bean.getAr().get(0).getName());
            musicInfo.setSongCover(bean.getAl().getPicUrl());
            for (DailySongsEntity.DataEntity.RecommendReasonsEntity data: reasonList){
                if (data.getSongId() == bean.getId()){
                    holder.tvRecommend.setText(data.getReason());
                }
            }
            if (bean.getFee() == 1){//1表示vip歌曲
                holder.tvVip.setVisibility(View.VISIBLE);
            }
            if (bean.getSq() != null){//表示该歌曲有SQ版本
                holder.tvSq.setVisibility(View.VISIBLE);
            }
            if (bean.getHr() != null){//表示有hi版
                holder.tvSq.setText(mContext.getString(R.string.music_type_hi));
                holder.tvSq.setVisibility(View.VISIBLE);
            }
            holder.itemView.setOnClickListener(view -> {
                if (ClickUtil.enableClick()){
                    MusicPlay.playMusicByInfo(musicInfo);
                    mContext.startActivity(new Intent(
                            mContext, CurrentSongPlayActivity.class
                    ).putExtra(MUSIC_INFO,musicInfo));
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    Vue 全套教程(二),入门 Vue 必知必会
    如何从任何苹果、Windows或安卓设备访问iCloud照片
    分享PPT设计思路,让你的PPT更吸睛
    Servlet
    抖音seo账号矩阵源码系统
    【LeetCode每日一题:1774. 最接近目标价格的甜点成本~~~递归+深度优先遍历】
    vi配置文件.vimrc内容示例
    EN 671-1固定式消防系统软管—CE认证
    30天消化MyBatis源码解析笔记,吊打面试官,offer接到手软
    shell基础(4):编程基础之测试和判断
  • 原文地址:https://blog.csdn.net/Tobey_r1/article/details/125792553