Android自定义view之围棋动画

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

废话不多说直接开始,老规矩,文章最后有源码

图片

完成效果图

1.棋子加渐变色

图片

2.棋子不加渐变色

图片

下面开始正文部分

01测量

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;

02

元旦绘制背景(棋盘)

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

效果图:

图片

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

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

效果图:

图片

04为动画开始做准备以及动画

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

效果图:

图片

05自定义属性

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

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

图片

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

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

图片

08源码

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://eyangzhen.com/221920.html

联系我们
联系我们
分享本页
返回顶部