Android自定义view之围棋动画(化繁为简)

简介: 本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。

系列文章目录

Android自定义view之围棋动画(化繁为简)

文章最后有源码

前言

在上一篇文章中实现了简单的动态效果,假设这是一种动画的类型,那么能否像Android中的动画一样多个效果结合显示呢,这篇文章将以两个例子来进行拓展。第二个例子为围棋动画的精简版

上篇文章链接:Android自定义view之利用drawArc方法实现动态效果

一、上篇文章的实现

        mSweep += SWEEP_INC;
        if (mSweep > 360) {
   
            mSweep -= 360;
        }
        //刷新View
        invalidate();

二、第一个拓展(未优化)

效果:

1.gif

思路:

1.在简单的实现中,仅借助了一个值来重复的循环得以实现动态的效果。而大多情况下动画是只显示一次的。而只让动效实现一次很简单:如下。

  if (mSweep > 360) {
   
            //mSweep -= 360;
        }

如上,不将修改后的值复原即可。

这个效果的实现关键是将添加一个实心的样式画笔

 mPaint1.setStyle(Paint.Style.FILL);

2.为了达到动画只执行一次的效果,需要借助一个布尔值。

    private boolean viewContinue=true;

而逻辑代码也就变成了

    if (viewContinue==true){
   
            mSweep += SWEEP_INC;
            if (mSweep > 360) {
   
                mSweep1 += SWEEP_INC;
                if (mSweep1>360){
   
                  viewContinue=false;
                }
            }
            //刷新View
            invalidate();
        }

案例拓展:

  • 可提供给用户一个方法使动画再执行一次,只需将mSweep,mSweep1改为0,并将viewContinue值改为true即可。
  • 可放在数组中,进行多个效果的迭代。

此案例源码

public class MySampleView extends View {
   
    private int mWidth;
    private int mHeight;
    private int useWidth, minwidth;
    private Paint mPaint,mPaint1;
    private Paint mFramePaint;
    private RectF mBigOval;
    private float mStart;
    private float mSweep ,mSweep1;
    private boolean viewContinue=true;

    private static final float SWEEP_INC = 2;

    public MySampleView(Context context) {
   
        super(context);
    }

    public MySampleView(Context context, @Nullable AttributeSet attrs) {
   
        super(context, attrs);
    }

    public MySampleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public MySampleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
   
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init() {
   
        initPaint();
    }
    private void initPaint() {
   
        //初始化
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(0x88FF0000);
        mPaint.setStrokeWidth(4);

        mPaint1 = new Paint();
        mPaint1.setAntiAlias(true);
        mPaint1.setStyle(Paint.Style.FILL);
        mPaint1.setColor(0x88FF0000);
        //初始化
        mFramePaint = new Paint();
        mFramePaint.setAntiAlias(true);
        mFramePaint.setStyle(Paint.Style.STROKE);
        mFramePaint.setStrokeWidth(0);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        useWidth = mWidth;
        if (mWidth > mHeight) {
   
            useWidth = mHeight;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
   
        init();
        //定义一个最小标识
        minwidth = useWidth / 10;
        mBigOval = new RectF(minwidth, minwidth, minwidth*9, minwidth*9);
        //绘制背景
        canvas.drawColor(Color.WHITE);
        canvas.drawRect(mBigOval, mFramePaint);
        canvas.drawArc(mBigOval, mStart, mSweep, true, mPaint);
        canvas.drawArc(mBigOval, mStart, mSweep1, true, mPaint1);

        if (viewContinue==true){
   
            mSweep += SWEEP_INC;
            if (mSweep > 360) {
   
                mSweep1 += SWEEP_INC;
                if (mSweep1>360){
   
                  viewContinue=false;
                }

            }
            //刷新View
            invalidate();
        }


    }
}

三、第二个案例(本文重点)

相信很多朋友是看过博主这篇文章的:Android自定义view之围棋动画

效果

2.gif

之前文章写得那么复杂是为了介绍一些Android自定义view的一些知识,而其实实现类似的效果极其的简单,那么直接开始。

1.先实现一个球的直线运动

效果:

3.gif

思路:

借助一个偏移量改变绘制的小球位置。

代码:

displacement为偏移量

 mBigOval = new RectF(minwidth+displacement, minwidth, minwidth*2+displacement, minwidth*2);
        //绘制背景
        canvas.drawColor(Color.WHITE);
        //canvas.drawRect(mBigOval, mFramePaint);
        canvas.drawArc(mBigOval, 0, 360, false, mPaint);


        if (viewContinue){
   
            displacement += SWEEP_INC;
            if (displacement > minwidth*7) {
   
              viewContinue=false;
            }
            //刷新View
            invalidate();
        }

2.绘制另一个小球反方向运动

效果:

4.gif

代码:

  mBigOval = new RectF(minwidth+displacement, minwidth, minwidth*2+displacement, minwidth*2);
        mBigOval1 = new RectF(minwidth*8-displacement, minwidth, minwidth*9-displacement, minwidth*2);
        //绘制背景
        canvas.drawColor(Color.WHITE);
        canvas.drawArc(mBigOval, 0, 360, false, mPaint);
        canvas.drawArc(mBigOval1, 0, 360, false, mPaint1);


        if (viewContinue){
   
            displacement += SWEEP_INC;
            if (displacement > minwidth*7) {
   
              viewContinue=false;
            }
            //刷新View
            invalidate();
        }

3.改变匀速

根据情况增加displacement的值。如下

        if (viewContinue){
   
            displacement += SWEEP_INC;
            if (displacement>minwidth*2){
   
                displacement+=2;
            }
            if (displacement > minwidth*7) {
   
              viewContinue=false;
            }
            //刷新View
            invalidate();
        }

4.动画重复执行

效果:

5.gif

思路:再添加另外的一个标识符,同理,将displacement的值变小。

  if (viewContinue){
   
            displacement += SWEEP_INC;
            if (displacement>minwidth*2){
   
                displacement+=2;
            }
            if (displacement > minwidth*7) {
   
              viewContinue=false;
                viewContinue1=true;
            }
            //刷新View
            invalidate();
        }
       if (viewContinue1){
   
           displacement -= SWEEP_INC;
           if (displacement<minwidth*5){
   
               displacement-=2;
           }
           if (displacement <0) {
   
               viewContinue1=false;
               viewContinue=true;
           }
           //刷新View
           invalidate();
       }

如只需要执行一次来回,在viewContinue1的判断中不将viewContinue的值设为true。

5.源码

public class MySampleView extends View {
   
    private int mWidth;
    private int mHeight;
    private int useWidth, minwidth;
    private Paint mPaint,mPaint1;
    private Paint mFramePaint;
    private RectF mBigOval,mBigOval1;
    private float displacement;
    private boolean viewContinue=true,viewContinue1=false;

    private static final float SWEEP_INC = 2;

    public MySampleView(Context context) {
   
        super(context);
    }

    public MySampleView(Context context, @Nullable AttributeSet attrs) {
   
        super(context, attrs);
    }

    public MySampleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public MySampleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
   
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init() {
   
        initPaint();
    }
    private void initPaint() {
   
        //初始化
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(0x88FF0000);

        mPaint1 = new Paint();
        mPaint1.setAntiAlias(true);
        mPaint1.setStyle(Paint.Style.FILL);
        mPaint1.setColor(0x88888888);
        //初始化
        mFramePaint = new Paint();
        mFramePaint.setAntiAlias(true);
        mFramePaint.setStyle(Paint.Style.STROKE);
        mFramePaint.setStrokeWidth(0);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        useWidth = mWidth;
        if (mWidth > mHeight) {
   
            useWidth = mHeight;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
   
        init();
        //定义一个最小标识
        minwidth = useWidth / 10;
        mBigOval = new RectF(minwidth+displacement, minwidth, minwidth*2+displacement, minwidth*2);
        mBigOval1 = new RectF(minwidth*8-displacement, minwidth, minwidth*9-displacement, minwidth*2);
        //绘制背景
        canvas.drawColor(Color.WHITE);
        //canvas.drawRect(mBigOval, mFramePaint);
        canvas.drawArc(mBigOval, 0, 360, false, mPaint);
        canvas.drawArc(mBigOval1, 0, 360, false, mPaint1);


        if (viewContinue){
   
            displacement += SWEEP_INC;
            if (displacement>minwidth*2){
   
                displacement+=2;
            }
            if (displacement > minwidth*7) {
   
              viewContinue=false;
                viewContinue1=true;
            }
            //刷新View
            invalidate();
        }
       if (viewContinue1){
   
           displacement -= SWEEP_INC;
           if (displacement<minwidth*5){
   
               displacement-=2;
           }
           if (displacement <0) {
   
               viewContinue1=false;
               viewContinue=true;
           }
           //刷新View
           invalidate();
       }

    }
}

四、总结

通过第二个案例实现,应该能够拓展开大家的思路,实现更加好的动态效果,相对于博主之前类似效果的文章,算是化繁为简了。想来应该能够对各位有所帮助。欢迎留言。

相关文章
|
2月前
|
Android开发 开发者
Android利用SVG实现动画效果
本文介绍了如何在Android中利用SVG实现动画效果。首先通过定义`pathData`参数(如M、L、Z等)绘制一个简单的三角形SVG图形,然后借助`objectAnimator`实现动态的线条绘制动画。文章详细讲解了从配置`build.gradle`支持VectorDrawable,到创建动画文件、关联SVG与动画,最后在Activity中启动动画的完整流程。此外,还提供了SVG绘制原理及工具推荐,帮助开发者更好地理解和应用SVG动画技术。
128 30
|
2月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
148 65
Android自定义view之网易云推荐歌单界面
|
2月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
398 84
|
2月前
|
Android开发 开发者
Android SVG动画详细例子
本文详细讲解了在Android中利用SVG实现动画效果的方法,通过具体例子帮助开发者更好地理解和应用SVG动画。文章首先展示了动画的实现效果,接着回顾了之前的文章链接及常见问题(如属性名大小写错误)。核心内容包括:1) 使用阿里图库获取SVG图形;2) 借助工具将SVG转换为VectorDrawable;3) 为每个路径添加动画绑定属性;4) 创建动画文件并关联SVG;5) 在ImageView中引用动画文件;6) 在Activity中启动动画。文末还提供了完整的代码示例和源码下载链接,方便读者实践操作。
205 65
|
2月前
|
XML Java Maven
Android线条等待动画JMWorkProgress(可添加依赖直接使用)
这是一篇关于Android线条等待动画JMWorkProgress的教程文章,作者计蒙将其代码开源至GitHub,提升可读性。文章介绍了如何通过添加依赖库使用该动画,并详细讲解了XML与Java中的配置方法,包括改变线条颜色、宽度、添加文字等自定义属性。项目已支持直接依赖集成(`implementation &#39;com.github.Yufseven:JMWorkProgress:v1.0&#39;`),开发者可以快速上手实现炫酷的等待动画效果。文末附有GitHub项目地址,欢迎访问并点赞支持!
68 26
|
2月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
|
9月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
9月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
80 2
|
9月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
158 3
|
11月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
216 3