Android自定义view之围棋动画

简介: 本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。

Android自定义view之围棋动画

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

干货文,建议收藏

前言

废话不多说直接开始

老规矩,文章最后有源码

完成效果图

棋子加渐变色
1.gif

棋子不加渐变色
2.gif

一、测量

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;
        }
    }
AI 代码解读

2.定义测量最小长度

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

        minwidth = useWidth / 10;
AI 代码解读

二、绘制背景(棋盘)

1.初始化画笔

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

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);// 斜线
AI 代码解读

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

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);
AI 代码解读

效果图:
4.jpg

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

位置比例
(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);
AI 代码解读

效果图:
5.jpg

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

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()));
    }
}
AI 代码解读

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;
    }
AI 代码解读

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);
        }
    }
AI 代码解读

5.两个动画的同步执行

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(bounceAnim).with(bounceAnim1);
        animatorSet.start();
AI 代码解读

6.效果图

6.gif

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

7.解决第6步问题

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

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

效果图:
7.gif

五.自定义属性

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>
AI 代码解读

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

    }
AI 代码解读

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

8.gif

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

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

八.源码

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();
    }
}
AI 代码解读

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());
    }
}
AI 代码解读

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;
    }
}
AI 代码解读

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()));
    }
}
AI 代码解读

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;
    }
}
AI 代码解读

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>
AI 代码解读

布局调用

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

activity文件中开启动画

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

总结

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

相关文章
Android利用SVG实现动画效果
本文介绍了如何在Android中利用SVG实现动画效果。首先通过定义`pathData`参数(如M、L、Z等)绘制一个简单的三角形SVG图形,然后借助`objectAnimator`实现动态的线条绘制动画。文章详细讲解了从配置`build.gradle`支持VectorDrawable,到创建动画文件、关联SVG与动画,最后在Activity中启动动画的完整流程。此外,还提供了SVG绘制原理及工具推荐,帮助开发者更好地理解和应用SVG动画技术。
140 30
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
153 65
Android自定义view之网易云推荐歌单界面
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
410 84
Android SVG动画详细例子
本文详细讲解了在Android中利用SVG实现动画效果的方法,通过具体例子帮助开发者更好地理解和应用SVG动画。文章首先展示了动画的实现效果,接着回顾了之前的文章链接及常见问题(如属性名大小写错误)。核心内容包括:1) 使用阿里图库获取SVG图形;2) 借助工具将SVG转换为VectorDrawable;3) 为每个路径添加动画绑定属性;4) 创建动画文件并关联SVG;5) 在ImageView中引用动画文件;6) 在Activity中启动动画。文末还提供了完整的代码示例和源码下载链接,方便读者实践操作。
214 65
Android线条等待动画JMWorkProgress(可添加依赖直接使用)
这是一篇关于Android线条等待动画JMWorkProgress的教程文章,作者计蒙将其代码开源至GitHub,提升可读性。文章介绍了如何通过添加依赖库使用该动画,并详细讲解了XML与Java中的配置方法,包括改变线条颜色、宽度、添加文字等自定义属性。项目已支持直接依赖集成(`implementation &#39;com.github.Yufseven:JMWorkProgress:v1.0&#39;`),开发者可以快速上手实现炫酷的等待动画效果。文末附有GitHub项目地址,欢迎访问并点赞支持!
77 26
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
306 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
Android自定义view之围棋动画(化繁为简)
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
332 2
基于Android P,自定义Android开机动画的方法

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问