*
* setSubpixelText(boolean subpixelText);
* 设置该项为true,将有助于文本在LCD屏幕上的显示效果
*
* setTextAlign(Paint.Align align);
* 设置绘制文字的对齐方向
*
* setTextScaleX(float scaleX);
* 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
*
* setTextSize(float textSize);
* 设置绘制文字的字号大小
*
* setTextSkewX(float skewX);
* 设置斜体文字,skewX为倾斜弧度
*
* setTypeface(Typeface typeface);
* 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
*
* setUnderlineText(boolean underlineText);
* 设置带有下划线的文字效果
*
* setStrikeThruText(boolean strikeThruText);
* 设置带有删除线的效果
剩余的有些方法在这次的验证中暂时没用到 具体可以参考 PS:谢谢大佬的总结嗷
canvas又是啥呢?
Canvas我们可以简单理解为画布或是ps里面的图层,是绘制图形的直接对象,控制着图形的形状,比如矩形、圆形等。我们在自定义View时,通过调用Canvas的API来绘制具体的图形。
2.1 Canvas的常用操作速查表
| 操作类型 | 相关API | 备注 |
| :-- | :-- | :-- |
| 绘制颜色 | drawColor, drawRGB, drawARGB | 使用单一颜色填充整个画布 |
| 绘制基本形状 | drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc | 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 |
| 绘制图片 | drawBitmap, drawPicture | 绘制位图和图片 |
| 绘制文本 | drawText, drawPosText, drawTextOnPath | 依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字 |
| 绘制路径 | drawPath | 绘制路径,绘制贝塞尔曲线时也需要用到该函数 |
| 顶点操作 | drawVertices, drawBitmapMesh | 通过对顶点操作可以使图像形变,drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用 |
| 画布剪裁 | clipPath, clipRect | 设置画布的显示区域 |
| 画布快照 | save, restore, saveLayerXxx, restoreToCount, getSaveCount | 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 |
| 画布变换 | translate, scale, rotate, skew | 依次为 位移、缩放、 旋转、倾斜 |
| Matrix(矩阵) | getMatrix, setMatrix, concat | 实际上画布的位移,缩放等操作的都是图像矩阵Matrix, 只不过Matrix比较难以理解和使用,故封装了一些常用的方法。 |
-
canvas.drawArc ();画扇形
-
canvas.drawCircle();画圆
-
canvas.drawOval();画椭圆
-
canvas.drawLine();画线
-
canvas.drawPoint();画点
-
canvas.drawRect();画矩形
-
canvas.drawRoundRect();画圆角矩形
-
canvas.drawVertices();画顶点
-
canvas.drawPath();画路径
-
canvas.drawBitmap();画位图
-
canvas.drawText() ;画文字
2.2 Canvas的四大方法
- 保存画布
canvas.save()
作用是将之前的所有已经绘制的图像保存起来,让后续的操作就好像在一个新的图层上操作一样
- 合并画布
canvas.restore()
可以理解为PS中的合并图层操作。作用是在save()之后绘制的所有图像和save()之前的图像进行合并。
- 平移画布
canvas.translate(float dx, float dy)
默认的绘图原点在(0,0),调用translate(x,y)后,则将原点移动到了(x,y)。之后的所有绘图操作都将以(x,y)为原点执行。
- 旋转画布
canvas.rotate()
2.属性动画 常用方法 与 插值器 Interpolator
2.1常用方法
1. setDuration(int duration) 设置动画时长
2. setInterpolator(Interpolator interpolator) 设置 Interpolator
如图所示
2.2插值器 Interpolator 大全
再简单介绍一下每一个 Interpolator。
官方Interpolator介绍:
除了最后一个是自定义Interpolator外,其他都是系统自带的。下面主要介绍下效果就好了:
AccelerateDecelerateInterpolator:先加速、再减速,默认的插值器
LinearInterpolator:线性插值器
AccelerateInterpolator:加速
DecelerateInterpolator:减速
AnticipateInterpolator:开始时先反向
BounceInterpolator:达到最终位置会先反弹,类似弹弹球着地的效果
OvershootInterpolator:达到最终位置会超出一些
AnticipateOvershootInterpolator:AnticipateInterpolator与OvershootInterpolator的结合
CycleInterpolator:正弦效果,可以指定回荡的次数
PathInterpolator:根据指定的path进行运动,可以实现贝塞尔曲线
OvershootInterpolator 动画会超过目标值一些,然后再弹回来。(具体实现中就是用到了这种:扩散聚合时)
LinearInterpolator 匀速(具体实现中就是用到了这种:旋转时)
还有很多种的形式,具体就不一一列出来了。
可参照 : Interpolator
3.1先定义画笔和球半径旋转角度等
//定义小圆的画笔
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;
3.2 绘制小圆
一共有四种颜色,对应四个小球。
相关代码实现
//这边就用到了上述的drawCircles画圆
private void drawCircles(Canvas canvas) {
//每个小圆得角度 2π/小圆的个数
float rotateAngle = (float) (Math.PI *2 / mCircleColors.length);
for (int i = 0; i < mCircleColors.length; i++) {
//每个小圆i*间隔角度 + 旋转的角度 = 当前小圆的真是角度
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 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.WHITE);
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);
}
完成这一步后,4个小球就已经被绘制到屏幕上了。
接下来,使用属性动画(ValueAnimator)让4个小球旋转:
3.3 旋转
-
每个圆得角度= 2π/小圆的个数
-
每个圆得x坐标 = 圆半径*cos(a) +圆心X坐标
-
每个圆得y坐标 = 圆半径*sin(a) +圆心Y坐标
-
每个小圆i*间隔角度 + 旋转的角度 = 当前小圆的真是角度
先新建一个抽象状态类,旋转状态继承这个类
private abstract class SplashState{
abstract void drawState(Canvas canvas);
}
旋转效果实现:
关键的代码:
// 初始化属性动画对象,它表示的意思很简单,在规定的时间内值从0到2π,因此给定一个持续时间:
mValueAnimator = ValueAnimator.ofFloat(0, (float)(Math.PI * 2));
//简单理解为设置重复的次数。
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);
}
}
}
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
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)
文末
当你打算跳槽的时候,应该把“跳槽成功后,我能学到什么东西?对我的未来发展有什么好处”放在第一位。这些东西才是真正引导你的关键。在跳槽之前尽量“物尽其用”,把手头上的工作做好,最好是完成了某个项目或是得到提升之后再走。跳槽不是目的,而是为了达到最终职业目标的手段
最后祝大家工作升职加薪,面试拿到心仪Offer
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
(){
mValueAnimator = ValueAnimator.ofFloat(0, (float)(Math.PI * 2));
mValueAnimator.setRepeatCount(1);
mValueAnimator.setDuration(mRotateDuration);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-8JGtOY0A-1711592986570)]
[外链图片转存中…(img-KBbuhNrH-1711592986571)]
[外链图片转存中…(img-8JBFZmpq-1711592986571)]
[外链图片转存中…(img-uxczTNPm-1711592986571)]
[外链图片转存中…(img-FMWFZi0h-1711592986572)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)
文末
当你打算跳槽的时候,应该把“跳槽成功后,我能学到什么东西?对我的未来发展有什么好处”放在第一位。这些东西才是真正引导你的关键。在跳槽之前尽量“物尽其用”,把手头上的工作做好,最好是完成了某个项目或是得到提升之后再走。跳槽不是目的,而是为了达到最终职业目标的手段
最后祝大家工作升职加薪,面试拿到心仪Offer
[外链图片转存中…(img-hAgHTgdR-1711592986572)]
[外链图片转存中…(img-WdDZelbA-1711592986572)]