Android自定義view之圍棋動畫

這是我參與8月更文挑戰的第10天,活動詳情查看:8月更文挑戰java

Android自定義view之圍棋動畫

很久不見,最近粉絲要求上新一篇有點難度的自定義view文章,因此它來了!!android


乾貨文,建議收藏canvas

前言

廢話很少說直接開始api


文章最後有源碼markdown

完成效果圖

棋子加漸變色ide

在這裏插入圖片描述

棋子不加漸變色佈局

在這裏插入圖片描述

1、測量

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的倍數爲標準點。post

minwidth = useWidth / 10;
複製代碼

2、繪製背景(棋盤)

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);// 斜線
複製代碼

繪製完後,發現有點小瑕疵動畫

效果圖:ui

在這裏插入圖片描述

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();
            }
        });
複製代碼

總結

但願對您有所幫助,歡迎留言。若有問題可聯繫計蒙。

相關文章
相關標籤/搜索