Android自定义view之围棋动画效果的实现

作者:计蒙不吃鱼 时间:2022-05-07 17:36:38 

前言

废话不多说直接开始

老规矩,文章最后有源码

完成效果图

棋子加渐变色

Android自定义view之围棋动画效果的实现

棋子不加渐变色

Android自定义view之围棋动画效果的实现

一、测量

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);// 斜线

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

Android自定义view之围棋动画效果的实现

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);

效果图:

Android自定义view之围棋动画效果的实现

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

位置比例
(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);

效果图:

Android自定义view之围棋动画效果的实现

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

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.效果图

Android自定义view之围棋动画效果的实现

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

7.解决第6步问题

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


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

效果图:
Android自定义view之围棋动画效果的实现

五.自定义属性

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();

}

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

Android自定义view之围棋动画效果的实现

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

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

Android自定义view之围棋动画效果的实现

八.源码

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();
  }
 });

来源:https://blog.csdn.net/qq_42761395/article/details/111961117

标签:Android,自定义view,围棋动画
0
投稿

猜你喜欢

  • Android 滑动监听的实例详解

    2023-10-28 14:32:32
  • 详解如何将JAR包发布到Maven中央仓库

    2023-02-14 07:26:00
  • C#中AS和IS关键字的用法

    2021-06-12 09:44:53
  • C# Winform实现圆角无锯齿按钮

    2023-09-16 02:26:29
  • java8使用filter()取出自己所需数据

    2023-04-07 02:30:37
  • C# Winfom 中ListBox的简单用法详解

    2023-03-14 20:38:47
  • Java日常练习题,每天进步一点点(58)

    2021-06-26 01:13:02
  • android判断手机是否安装地图应用实现跳转到该地图应用

    2022-11-30 11:01:45
  • Java异常--常见方法--自定义异常--增强try(try-with-resources)详解

    2021-10-31 07:17:42
  • Android中View的炸裂特效实现方法详解

    2022-07-25 17:59:28
  • Java数据结构之树和二叉树的相关资料

    2022-07-31 04:48:18
  • java实现二叉树的创建及5种遍历方法(总结)

    2022-03-14 09:00:28
  • springcloud整合openfeign使用实例详解

    2022-10-05 08:51:52
  • android实现拍照或从相册选取图片

    2022-08-08 06:55:36
  • C#利用时间和随即字符串创建唯一的订单编号

    2022-01-21 15:18:56
  • Flutter TV Android端开发技巧详细教程

    2021-09-16 20:32:18
  • SpringBoot如何通过devtools实现热部署

    2022-07-23 10:04:10
  • Android App支付系列(二):支付宝SDK接入详细指南(附官方支付demo)

    2022-03-14 01:12:28
  • 一文助你搞懂参数传递原理解析(java、go、python、c++)

    2022-04-04 13:10:15
  • 浅谈C# 构造方法(函数)

    2021-11-23 08:01:49
  • asp之家 软件编程 m.aspxhome.com