Android 音乐APP(三)播放音乐、自定义进度条、自动下一曲

简介: Android 音乐APP(三)播放音乐、自定义进度条、自动下一曲

Android 音乐APP 播放音乐


前言


作为音乐APP的主要功能,我放到了第三篇文章,因为播放音乐的功能并没有看上去那么简单,里面有很多细节是在写代码的时候就要考虑,并且加入到逻辑里面的,这可不是危言耸听,下面来看是怎样一个不简单吧。


正文


既然要做播放音乐的功能自然要好好的设计一下UI了,不然太难看我可拿不出手,于是我参考了QQ和网易的列表播放页面,合二为一就产生了下面这个页面


20201021175108116.png


从这张图可以看出什么呢?首先播放布局不随页面滚动,一直固定在屏幕的底部,其次是播放的进度是左边的这个logo中,而这个logo在播放的时候自动旋转,logo右边的是歌曲信息,当内容超过控件时会有跑马灯效果,最右边自然就是控制歌曲的播放和暂停了。你可能会问上一曲、下一曲呢?这个嘛,我打算放在下一篇文章再来说明,步步为营,循序渐进。


① 修改布局


首先要修改这个布局先达到图中的效果。


20201021180915623.png


下面我附上现在的布局代码。如果你之前是跟着来写的话,那么这里你可以对照着布局,再增加上去,不过你可能会漏掉一些细节,所以建议你先看懂布局,然后复制粘贴,万无一失。activity_local_music.xml如下:


<?xml version="1.0" encoding="utf-8"?>
<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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/app_bg"
        android:orientation="vertical"
        tools:context=".ui.MainActivity">
        <!--Toolbar-->
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/app_color"
            app:navigationIcon="@mipmap/icon_return_white">
            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="本地音乐"
                android:textColor="@color/white"
                android:textSize="@dimen/sp_18" />
        </androidx.appcompat.widget.Toolbar>
        <!--主体页面-->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--扫描布局和歌曲列表-->
            <RelativeLayout
                android:id="@+id/lay_body"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_above="@+id/lay_bottom">
                <!--扫描音乐布局-->
                <LinearLayout
                    android:id="@+id/lay_scan_music"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:orientation="vertical">
                    <ImageView
                        android:layout_width="@dimen/dp_140"
                        android:layout_height="@dimen/dp_140"
                        android:src="@mipmap/icon_empty" />
                    <!--扫描本地音乐-->
                    <com.google.android.material.button.MaterialButton
                        android:id="@+id/btn_scan_local_music"
                        style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
                        android:layout_width="@dimen/dp_140"
                        android:layout_height="@dimen/dp_40"
                        android:layout_marginTop="@dimen/dp_16"
                        android:insetTop="@dimen/dp_0"
                        android:insetBottom="@dimen/dp_0"
                        android:onClick="onClick"
                        android:text="扫描本地音乐"
                        android:textSize="@dimen/sp_14"
                        android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
                        app:backgroundTint="@color/transparent"
                        app:cornerRadius="@dimen/dp_20"
                        app:strokeColor="@color/white"
                        app:strokeWidth="@dimen/dp_1" />
                </LinearLayout>
                <!--列表-->
                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_music"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </RelativeLayout>
            <!--定位当前播放音乐按钮-->
            <com.google.android.material.button.MaterialButton
                android:id="@+id/btn_location_play_music"
                style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
                android:layout_width="@dimen/dp_28"
                android:layout_height="@dimen/dp_28"
                android:layout_above="@+id/lay_bottom"
                android:layout_alignParentRight="true"
                android:layout_margin="@dimen/dp_36"
                android:insetLeft="@dimen/dp_0"
                android:insetTop="@dimen/dp_0"
                android:insetRight="@dimen/dp_0"
                android:insetBottom="@dimen/dp_0"
                android:onClick="onClick"
                android:textSize="@dimen/sp_14"
                android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
                android:visibility="gone"
                app:backgroundTint="@color/white"
                app:cornerRadius="@dimen/dp_20"
                app:icon="@drawable/music_location"
                app:iconGravity="textStart"
                app:iconPadding="0dp"
                app:iconTint="@color/black" />
            <!--底部播放控制布局-->
            <LinearLayout
                android:id="@+id/lay_bottom"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:background="@color/bottom_bg_color"
                android:gravity="center_vertical"
                android:paddingLeft="@dimen/dp_8"
                android:paddingTop="@dimen/dp_8"
                android:paddingRight="@dimen/dp_16"
                android:paddingBottom="@dimen/dp_8">
                <!-- logo和播放进度 使用相对布局达成覆盖的效果-->
                <RelativeLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">
                    <!--logo-->
                    <com.google.android.material.imageview.ShapeableImageView
                        android:id="@+id/iv_logo"
                        android:layout_width="@dimen/dp_48"
                        android:layout_height="@dimen/dp_48"
                        android:padding="1dp"
                        android:src="@mipmap/icon_music"
                        app:shapeAppearanceOverlay="@style/circleImageStyle"
                        app:strokeColor="@color/white"
                        app:strokeWidth="@dimen/dp_2" />
                    <!--播放进度  自定义View-->
                    <com.llw.goodmusic.view.MusicRoundProgressView
                        android:id="@+id/music_progress"
                        android:layout_width="@dimen/dp_48"
                        android:layout_height="@dimen/dp_48"
                        app:radius="20dp"
                        app:strokeColor="@color/black"
                        app:strokeWidth="2dp" />
                </RelativeLayout>
                <!--歌曲信息  歌名 - 歌手 -->
                <com.google.android.material.textview.MaterialTextView
                    android:id="@+id/tv_song_name"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:ellipsize="marquee"
                    android:focusable="true"
                    android:focusableInTouchMode="true"
                    android:marqueeRepeatLimit="marquee_forever"
                    android:paddingLeft="@dimen/dp_12"
                    android:paddingRight="@dimen/dp_12"
                    android:singleLine="true"
                    android:text="Good Music"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_16" />
                <!--歌曲控制按钮-->
                <com.google.android.material.button.MaterialButton
                    android:id="@+id/btn_play"
                    android:layout_width="@dimen/dp_36"
                    android:layout_height="@dimen/dp_36"
                    android:insetLeft="@dimen/dp_0"
                    android:insetTop="@dimen/dp_0"
                    android:insetRight="@dimen/dp_0"
                    android:insetBottom="@dimen/dp_0"
                    android:onClick="onClick"
                    android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
                    app:backgroundTint="@color/transparent"
                    app:cornerRadius="@dimen/dp_18"
                    app:icon="@mipmap/icon_play"
                    app:iconGravity="textStart"
                    app:iconPadding="@dimen/dp_0"
                    app:iconSize="@dimen/dp_36" />
            </LinearLayout>
        </RelativeLayout>
    </LinearLayout>
</layout>


里面用到三个图标一个自定义View。图标你可以去我的源码里面拿,源码图标,自定义View我会写出来。在com.llw.goodmusic下新建一个view包,然后新建一个MusicRoundProgressView。


20201021181346348.png


代码如下:

package com.llw.goodmusic.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.llw.goodmusic.R;
/**
 * 圆形进度条
 *
 * @author llw
 */
public class MusicRoundProgressView extends View {
    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 画笔颜色
     */
    private int mPaintColor;
    /**
     * 半径
     */
    private float mRadius;
    /**
     * 圆环半径
     */
    private float mRingRadius;
    /**
     * 圆环宽度
     */
    private float mStrokeWidth;
    /**
     * 圆心 X 轴坐标
     */
    private int mCenterX;
    /**
     * 圆心 Y 轴坐标
     */
    private int mCenterY;
    /**
     * 总进度
     */
    private int mTotalProgress;
    /**
     * 当前进度
     */
    private int mProgress;
    public MusicRoundProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressView);
        //半径
        mRadius = typedArray.getDimension(R.styleable.RoundProgressView_radius, 40);
        //宽度
        mStrokeWidth = typedArray.getDimension(R.styleable.RoundProgressView_strokeWidth, 5);
        //颜色
        mPaintColor = typedArray.getColor(R.styleable.RoundProgressView_strokeColor, 0xFFFFFFFF);
        //圆环半径 = 半径 + 圆环宽度的1/2
        mRingRadius = mRadius + mStrokeWidth / 2;
        //画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mPaintColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mStrokeWidth);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        mCenterX = getWidth() / 2;
        mCenterY = getHeight() / 2;
        if (mProgress > 0) {
            RectF rectF = new RectF();
            rectF.left = (mCenterX - mRingRadius);
            rectF.top = (mCenterY - mRingRadius);
            rectF.right = mRingRadius * 2 + (mCenterX - mRingRadius);
            rectF.bottom = mRingRadius * 2 + (mCenterY - mRingRadius);
            canvas.drawArc(rectF, -90, ((float) mProgress / mTotalProgress) * 360, false, mPaint);
        }
    }
    /**
     * 设置进度
     */
    public void setProgress(int progress, int totalProgress) {
        mProgress = progress;
        mTotalProgress = totalProgress;
        //重绘
        postInvalidate();
    }
}


里面涉及到的样式如下:


20201021181557544.png


在styles.xml中增加如下代码:

  <!--圆形进度条-->
    <declare-styleable name="RoundProgressView">
        <attr name="radius" format="dimension"/>
        <attr name="strokeWidth" format="dimension"/>
        <attr name="strokeColor" format="color"/>
    </declare-styleable>


现在你的自定义从理论上来说就不会报错了。当然可能需要改一下包名之类,下面就是回到LocalMusicActivty。


② 初始化数据


首先在当前定位按钮后面加上这些变量

  /**
     * 底部logo图标,点击之后弹出当前播放歌曲详情页
     */
    private ShapeableImageView ivLogo;
    /**
     * 底部当前播放歌名
     */
    private MaterialTextView tvSongName;
    /**
     * 底部当前歌曲控制按钮, 播放和暂停
     */
    private MaterialButton btnPlay;
    /**
     * 音频播放器
     */
    private MediaPlayer mediaPlayer;
    /**
     * 记录当前播放歌曲的位置
     */
    public int mCurrentPosition = -1;
    /**
     * 自定义进度条
     */
    private MusicRoundProgressView musicProgress;
    /**
     * 音乐进度间隔时间
     */
    private static final int INTERNAL_TIME = 1000;
    /**
     * 图片动画
     */
    private ObjectAnimator logoAnimation;


每一个都有注释,然后绑定相关的控件


20201022151910733.png


同样点击事件必不可少


20201022152744291.png


③ 播放音乐


常规的操作是通过点击音乐列表中的某一首歌之后播放歌曲。还记得列表的点击事件在哪里吗?当然是在**showLocalMusicData()**方法里面,之前在这个方法中设置适配器和列表的一些相关属性和数据,当然还有点击事件。


回顾一下这个代码:


    //item的点击事件
        mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                if (view.getId() == R.id.item_music) {
                    if (oldPosition == -1) {
                        //未点击过 第一次点击
                        oldPosition = position;
                        mList.get(position).setCheck(true);
                    } else {
                        //大于 1次
                        if (oldPosition != position) {
                            mList.get(oldPosition).setCheck(false);
                            mList.get(position).setCheck(true);
                            //重新设置位置,当下一次点击时position又会和oldPosition不一样
                            oldPosition = position;
                        }
                    }
                    mAdapter.changeState();
                }
            }
        });


我不想这里面的代码太多,所以我新写了一个方法。


  /**
     * 控制播放位置
     *
     * @param position
     */
    private void playPositionControl(int position) {
        if (oldPosition == -1) {
            //未点击过 第一次点击
            oldPosition = position;
            mList.get(position).setCheck(true);
        } else {
            //大于 1次
            if (oldPosition != position) {
                mList.get(oldPosition).setCheck(false);
                mList.get(position).setCheck(true);
                //重新设置位置,当下一次点击时position又会和oldPosition不一样
                oldPosition = position;
            }
        }
        mAdapter.changeState();
    }


当点击这个item时将position传递给全局变量mCurrentPosition。


20201022153945210.png


然后通过changeSong(mCurrentPosition);方法来播放歌曲


/**
     * 切换歌曲
     */
    private void changeSong(int position) {
        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
        }
        try {
            //切歌前先重置,释放掉之前的资源
            mediaPlayer.reset();
            BLog.i(TAG, mList.get(position).path);
            //设置播放音频的资源路径
            mediaPlayer.setDataSource(mList.get(position).path);
            //设置播放的歌名和歌手
            tvSongName.setText(mList.get(position).song + " - " + mList.get(position).singer);
            //如果内容超过控件,则启用跑马灯效果
            tvSongName.setSelected(true);
            //开始播放前的准备工作,加载多媒体资源,获取相关信息
            mediaPlayer.prepare();
            //开始播放音频
            mediaPlayer.start();
            //播放按钮控制
            if (mediaPlayer.isPlaying()) {
                btnPlay.setIcon(getDrawable(R.mipmap.icon_pause));
                btnPlay.setIconTint(getColorStateList(R.color.gold_color));
            } else {
                btnPlay.setIcon(getDrawable(R.mipmap.icon_play));
                btnPlay.setIconTint(getColorStateList(R.color.white));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


相信代码都能够看懂,播放歌曲之前先实例化,然后重置mediaPlayer,设置相关的信息之后就开始播放,这个时候也要处理一下按钮的状态。那么现在你再列表中就可以随意点击了,点击那一首就播放哪一首。现在的确是有播放音乐了,但是我也需要暂停啊。


④ 暂停音乐


在底部播放按钮btn_play的点击事件中进行处理。


      case R.id.btn_play:
                //控制音乐 播放和暂停
                if (mediaPlayer == null) {
                    //没有播放过音乐 ,点击之后播放第一首
                    oldPosition = 0;
                    mCurrentPosition = 0;
                    mList.get(mCurrentPosition).setCheck(true);
                    mAdapter.changeState();
                    changeSong(mCurrentPosition);
                } else {
                    //播放过音乐  暂停或者播放
                    if (mediaPlayer.isPlaying()) {
                        mediaPlayer.pause();
                        btnPlay.setIcon(getDrawable(R.mipmap.icon_play));
                        btnPlay.setIconTint(getColorStateList(R.color.white));
                    } else {
                        mediaPlayer.start();
                        btnPlay.setIcon(getDrawable(R.mipmap.icon_pause));
                        btnPlay.setIconTint(getColorStateList(R.color.gold_color));
                    }
                }
                break;


也要考虑到用户一进入这个页面直接点击底部播放按钮的因素,这样就直接播放列表中的第一首,至于记录当前歌曲的位置和播放进度,下一次进入时继续这个进度,这个功能放到后面来实现,先考虑这个页面的。


⑤ 自动下一曲


说道自动下一曲,就是没有人为干涉的情况下,当前歌曲播放完毕之后自行播放下一首。这里需要实现MediaPlayer的OnCompletionListener,r方法如下:


  /**
     * 播放完成之后自动下一曲
     *
     * @param mp
     */
    @Override
    public void onCompletion(MediaPlayer mp) {
        int position = -1;
        if (mList != null) {
            if (mCurrentPosition == mList.size() - 1) {
                //当前为最后一首歌时,则切换到列表的第一首歌
                position = mCurrentPosition = 0;
            } else {
                position = ++mCurrentPosition;
            }
        }
        //移动播放位置
        playPositionControl(position);
        //切歌
        changeSong(position);
    }


在这个方法里面通过监听到歌曲完毕之后,先判断是否在最后一首歌,是则从第一首歌开始,不是则位置+1,然后移动播放的位置,更新列表数据,之后就通过刚才得到的位置进行切歌。当然这个功能要完成还需要最后一个不走。记得加一个监听才行,如下所示,可以在对MediaPlayer进行实例化的时候设置完成播放时的监听。不加,则你的音乐播放完了就一直在哪里不动。


20201022162334504.png

⑥ 播放进度


播放进度对于用户来说是比较重要的,这里我没有用Seekbar,来让用户看到播放进度并且可以手动拖动,而是用了一个自定义View,只用来显示歌曲当前的播放进度,没有具体的播放时间和操作控件,这样做会让你的页面显得很简洁,同时静中有动,说了这么多,不如写代码来的实际。

private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {
            // 展示给进度条和当前时间
            int progress = mediaPlayer.getCurrentPosition();
            musicProgress.setProgress(progress, mediaPlayer.getDuration());
            //更新进度
            updateProgress();
            return true;
        }
    });


首先通过Handle来发送消息,定时更新进度。


  /**
     * 更新进度
     */
    private void updateProgress() {
        // 使用Handler每间隔1s发送一次空消息,通知进度条更新
        // 获取一个现成的消息
        Message msg = Message.obtain();
        // 使用MediaPlayer获取当前播放时间除以总时间的进度
        int progress = mediaPlayer.getCurrentPosition();
        msg.arg1 = progress;
        mHandler.sendMessageDelayed(msg, INTERNAL_TIME);
    }


发送消息


20201022163423385.png


在changeSong方法中,当开始播放时,设置当前的进度和音乐的总进度,然后通过**updateProgress()**方法来发送消息。在handler中更新进行自定义View的重新位置,这样就可以看到进度增长了。因为不管你是点击列表得item还是点击底部的播放按钮,都会进入changeSong方法中,所以我放在这个里面。


⑦ 旋转动画


在静中增加动,可以给用户更好的体验,所以我想到了图片的自转。通过属性动画来实现。


  /**
     * 初始化动画
     */
    private void initAnimation() {
        logoAnimation = ObjectAnimator.ofFloat(ivLogo, "rotation", 0.0f, 360.0f);
        logoAnimation.setDuration(3000);
        logoAnimation.setInterpolator(new LinearInterpolator());
        logoAnimation.setRepeatCount(-1);
        logoAnimation.setRepeatMode(ObjectAnimator.RESTART);
    }


在这个方法里面进行了动画的参数配置,我来解释一下上面的配置做了什么事情,首先是顺时针方向旋转360°。然后旋转一圈耗时3s,使用线性插值器,重复旋转。下面就是用的地方了。


20201022164628476.png


在歌曲播放的时候,开始旋转,可以暂停和继续。同时在底部的播放按钮里面也需要做相应的动画控制。


20201022164819660.png


最后在播放完成监听方法里面重置这个动画


20201022164907681.png


好了,功能就写完了,下面直接运行吧。


⑧ 运行效果图


20201022165505400.gif


看这个图片是不是有那么点意思了呢?


结语


写代码的工程中逻辑很重要,最好是一气呵成,当你的思路被打断,无法集中注意力时,是写不好代码的,而文章则是在代码写好之后再写的,如果有什么问题及时提出来,我会尽快解决。


相关文章
|
12月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
682 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
12月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
220 0
|
12月前
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
263 0
|
12月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
479 65
Android自定义view之网易云推荐歌单界面
|
12月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
868 84
|
10月前
|
存储 Android开发 数据安全/隐私保护
Thanox安卓系统增加工具下载,管理、阻止、限制后台每个APP运行情况
Thanox是一款Android系统管理工具,专注于权限、后台启动及运行管理。支持应用冻结、系统优化、UI自定义和模块管理,基于Xposed框架开发,安全可靠且开源免费,兼容Android 6.0及以上版本。
1168 4
|
12月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
314 3
|
12月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
209 0
Android自定义view之围棋动画(化繁为简)
|
12月前
|
Java Android开发 开发者
Android自定义view之围棋动画
本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。
258 0
|
7月前
|
缓存 移动开发 JavaScript
如何优化UniApp开发的App的启动速度?
如何优化UniApp开发的App的启动速度?
1210 139

热门文章

最新文章