Android使用Paint 和 Canvas 的相关知识,自定义 View 实现一系列效果动画

mValueAnimator.setRepeatCount(1);

​ //设置时长

mValueAnimator.setDuration(mRotateDuration);

​ //线性均匀改变

mValueAnimator.setInterpolator(new LinearInterpolator());

​ //用addUPdatelistener方法添加监听器,当值在【0,2pi】之间,监听器的方法被调用若干次

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentRotateAngle = (float)animation.getAnimatedValue();

invalidate();

}

调用ondraw进行重绘

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (mState == null){

mState = new RotateState();

}

mState.drawState(canvas);

}

完整的代码块

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (mState == null){

mState = new RotateState();

}

mState.drawState(canvas);

}

private abstract class SplashState{

abstract void drawState(Canvas canvas);

}

//旋转

private class RotateState extends SplashState{

private RotateState(){

mValueAnimator = ValueAnimator.ofFloat(0, (float)(Math.PI * 2));

mValueAnimator.setRepeatCount(1);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new LinearInterpolator());

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentRotateAngle = (float)animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

super.onAnimationEnd(animation);

mState = new MerginState();

}

});

mValueAnimator.start();

}

@Override

void drawState(Canvas canvas) {

//绘制背景

drawBackground(canvas);

//绘制6个小球

drawCircles(canvas);

}

}

3.4 扩散聚合效果实现

看成把4个小球看成“镶嵌”在中间大圆边上,扩散的效果不过是中间大圆半径变大了

改变这个半径R的值,来达到缩放功能

通过动态修改4个圆的绘制半径达到缩放效果

在这里插入图片描述

扩散聚合状态同样也继承上面的抽象类

将一个值从小圆半径平滑过渡到大圆,时长为mRotateDuration毫秒

mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);

mValueAnimator.setDuration(mRotateDuration);

​ //线性均匀改变

mValueAnimator.setInterpolator(new OvershootInterpolator(10f));

//动画选择差值器为OvershootInterpolato,它表示 向前甩一定值后再回到原来位置,然后在下面通过reverse方法来反向执行操作。

mValueAnimator.reverse();

完整代码:

private class MerginState extends SplashState{

private MerginState(){

mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new OvershootInterpolator(10f));

//监听动画结束,然后开启另一个动画

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentRotateRadius = (float) animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

super.onAnimationEnd(animation);

mState = new ExpandState();

}

});

mValueAnimator.reverse();

}

@Override

void drawState(Canvas canvas) {

drawBackground(canvas);

drawCircles(canvas);

}

}

5.水波纹

private void drawBackground(Canvas canvas) {

if (mCurrentHoleRadius > 0) {//绘制水波纹,水波纹效果通过空心圆实现

//绘制空心圆

float strokeWidth = mDistance - mCurrentHoleRadius;

float radius = strokeWidth / 2 + mCurrentHoleRadius;

mHolePaint.setStrokeWidth(strokeWidth);

canvas.drawCircle(mCenterX, mCenterY, radius, mHolePaint);

} else {//绘制其他动画的背景

canvas.drawColor(mBackGroundColor);

}

}

绘制水波纹扩散动画

原理:水波纹效果其实就是将这个圆的半径通过半径不断扩大,最后底部的图片就显示出来了!

R2=屏幕对角线长度/2

R1就是空心圆半径

画笔的宽度= R2 - R1

R2是固定大小,我们只需要动态修改R1的大小

在这里插入图片描述

private class ExpandState extends SplashState{

public ExpandState() {

mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mDistance);

//计算某个时刻当前的空心圆的半径是多少 r~0中的某个值

mValueAnimator.setRepeatCount(2);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new LinearInterpolator());

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

//当前的空心圆的半径是多少

mCurrentHoleRadius = (float) animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.start();

}

@Override

void drawState(Canvas canvas) {

drawBackground(canvas);

}

}

}

4 代码:


color.xml

#008577

#00574B

#D81B60

#02D1AC

#FFD200

#00C6FF

#FF3892

@color/aqua

@color/pink

@color/blue

@color/yellow

splashview.java

package com.saina.uitestdemo;

import android.animation.Animator;

import android.animation.AnimatorListenerAdapter;

import android.animation.ValueAnimator;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.util.AttributeSet;

import android.view.View;

import android.view.animation.LinearInterpolator;

import android.view.animation.OvershootInterpolator;

import androidx.annotation.Nullable;

/**

  • @author lucas

  • @package com.saina.uitestdemo

  • @fileName SplashView

  • @date on 2019/9/25 14:13

  • @describe 旋转 扩散聚合 水波纹动画

*/

public class SplashView extends View {

//定义小圆的画笔

Paint mPaint = new Paint();

//定义扩散大圆的画笔

Paint mHolePaint = new Paint();

//定义属性动画

ValueAnimator mValueAnimator = new ValueAnimator();

//背景色

private int mBackgroundColor = Color.WHITE;

private int[] mCircleColors;

//表示旋转圆的中心坐标

private float mCenterX;

private float mCenterY;

//表示斜对角线长度的一半,扩散圆最大半径

private float mDistance;

//6个小球的半径

private float mCircleRadius = 18;

//旋转大圆的半径

private float mRotateRadius = 90;

//当前大圆的旋转角度

private float mCurrentRotateAngle = 0F;

//当前大圆的半径

private float mCurrentRotateRadius = mRotateRadius;

//扩散圆的半径

private float mCurrentHoleRadius = 0F;

//表示旋转动画的时长

private int mRotateDuration = 1200;

private SplashState mState;

public SplashView(Context context) {

this(context, null);

}

public SplashView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init(context);

}

private void init(Context context) {

//画笔颜色

mPaint.setColor(Color.BLUE);

//抗锯齿标志

mPaint.setColor(getResources().getColor(R.color.colorAccent));

// 画笔透明度,先设置颜色,再设置透明度0-255

mPaint.setAlpha(200);

//抗锯齿标志

mHolePaint.setColor(getResources().getColor(R.color.colorAccent));

mHolePaint.setAlpha(200);

mPaint.setAntiAlias(true);

mHolePaint.setAntiAlias(true);

mHolePaint.setStyle(Paint.Style.STROKE);

mHolePaint.setColor(Color.DKGRAY);

mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

mCenterX = w * 1f / 2;

mCenterY = h * 1f / 2;

//将所提供的参数求平方和后开平方根的结果

mDistance = (float) (Math.hypot(w, h) / 2);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (mState == null){

mState = new RotateState();

}

mState.drawState(canvas);

}

private abstract class SplashState{

abstract void drawState(Canvas canvas);

}

//旋转

private class RotateState extends SplashState{

private RotateState(){

mValueAnimator = ValueAnimator.ofFloat(0, (float)(Math.PI * 2));

mValueAnimator.setRepeatCount(1);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new LinearInterpolator());

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentRotateAngle = (float)animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

super.onAnimationEnd(animation);

mState = new MerginState();

}

});

mValueAnimator.start();

}

@Override

void drawState(Canvas canvas) {

//绘制背景

drawBackground(canvas);

//绘制6个小球

drawCircles(canvas);

}

}

private void drawBackground(Canvas canvas) {

if (mCurrentHoleRadius > 0){

//绘制空心圆

float strokeWidth = mDistance - mCurrentHoleRadius;

float radius = strokeWidth / 2 + mCurrentHoleRadius;

mHolePaint.setStrokeWidth(strokeWidth);

canvas.drawCircle(mCenterX,mCenterY, radius, mHolePaint);

}else{

canvas.drawColor(mBackgroundColor);

}

}

private void drawCircles(Canvas canvas) {

float rotateAngle = (float) (Math.PI *2 / mCircleColors.length);

for (int i = 0; i < mCircleColors.length; i++) {

// x = r * cos(a) + centX;

// y = r * sin(a) + centY;

float angle = i * rotateAngle + mCurrentRotateAngle;

float cx = (float) (Math.cos(angle) * mCurrentRotateRadius + mCenterX);

float cy = (float) (Math.sin(angle) * mCurrentRotateRadius + mCenterY);

mPaint.setColor(mCircleColors[i]);

canvas.drawCircle(cx, cy, mCircleRadius, mPaint);

}

}

//扩散聚合

private class MerginState extends SplashState{

private MerginState(){

mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);

// mValueAnimator.setRepeatCount(2);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new OvershootInterpolator(10f));

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentRotateRadius = (float) animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

super.onAnimationEnd(animation);

mState = new ExpandState();

}

});

mValueAnimator.reverse();

}

@Override

void drawState(Canvas canvas) {

drawBackground(canvas);

drawCircles(canvas);

}

}

//3.水波纹

private class ExpandState extends SplashState{

public ExpandState() {

mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mDistance);

// mValueAnimator.setRepeatCount(2);

mValueAnimator.setDuration(mRotateDuration);

mValueAnimator.setInterpolator(new LinearInterpolator());

mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentHoleRadius = (float) animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.start();

}

@Override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

给大家送上我成功跳槽复习中所整理的资料,由于文章篇幅有限,所以只是把题目列出来了,我自己手头上整理的资料均和上面的答案可免费分享,需要这些资料和答案的朋友,可以点击这里免费领取

image

image

er() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

mCurrentHoleRadius = (float) animation.getAnimatedValue();

invalidate();

}

});

mValueAnimator.start();

}

@Override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-b8xWANvk-1710818163395)]
[外链图片转存中…(img-R5nt8qte-1710818163395)]
[外链图片转存中…(img-EQpugnuS-1710818163396)]
[外链图片转存中…(img-Cr927MWS-1710818163396)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-EYQMzbT6-1710818163397)]

最后

给大家送上我成功跳槽复习中所整理的资料,由于文章篇幅有限,所以只是把题目列出来了,我自己手头上整理的资料均和上面的答案可免费分享,需要这些资料和答案的朋友,可以点击这里免费领取

[外链图片转存中…(img-1VPzACtK-1710818163397)]

[外链图片转存中…(img-syePeNDz-1710818163397)]

image

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Android 自定义对号,可以使用自定义 View 实现打钩动画功能。以下是实现方法: 首先,创建一个类来实现自定义 View,这个类需要继承自 View,然后重写 onDraw 方法。在 onDraw 方法中,使用 Canvas 和 Path 对象来绘制对号的形状。 在绘制对号之前,需要先设置对号的起点和终点坐标,可以通过计算 View 的宽度和高度来确定这些坐标。然后,使用 Path 对象来创建对号的形状,具体方法如下: 1. 创建一个 Path 对象。 2. 使用 moveTo 方法将画笔移动到对号的起点。 3. 使用 lineTo 方法将画笔画出对号的一条线段。 4. 使用 moveTo 方法将画笔移动到对号的另一个起点。 5. 使用 lineTo 方法将画笔画出对号的另一条线段。 在绘制 Path 对象之后,可以使用 Paint 对象来设置对号的样式,例如颜色和宽度等。最后,在 onDraw 方法中调用 Canvas 的 drawPath 方法来将对号绘制出来。 另外,为了实现打钩动画,还需要使用 ValueAnimator 对象来控制 Path 的绘制过程。具体方法如下: 1. 创建一个 ValueAnimator 对象,并设置动画的起始值和结束值。 2. 在动画的监听器中,使用 ValueAnimator 的 getAnimatedValue 方法来获取当前动画的进度。 3. 根据当前进度,计算出对号的绘制进度,并使用 PathMeasure 对象来获取对应位置的 Path。 4. 在 onDraw 方法中,使用 Canvas 的 drawPath 方法来绘制当前的 Path。 最后,将自定义 View 添加到布局中即可实现自定义对号的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值