zoukankan      html  css  js  c++  java
  • 动画学习之Music图形绘制

    今天来实现一个类似于网易云音乐类似的动态效果,在用网易云音乐听歌时会有一个类似这样的效果,如下:

    而咱们这次要实现的效果如下:

    music图形的绘制:

    在实现动画之前先来将静态的图形绘制出来, 如下:

    首先绘制两个圆,如下:

    所以新建一个自定义View,如下:

    /**
     * 音乐加载View
     * 首先绘制静态图形
     */
    public class MusicLoadingView extends View {
    
        //variables
        private Paint paint;
    
        public MusicLoadingView(Context context) {
            this(context, null);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setAntiAlias(true);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //1、首先绘制两个圆(大圆和小圆)
            //2、绘制两段对称的弧
        }
    }

    而绘制圆需要确定圆心和半径,而这两个值的确定需要先知道圆的直径,而这里的直径取咱们自定义View的宽高的最小值,如下:

    有了这个直径之后,接下来先绘制大圆了,如下:

    此时编译运行:

    呃~~不是空心的,那简单,更改画笔的样式既可:

    再次运行:

    接下来再来绘制中心的小圆,很显然需要给这个小圆定义一下它的半径,所以:

    编译运行:

    嗯~~但是对比最终效果发现这个小圆的线太细了,那就加粗呗,如下:

    编译运行:

    但是!!!大圆太贴边了,都有点被切的感觉,如下:

    所以应该将其与View的边界有一定间隔,很简单,将大圆的半径缩小一点既可,如下:

    大小圆目前已经绘制好了,接下来则需要要圆上绘制两段弧,如下:

    关于如何绘制弧通过之前的练习已经非常熟悉了,这里就不过多解释,只是需要说明的一点,上面两段对立的弧其角度相差180度,很容易理解,所以接下来先绘制一对弧,如下:

    /**
     * 音乐加载View
     * 首先绘制静态图形
     */
    public class MusicLoadingView extends View {
    
        //constants
        /* 小圆的半径 */
        private static final float SMALL_RADIUS = 5f;
        private static final float SWEEP_ANGLE = 80f;
    
        //variables
        private Paint paint;
        /* 大圆的直径:取屏幕宽高的较小值 */
        private int length;
    
        public MusicLoadingView(Context context) {
            this(context, null);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);//设置成空心的样式
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            length = Math.min(w, h);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //1、首先绘制两个圆(大圆和小圆)
            paint.setStrokeWidth(2);
            canvas.drawCircle(length / 2, length / 2, length / 2 - SMALL_RADIUS, paint);//大圆
            paint.setStrokeWidth(5);
            canvas.drawCircle(length / 2, length / 2, SMALL_RADIUS, paint);//小圆
    
            //2、绘制两段对称的弧
            RectF rectF = new RectF(length / 2 - length / 3, length / 2 - length / 3, length / 2 + length / 3, length / 2 + length / 3);
            canvas.drawArc(rectF, 0, SWEEP_ANGLE, false, paint);//第一段弧
            canvas.drawArc(rectF, 180, SWEEP_ANGLE, false, paint);//第一段对称弧
        }
    }

    其外切矩形就是以大圆的直径为参照来定的,这个可以根据实际业务场景动态去决定,下面编译运行一下:

    嗯~~弧正常被设置了,不过貌似太粗了,原因是由于它是在绘制小圆之后进行绘制的,而小圆更改了画笔的粗度,要改细咱们可以将下面代码进行对换一下:

    此时再次编译运行:

    接下来再来绘制另外一对弧那就依葫芦画瓢呗,只是说弧度比刚才的要小一点,直接上代码:

    编译运行:

    至此~~静态效果就已经实现啦,接下来则想办法让其动起来。

    music图形动画实现:

    此次的动画实现还是跟上一次WIFI【http://www.cnblogs.com/webor2006/p/8322387.html】的实现思路类似,纯代码来控制而不采用Android提供的动画,在上一次中是采用Handler的方式来按一定的时间不断去调用invalidate()来达到不断绘制的目的,如:

    对于这一次的实现思路基本类似,但是这次不采用handler了,而是直接在onDraw()方法中不断调用invalidate()方法,因为这次的动画本身要求就是不断绘制不需要加延时,所以:

    而最终效果关键之所在在于两对弧不断的在“运动”,而弧的具体位置的改变也就是在绘制弧形时的startAngle来决定,所以咱们需要将弧的开始角度提成变量,以便在onDraw()方法中不断去更改起始角度来达到"运动"的目的,所以:

    /**
     * 音乐加载View
     * 首先绘制静态图形
     */
    public class MusicLoadingView extends View {
    
        //constants
        /* 小圆的半径 */
        private static final float SMALL_RADIUS = 5f;
        private static final float SWEEP_ANGLE = 80f;
    
        //variables
        private Paint paint;
        /* 大圆的直径:取屏幕宽高的较小值 */
        private int length;
        /* 第一条弧的起始角度 */
        private float startAngle1 = 0;
        /* 第二条对称弧的起始角度 */
        private float startAngle2 = 180;
    
        public MusicLoadingView(Context context) {
            this(context, null);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);//设置成空心的样式
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            length = Math.min(w, h);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //1、首先绘制两个圆(大圆和小圆)
            paint.setStrokeWidth(5);
            canvas.drawCircle(length / 2, length / 2, SMALL_RADIUS, paint);//小圆
            paint.setStrokeWidth(2);
            canvas.drawCircle(length / 2, length / 2, length / 2 - SMALL_RADIUS, paint);//大圆
    
            //2、绘制两段对称的弧
            RectF rectF = new RectF(length / 2 - length / 3, length / 2 - length / 3, length / 2 + length / 3, length / 2 + length / 3);
            canvas.drawArc(rectF, startAngle1, SWEEP_ANGLE, false, paint);//第一段弧
            canvas.drawArc(rectF, startAngle2, SWEEP_ANGLE, false, paint);//第一段对称弧
    
            rectF = new RectF(length / 2 - length / 4, length / 2 - length / 4, length / 2 + length / 4, length / 2 + length / 4);
            canvas.drawArc(rectF, startAngle1, SWEEP_ANGLE, false, paint);//第二段弧
            canvas.drawArc(rectF, startAngle2, SWEEP_ANGLE, false, paint);//第二段对称弧
    
            invalidate();
        }
    }

    接着每次绘制时不断改变其角度,如下:

    此时编译运行就是在开篇所看到的效果了,另外还差一个收尾工作,就是当app退出时的处理,目前onDraw()是不断在重绘,所以需要控制一下生命周期合理释放资源,在上次wifi的资源释放时是采用给View增加一个onDestroy()在Activity退出时主动调一下,而这次不借助Activity了,而是在View中能知道什么时候销毁了,具体做法如下:

    /**
     * 音乐加载View
     * 首先绘制静态图形
     */
    public class MusicLoadingView extends View {
    
        //constants
        /* 小圆的半径 */
        private static final float SMALL_RADIUS = 5f;
        private static final float SWEEP_ANGLE = 80f;
    
        //variables
        private Paint paint;
        /* 大圆的直径:取屏幕宽高的较小值 */
        private int length;
        /* 第一条弧的起始角度 */
        private float startAngle1 = 0;
        /* 第二条对称弧的起始角度 */
        private float startAngle2 = 180;
        /*是否不断绘制,用来控制生命周期*/
        private boolean isLoopDraw = true;
    
        public MusicLoadingView(Context context) {
            this(context, null);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public MusicLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);//设置成空心的样式
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            length = Math.min(w, h);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            startAngle1 += 3;
            startAngle2 += 3;
            //1、首先绘制两个圆(大圆和小圆)
            paint.setStrokeWidth(5);
            canvas.drawCircle(length / 2, length / 2, SMALL_RADIUS, paint);//小圆
            paint.setStrokeWidth(2);
            canvas.drawCircle(length / 2, length / 2, length / 2 - SMALL_RADIUS, paint);//大圆
    
            //2、绘制两段对称的弧
            RectF rectF = new RectF(length / 2 - length / 3, length / 2 - length / 3, length / 2 + length / 3, length / 2 + length / 3);
            canvas.drawArc(rectF, startAngle1, SWEEP_ANGLE, false, paint);//第一段弧
            canvas.drawArc(rectF, startAngle2, SWEEP_ANGLE, false, paint);//第一段对称弧
    
            rectF = new RectF(length / 2 - length / 4, length / 2 - length / 4, length / 2 + length / 4, length / 2 + length / 4);
            canvas.drawArc(rectF, startAngle1, SWEEP_ANGLE, false, paint);//第二段弧
            canvas.drawArc(rectF, startAngle2, SWEEP_ANGLE, false, paint);//第二段对称弧
    
            if (isLoopDraw)
                invalidate();//让View不断重绘
        }
    
        //当自定义控制脱离窗体,即将销毁的时候会被调用
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            Log.e("cexo", "MusicLoadingView.onDetachedFromWindow()");
            isLoopDraw = false;
        }
    }

    最后运行在退出时可以看到它被调用了,如下:

    至此~效果完美实现!

  • 相关阅读:
    ubuntu,day1基础命令,shutdown,man,touch,rm,mv,cp,stat,locale,apt,date,tzselect,cal,快捷方式,echo,查看文件
    day 7 编码
    NO.6 appium-网络设置
    NO.5 appium-滑动和点击
    NO.4 appium-定位
    NO.3 appium-退出/启动
    NO.2 appium-安装和卸载
    NO.1 appium-关于输入法
    Springboot框架搭建
    遍历Map的四种方法
  • 原文地址:https://www.cnblogs.com/webor2006/p/8604863.html
Copyright © 2011-2022 走看看