Android自定義view之圍棋動(dòng)畫效果的實(shí)現(xiàn)
前言
廢話不多說直接開始
老規(guī)矩,文章最后有源碼
完成效果圖
棋子加漸變色

棋子不加漸變色

一、測(cè)量
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.定義測(cè)量最小長(zhǎng)度
將布局分為10份。以minwidth的1,3,5,7,9的倍數(shù)為標(biāo)準(zhǔn)點(diǎn)。
minwidth = useWidth / 10;
二、繪制背景(棋盤)
1.初始化畫筆
mPaint = new Paint(); //創(chuàng)建畫筆對(duì)象 mPaint.setColor(Color.BLACK); //設(shè)置畫筆顏色 mPaint.setStyle(Paint.Style.FILL); //設(shè)置畫筆模式為填充 mPaint.setStrokeWidth(4f); //設(shè)置畫筆寬度為10px mPaint.setAntiAlias(true); //設(shè)置抗鋸齒 mPaint.setAlpha(255); //設(shè)置畫筆透明度
2.畫棋盤
//細(xì)的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);// 斜線 //細(xì)的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);// 斜線
繪制完后,發(fā)現(xiàn)有點(diǎn)小瑕疵
效果圖:

3.補(bǔ)棋盤瑕疵
canvas.drawPoint(minwidth, minwidth, mPaint); canvas.drawPoint(9 * minwidth, minwidth, mPaint); canvas.drawPoint(minwidth, 9 * minwidth, mPaint); canvas.drawPoint(9 * minwidth, 9 * minwidth, mPaint);
效果圖:

三.畫個(gè)不可改變的棋子(以便于了解動(dòng)畫移動(dòng)位置)
位置比例
(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);
效果圖:

四.為動(dòng)畫開始做準(zhǔn)備以及動(dòng)畫
1.三個(gè)輔助類為動(dòng)畫做準(zhǔn)備(參數(shù)模仿Android官方Demo)
主要為get set構(gòu)造,代碼會(huì)貼到最后
2.自定義該接口實(shí)例來控制動(dòng)畫的更新計(jì)算表達(dá)式
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.棋子的創(chuàng)建
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.動(dòng)畫的創(chuàng)建
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.兩個(gè)動(dòng)畫的同步執(zhí)行
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bounceAnim).with(bounceAnim1); animatorSet.start();
6.效果圖

視覺效果:感覺白子不太明顯
7.解決第6步問題
在棋子的創(chuàng)建方法中添加漸變色
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"/> <!-- 動(dòng)畫時(shí)間--> <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);
//獲取動(dòng)畫時(shí)間
animaltime = ta.getInt(R.styleable.WeiqiView_animalstime, 2000);
//回收
ta.recycle();
}
六.自定義屬性設(shè)置后運(yùn)行效果

七.小改變,視覺效果就不一樣了!
然后,把背景注釋,像不像那些等待動(dòng)畫?

八.源碼
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;
//畫一個(gè)圓(棋子)
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(); //創(chuàng)建畫筆對(duì)象
mPaint.setColor(Color.BLACK); //設(shè)置畫筆顏色
mPaint.setStyle(Paint.Style.FILL); //設(shè)置畫筆模式為填充
mPaint.setStrokeWidth(4f); //設(shè)置畫筆寬度為10px
mPaint.setAntiAlias(true); //設(shè)置抗鋸齒
mPaint.setAlpha(255); //設(shè)置畫筆透明度
}
@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ì)的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);// 斜線
//細(xì)的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);// 斜線
//補(bǔ)瑕疵
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"/> <!-- 動(dòng)畫時(shí)間--> <attr name="animalstime" format="integer"/> </declare-styleable> </resources>
布局調(diào)用
<com.shenzhen.jimeng.lookui.UI.WeiqiView android:layout_centerInParent="true" android:id="@+id/weiqi" android:layout_width="400dp" android:layout_height="400dp"/>
activity文件中開啟動(dòng)畫
weiqi = (WeiqiView) findViewById(R.id.weiqi);
weiqi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
weiqi.startAnimation();
}
});
到此這篇關(guān)于Android自定義view之圍棋動(dòng)畫效果的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Android自定義view圍棋動(dòng)畫內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android實(shí)現(xiàn)五子棋游戲(局域網(wǎng)版)
- android自定義View實(shí)現(xiàn)簡(jiǎn)單五子棋游戲
- 基于android實(shí)現(xiàn)五子棋開發(fā)
- Android自定義View實(shí)現(xiàn)五子棋游戲
- Android自定義View實(shí)現(xiàn)五子棋小游戲
- android簡(jiǎn)單自定義View實(shí)現(xiàn)五子棋
- Android自定義View實(shí)現(xiàn)五子棋游戲
- Android開發(fā)實(shí)現(xiàn)的簡(jiǎn)單五子棋游戲示例
- Android游戲開發(fā)之黑白棋
- Android實(shí)現(xiàn)中國(guó)象棋游戲(局域網(wǎng)版)
相關(guān)文章
OpenGL Shader實(shí)例分析(2)繪制心臟跳動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了OpenGL Shader實(shí)例分析第2篇,繪制心臟跳動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
Kotlin Option與Either及Result實(shí)現(xiàn)異常處理詳解
Kotlin異常處理,異常是在程序運(yùn)行時(shí)可能發(fā)生的不必要的問題,并突然終止您的程序。異常處理是一個(gè)過程,使用它可以防止程序出現(xiàn)可能破壞我們代碼的異常2022-12-12
Android設(shè)備adb連接后顯示device unauthorized解決方案
這篇文章主要為大家介紹了Android設(shè)備adb連接后顯示device unauthorized解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Android自定義view實(shí)現(xiàn)動(dòng)態(tài)柱狀圖
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)動(dòng)態(tài)柱狀圖的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Kotlin超簡(jiǎn)單實(shí)現(xiàn)StepView的方法
這篇文章主要介紹了Kotlin超簡(jiǎn)單實(shí)現(xiàn)StepView的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11

