Android自定义view之围棋动画

好久不见,最近公众号内粉丝要求上新一篇有点难度的自定义view文章,所以它来了!!


干货文,建议收藏

文章目录

  • Android自定义view之围棋动画
  • 前言
  • 完成效果图
  • 一、测量
    • 1.获取宽高
    • 2.定义测量最小长度
  • 二、绘制背景(棋盘)
    • 1.初始化画笔
    • 2.画棋盘
    • 3.补棋盘瑕疵
  • 三.画个不可改变的棋子(以便于了解动画移动位置)
  • 四.为动画开始做准备以及动画
    • 1.三个辅助类为动画做准备(参数模仿Android官方Demo)
    • 2.自定义该接口实例来控制动画的更新计算表达式
    • 3.棋子的创建
    • 4.动画的创建
    • 5.两个动画的同步执行
    • 6.效果图
    • 7.解决第6步问题
  • 五.自定义属性
  • 六.自定义属性设置后运行效果
  • 七.小改变,视觉效果就不一样了!
  • 八.源码
  • 总结


前言

废话不多说直接开始


老规矩,文章最后有源码

完成效果图

棋子加渐变色

棋子不加渐变色

一、测量

1.获取宽高

 @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;        }    }

2.定义测量最小长度

将布局分为10份。以minwidth的1,3,5,7,9的倍数为标准点。

        minwidth = useWidth / 10;

二、绘制背景(棋盘)

1.初始化画笔

        mPaint = new Paint();        //创建画笔对象        mPaint.setColor(Color.BLACK);    //设置画笔颜色        mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充        mPaint.setStrokeWidth(4f);     //设置画笔宽度为10px        mPaint.setAntiAlias(true);     //设置抗锯齿        mPaint.setAlpha(255);        //设置画笔透明度

2.画棋盘

        //细的X轴        canvas.drawLine(minwidth, 3 * minwidth, 9 * minwidth, 3 * minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 5 * minwidth, 9 * minwidth, 5 * minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 7 * minwidth, 9 * minwidth, 7 * minwidth, mPaint);// 斜线        //细的y轴        canvas.drawLine(3 * minwidth, minwidth, 3 * minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(5 * minwidth, minwidth, 5 * minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(7 * minwidth, minwidth, 7 * minwidth, 9 * minwidth, mPaint);// 斜线        mPaint.setStrokeWidth(8f);        //粗的X轴(边框)        canvas.drawLine(minwidth, minwidth, 9 * minwidth, minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 9 * minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线        //粗的y轴(边框)        canvas.drawLine(minwidth, minwidth, minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(9 * minwidth, minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线

绘制完后,发现有点小瑕疵
效果图:

3.补棋盘瑕疵

        canvas.drawPoint(minwidth, minwidth, mPaint);        canvas.drawPoint(9 * minwidth, minwidth, mPaint);        canvas.drawPoint(minwidth, 9 * minwidth, mPaint);        canvas.drawPoint(9 * minwidth, 9 * minwidth, mPaint);

效果图:

三.画个不可改变的棋子(以便于了解动画移动位置)

位置比例
(3,3)(3,5)(3,7)
(5,3)(5,5)(5,7)
(7,3)(7,5)(7,7)

        //画围棋        canvas.drawCircle(3*minwidth, 3*minwidth, useWidth/16, mPaint);        canvas.drawCircle(3*minwidth, 7*minwidth, useWidth/16, mPaint);        canvas.drawCircle(5*minwidth, 5*minwidth, useWidth/16, mPaint);        canvas.drawCircle(7*minwidth, 3*minwidth, useWidth/16, mPaint);        canvas.drawCircle(7*minwidth, 7*minwidth, useWidth/16, mPaint);        mPaint.setColor(rightcolor);        canvas.drawCircle(3*minwidth, 5*minwidth, useWidth/16, mPaint);        canvas.drawCircle(5*minwidth, 3*minwidth, useWidth/16, mPaint);        canvas.drawCircle(5*minwidth, 7*minwidth, useWidth/16, mPaint);        canvas.drawCircle(7*minwidth, 5*minwidth, useWidth/16, mPaint);

效果图:

四.为动画开始做准备以及动画

1.三个辅助类为动画做准备(参数模仿Android官方Demo)

主要为get set构造,代码会贴到最后

2.自定义该接口实例来控制动画的更新计算表达式

public class XYEvaluator implements TypeEvaluator {         public Object evaluate(float fraction, Object startValue, Object endValue) {             XYHolder startXY = (XYHolder) startValue;        XYHolder endXY = (XYHolder) endValue;        return new XYHolder(startXY.getX() + fraction * (endXY.getX() - startXY.getX()),                startXY.getY() + fraction * (endXY.getY() - startXY.getY()));    }}

3.棋子的创建

    private ShapeHolder createBall(float x, float y, int color) {             OvalShape circle = new OvalShape();        circle.resize(useWidth / 8f, useWidth / 8f);        ShapeDrawable drawable = new ShapeDrawable(circle);        ShapeHolder shapeHolder = new ShapeHolder(drawable);        shapeHolder.setX(x - useWidth / 16f);        shapeHolder.setY(y - useWidth / 16f);        Paint paint = drawable.getPaint();        paint.setColor(color);        return shapeHolder;    }

4.动画的创建

    private void createAnimation() {             if (bounceAnim == null) {                 XYHolder lstartXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            XYHolder processXY = new XYHolder(7 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            XYHolder lendXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            bounceAnim = ObjectAnimator.ofObject(ballHolder, "xY",                    new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);            bounceAnim.setDuration(animaltime);            bounceAnim.setRepeatCount(ObjectAnimator.INFINITE);            bounceAnim.setRepeatMode(ObjectAnimator.RESTART);            bounceAnim.addUpdateListener(this);        }        if (bounceAnim1 == null) {                 XYHolder lstartXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            XYHolder processXY = new XYHolder(3 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            XYHolder lendXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            bounceAnim1 = ObjectAnimator.ofObject(ballHolder1, "xY",                    new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);            bounceAnim1.setDuration(animaltime);            bounceAnim1.setRepeatCount(ObjectAnimator.INFINITE);            bounceAnim1.setRepeatMode(ObjectAnimator.RESTART);            bounceAnim1.addUpdateListener(this);        }    }

5.两个动画的同步执行

        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.play(bounceAnim).with(bounceAnim1);        animatorSet.start();

6.效果图


视觉效果:感觉白子不太明显

7.解决第6步问题

在棋子的创建方法中添加渐变色

        RadialGradient gradient = new RadialGradient(useWidth / 16f, useWidth / 16f,                useWidth / 8f, color, Color.GRAY, Shader.TileMode.CLAMP);        paint.setShader(gradient);        shapeHolder.setPaint(paint);

效果图:

五.自定义属性

attrs文件:

    <declare-styleable name="WeiqiView"><!--        黑子颜色-->        <attr name="leftscolor" format="reference|color"/><!--        白子颜色-->        <attr name="rightscolor" format="reference|color"/><!--        棋盘颜色-->        <attr name="qipancolor" format="reference|color"/><!--        动画时间-->        <attr name="animalstime" format="integer"/>    </declare-styleable>

java文件中获取

    /**     * 获取自定义属性     */    private void initCustomAttrs(Context context, AttributeSet attrs) {             //获取自定义属性        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WeiqiView);        //获取颜色        leftcolor = ta.getColor(R.styleable.WeiqiView_leftscolor, Color.BLACK);        rightcolor = ta.getColor(R.styleable.WeiqiView_rightscolor, Color.WHITE);        qipancolor = ta.getColor(R.styleable.WeiqiView_qipancolor, Color.BLACK);        //获取动画时间        animaltime = ta.getInt(R.styleable.WeiqiView_animalstime, 2000);        //回收        ta.recycle();    }

六.自定义属性设置后运行效果

七.小改变,视觉效果就不一样了!

然后,把背景注释,像不像那些等待动画?

八.源码

WeiqiView.java

public class WeiqiView extends View implements ValueAnimator.AnimatorUpdateListener {         private Paint mPaint;    private int mWidth;    private int mHeight;    private int useWidth, minwidth;    private int leftcolor;    private int rightcolor;    private int qipancolor;    private int animaltime;    //画一个圆(棋子)    ValueAnimator bounceAnim, bounceAnim1 = null;    ShapeHolder ball, ball1 = null;    QiziXYHolder ballHolder, ballHolder1 = null;    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public WeiqiView(Context context) {             this(context, null);    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public WeiqiView(Context context, @Nullable AttributeSet attrs) {             this(context, attrs, 0);    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public WeiqiView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {             this(context, attrs, defStyleAttr, 0);        initCustomAttrs(context, attrs);    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public WeiqiView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {             super(context, attrs, defStyleAttr, defStyleRes);    }    private void init() {             initPaint();    }    /**     * 获取自定义属性     */    private void initCustomAttrs(Context context, AttributeSet attrs) {             //获取自定义属性。        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WeiqiView);        //获取颜色        leftcolor = ta.getColor(R.styleable.WeiqiView_leftscolor, Color.BLACK);        rightcolor = ta.getColor(R.styleable.WeiqiView_rightscolor, Color.WHITE);        qipancolor = ta.getColor(R.styleable.WeiqiView_qipancolor, Color.BLACK);        animaltime = ta.getInt(R.styleable.WeiqiView_animalstime, 2000);        //回收        ta.recycle();    }    /**     * 初始化画笔     */    private void initPaint() {             mPaint = new Paint();        //创建画笔对象        mPaint.setColor(Color.BLACK);    //设置画笔颜色        mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充        mPaint.setStrokeWidth(4f);     //设置画笔宽度为10px        mPaint.setAntiAlias(true);     //设置抗锯齿        mPaint.setAlpha(255);        //设置画笔透明度    }    @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;        }    }    @RequiresApi(api = Build.VERSION_CODES.KITKAT)    @Override    protected void onDraw(Canvas canvas) {             super.onDraw(canvas);        init();        minwidth = useWidth / 10;        mPaint.setColor(qipancolor);        if (ball == null) {                 ball = createBall(3 * minwidth, 3 * minwidth, leftcolor);            ballHolder = new QiziXYHolder(ball);        }        if (ball1 == null) {                 ball1 = createBall(7 * minwidth, 7 * minwidth, rightcolor);            ballHolder1 = new QiziXYHolder(ball1);        }        //细的X轴        canvas.drawLine(minwidth, 3 * minwidth, 9 * minwidth, 3 * minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 5 * minwidth, 9 * minwidth, 5 * minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 7 * minwidth, 9 * minwidth, 7 * minwidth, mPaint);// 斜线        //细的y轴        canvas.drawLine(3 * minwidth, minwidth, 3 * minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(5 * minwidth, minwidth, 5 * minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(7 * minwidth, minwidth, 7 * minwidth, 9 * minwidth, mPaint);// 斜线        mPaint.setStrokeWidth(8f);        //粗的X轴(边框)        canvas.drawLine(minwidth, minwidth, 9 * minwidth, minwidth, mPaint);// 斜线        canvas.drawLine(minwidth, 9 * minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线        //粗的y轴(边框)        canvas.drawLine(minwidth, minwidth, minwidth, 9 * minwidth, mPaint);// 斜线        canvas.drawLine(9 * minwidth, minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线        //补瑕疵        canvas.drawPoint(minwidth, minwidth, mPaint);        canvas.drawPoint(9 * minwidth, minwidth, mPaint);        canvas.drawPoint(minwidth, 9 * minwidth, mPaint);        canvas.drawPoint(9 * minwidth, 9 * minwidth, mPaint);//        //画围棋//        canvas.drawCircle(3*minwidth, 3*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(3*minwidth, 7*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(5*minwidth, 5*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(7*minwidth, 3*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(7*minwidth, 7*minwidth, useWidth/16, mPaint);//        mPaint.setColor(rightcolor);//        canvas.drawCircle(3*minwidth, 5*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(5*minwidth, 3*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(5*minwidth, 7*minwidth, useWidth/16, mPaint);//        canvas.drawCircle(7*minwidth, 5*minwidth, useWidth/16, mPaint);        canvas.save();        canvas.translate(ball.getX(), ball.getY());        ball.getShape().draw(canvas);        canvas.restore();        canvas.save();        canvas.translate(ball1.getX(), ball1.getY());        ball1.getShape().draw(canvas);        canvas.restore();    }    private ShapeHolder createBall(float x, float y, int color) {             OvalShape circle = new OvalShape();        circle.resize(useWidth / 8f, useWidth / 8f);        ShapeDrawable drawable = new ShapeDrawable(circle);        ShapeHolder shapeHolder = new ShapeHolder(drawable);        shapeHolder.setX(x - useWidth / 16f);        shapeHolder.setY(y - useWidth / 16f);        Paint paint = drawable.getPaint();        paint.setColor(color);        RadialGradient gradient = new RadialGradient(useWidth / 16f, useWidth / 16f,                useWidth / 8f, color, Color.GRAY, Shader.TileMode.CLAMP);        paint.setShader(gradient);        shapeHolder.setPaint(paint);        return shapeHolder;    }    private void createAnimation() {             if (bounceAnim == null) {                 XYHolder lstartXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            XYHolder processXY = new XYHolder(7 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            XYHolder lendXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            bounceAnim = ObjectAnimator.ofObject(ballHolder, "xY",                    new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);            bounceAnim.setDuration(animaltime);            bounceAnim.setRepeatCount(ObjectAnimator.INFINITE);            bounceAnim.setRepeatMode(ObjectAnimator.RESTART);            bounceAnim.addUpdateListener(this);        }        if (bounceAnim1 == null) {                 XYHolder lstartXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            XYHolder processXY = new XYHolder(3 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);            XYHolder lendXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);            bounceAnim1 = ObjectAnimator.ofObject(ballHolder1, "xY",                    new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);            bounceAnim1.setDuration(animaltime);            bounceAnim1.setRepeatCount(ObjectAnimator.INFINITE);            bounceAnim1.setRepeatMode(ObjectAnimator.RESTART);            bounceAnim1.addUpdateListener(this);        }    }    public void startAnimation() {             createAnimation();        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.play(bounceAnim).with(bounceAnim1);        animatorSet.start();    }    @Override    public void onAnimationUpdate(ValueAnimator animation) {             invalidate();    }}

QiziXYHolder.java

public class QiziXYHolder {         private ShapeHolder mBall;    public QiziXYHolder(ShapeHolder ball) {             mBall = ball;    }    public void setXY(XYHolder xyHolder) {             mBall.setX(xyHolder.getX());        mBall.setY(xyHolder.getY());    }    public XYHolder getXY() {             return new XYHolder(mBall.getX(), mBall.getY());    }}

ShapeHolder.java

public class ShapeHolder {         private float x = 0, y = 0;    private ShapeDrawable shape;    private int color;    private RadialGradient gradient;    private float alpha = 1f;    private Paint paint;    public void setPaint(Paint value) {             paint = value;    }    public Paint getPaint() {             return paint;    }    public void setX(float value) {             x = value;    }    public float getX() {             return x;    }    public void setY(float value) {             y = value;    }    public float getY() {             return y;    }    public void setShape(ShapeDrawable value) {             shape = value;    }    public ShapeDrawable getShape() {             return shape;    }    public int getColor() {             return color;    }    public void setColor(int value) {             shape.getPaint().setColor(value);        color = value;    }    public void setGradient(RadialGradient value) {             gradient = value;    }    public RadialGradient getGradient() {             return gradient;    }    public void setAlpha(float alpha) {             this.alpha = alpha;        shape.setAlpha((int)((alpha * 255f) + .5f));    }    public float getWidth() {             return shape.getShape().getWidth();    }    public void setWidth(float width) {             Shape s = shape.getShape();        s.resize(width, s.getHeight());    }    public float getHeight() {             return shape.getShape().getHeight();    }    public void setHeight(float height) {             Shape s = shape.getShape();        s.resize(s.getWidth(), height);    }    public ShapeHolder(ShapeDrawable s) {             shape = s;    }}

XYEvaluator.java

public class XYEvaluator implements TypeEvaluator {         public Object evaluate(float fraction, Object startValue, Object endValue) {             XYHolder startXY = (XYHolder) startValue;        XYHolder endXY = (XYHolder) endValue;        return new XYHolder(startXY.getX() + fraction * (endXY.getX() - startXY.getX()),                startXY.getY() + fraction * (endXY.getY() - startXY.getY()));    }}

XYHolder.java

public class XYHolder {         private float mX;    private float mY;    public XYHolder(float x, float y) {             mX = x;        mY = y;    }    public float getX() {             return mX;    }    public void setX(float x) {             mX = x;    }    public float getY() {             return mY;    }    public void setY(float y) {             mY = y;    }}

attrs.xml

<resources>    <declare-styleable name="WeiqiView"><!--        黑子颜色-->        <attr name="leftscolor" format="reference|color"/><!--        白子颜色-->        <attr name="rightscolor" format="reference|color"/><!--        棋盘颜色-->        <attr name="qipancolor" format="reference|color"/><!--        动画时间-->        <attr name="animalstime" format="integer"/>    </declare-styleable></resources>

布局调用

<com.shenzhen.jimeng.lookui.UI.WeiqiView    android:layout_centerInParent="true"    android:id="@+id/weiqi"    android:layout_width="400dp"    android:layout_height="400dp"/>

activity文件中开启动画

     weiqi = (WeiqiView) findViewById(R.id.weiqi);        weiqi.setOnClickListener(new View.OnClickListener() {                 @Override            public void onClick(View v) {                     weiqi.startAnimation();            }        });

总结

希望对您有所帮助,欢迎留言。如有问题可联系计蒙。

更多相关文章

  1. android隐藏View动画的注意事项
  2. Android实用视图动画及工具系列之三:表情加载动画和失败加载动画,
  3. Developers 设计之二 材料设计【翻译】
  4. 超酷的计步器APP(一)——炫酷功能实现,自定义水波纹特效、自定义炫
  5. Android等待动画
  6. Android仿QQ微信UI实现导航页和开场动画【附源码】
  7. 自定义Drawable实现灵动的红鲤鱼动画(上篇)
  8. 使用属性动画ObjectAnimator来实现控件动画
  9. Android实战项目——音乐播放器 由四大组件之一Service、使用Ser

随机推荐

  1. Android中ScrollView布局初始化显示的时
  2. ubuntu连接android设备(附最简单方法)
  3. How to destroy an Activity in android
  4. Android Non-UI to UI Thread Communicat
  5. Android 获得屏幕分辨率
  6. android之实现各个组件点击事件监听
  7. CMAKE 在Linux下 构建android 编译、打包
  8. 短信接收--Android彩信的接收流程(应用层)
  9. Process 'command 'C:\zssinstallsoft\
  10. android 主线程和子线程之间的消息传递