场景:

存在较多绘制内容的区域需要某些动画效果,

需要尽量少修改视图的绘制方法,做到动画与绘制分离。

看个简单例子:

image

我在一个视图上绘制了一行文字,先看一下绘制部分的代码:

public class MyLayout extends LinearLayout

{

private String mText = "show me the money";

//……

@Override

protected void dispatchDraw(Canvas canvas)

{

super.dispatchDraw(canvas);

doPaint(canvas, null);

}

public void doPaint(Canvas canvas, TextAnimationController.AnimationView aniView)

{

Paint paint = new Paint();

paint.setColor(Color.RED);

paint.setTextSize(80);

paint.setStyle(Paint.Style.FILL);

//切分字符串

String[] words = mText.split("\\s+");

//起始位置

int x_start = 100;

int y_start = 400;

for (String subText : words)

{

//绘制文本

canvas.drawText(subText, x_start, y_start, paint);

float width = paint.measureText(subText);

float height = paint.descent() - paint.ascent();

x_start += width;

//绘制空格

String blank = " ";

canvas.drawText(blank, x_start, y_start, paint);

x_start += paint.measureText(blank);

}

}

}

现在需要做删除最后一个单词的动作并为之添加动画。

一个实现方法:

将属性动画结合到自己的视图(View)中,通过数值发生器(ValueAnimator)和估值器(TypeEvaluator)不断产生出的位置信息,修改单词绘制时的位置和大小,进行重新绘制。

这种做法会产生两个问题:

动画和绘制结合得太紧密,绘制的逻辑和动画的逻辑搅和在一起,导致代码复杂且混乱

频繁的直接绘制会导致屏幕闪烁。(当然,这个可以用双缓冲解决)

解决思路:

在执行动画时,可以在视图上再盖一层PopupWindow。强制调用原视图的绘制方法,让其在PopupWindow视图的canvas上重新绘制一遍文本,来参与动画的显示(双缓冲):

image

对于动画的绘制思路可参考下图来理解:

image

说明:我们先把最终的状态的Bitmap对象绘制到动画视图(PopupWindow的视图)上,然后再把原始状态Bitmap上的动画字符区域经过缩放绘制到动画数值发生器给出的具体位置上

为了实现动画,我们需要一些数据的支持:

动画文本在默认绘制时的坐标位置及宽(原始位置)

通过数值发生器(ValueAnimator)和估值器(TypeEvaluator)不断产生出的动画文本的位置信息

首先,我们需要修改原始视图的绘制方法,添加文本位置的搜集器:

public void doPaint(Canvas canvas, TextAnimationController.AnimationView aniView)

{

……

for (String subText : words)

{

//绘制文本

canvas.drawText(subText, x_start, y_start, paint);

float width = paint.measureText(subText);

float height = paint.descent() - paint.ascent();

//==============================================================================================

if (aniView != null)

{

TextAnimationController.TextObject textObj = new TextAnimationController.TextObject();

textObj.rcRangeSrc = new RectF(x_start, y_start + paint.descent() - height, x_start + width,

y_start + paint.descent());

aniView.addTextObject(textObj);

}

//==============================================================================================

x_start += width;

//绘制空格

String blank = " ";

canvas.drawText(blank, x_start, y_start, paint);

x_start += paint.measureText(blank);

}

}

为了方便控制,我们创建一个动画控制器类:TextAnimationController,所有的数据导入、动画生成、弹层、控制都在这个类里。

public class TextAnimationController

{

private MyLayout mOriginalView = null; //PopupWindow的视图

private AnimationView mAnimationView = null;

private AnimationWindow mAnimationWindow = null;

public TextAnimationController(MyLayout view)

{

mOriginalView = view;

}

//开始动画

public void startAnimation(Point target)

{

//创建动画视图层

mAnimationView = new AnimationView(mOriginalView.getContext(), target);

mAnimationWindow = new AnimationWindow(mAnimationView, mOriginalView.getWidth(), mOriginalView.getHeight());

mAnimationWindow.setFocusable(true);

//加载动画视图层

int[] location = new int[2];

mOriginalView.getLocationOnScreen(location);

mAnimationWindow.showAtLocation(mOriginalView, Gravity.TOP | Gravity.LEFT, location[0], location[1]);

}

public void endAnimation()

{

//关闭动画层

if (mAnimationWindow != null && mAnimationWindow.isShowing())

{

mAnimationWindow.dismiss();

mAnimationWindow = null;

}

}

static class TextObject

{

//原始尺寸

RectF rcRangeSrc;

//目标尺寸

RectF rcRangeDes;

//中间步骤尺寸

RectF rcRangeStep;

}

//PopupWindow弹层

public class AnimationWindow extends PopupWindow

{

public AnimationWindow(View contentView, int width, int height)

{

super(contentView, width, height);

setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

}

}

//动画视图(PopupWindow的视图)

class AnimationView extends View

{

private LinkedList objectList = new LinkedList<>();

private Paint paint = new Paint();

private Rect rectSrc = new Rect();

private Bitmap bitmapSrc = Bitmap

.createBitmap(mOriginalView.getWidth(), mOriginalView.getHeight(), Bitmap.Config.ARGB_8888);

private Bitmap bitmapFinal = Bitmap

.createBitmap(mOriginalView.getWidth(), mOriginalView.getHeight(), Bitmap.Config.ARGB_8888);

public AnimationView(Context context, final Point target)

{

super(context);

Canvas bmpCanvas = new Canvas(bitmapSrc);

mOriginalView.showStartText();

mOriginalView.doPaint(bmpCanvas, this);

bmpCanvas = new Canvas(bitmapFinal);

mOriginalView.showEndText();

mOriginalView.doPaint(bmpCanvas, null);

post(new Runnable()

{

@Override

public void run()

{

startAnimation(target.x, target.y);

}

});

}

public void addTextObject(TextObject object)

{

objectList.add(object);

}

@Override

protected void onDraw(Canvas canvas)

{

super.onDraw(canvas);

//绘制底图

canvas.drawBitmap(bitmapFinal, 0, 0, paint);

//绘制动画文本图

int lastIndex = objectList.size() - 1;

RectF rcSrc = objectList.get(lastIndex).rcRangeSrc;

rectSrc.set((int) rcSrc.left, (int) rcSrc.top, (int) rcSrc.right, (int) rcSrc.bottom);

RectF rcStep = objectList.get(lastIndex).rcRangeStep;

if (rcStep != null)

{

canvas.drawBitmap(bitmapSrc, rectSrc, rcStep, paint);

}

}

private void startAnimation(int desX, int desY)

{

for (int i = 0; i < objectList.size(); i++)

{

RectF src = objectList.get(i).rcRangeSrc;

objectList.get(i).rcRangeDes = new RectF(desX, desY, desX + src.width(), desY + src.height());

objectList.get(i).rcRangeStep = new RectF(src);

}

//==========================================================================================================

ArrayList animators = new ArrayList<>();

for (int i = 0; i < objectList.size(); i++)

{

final RectF src = objectList.get(i).rcRangeSrc;

RectF des = objectList.get(i).rcRangeDes;

AnimatorSet aniDisappear = new AnimatorSet();

final int index = i;

//贝塞尔曲线动画

final int pointX = (int) (src.left + 200);

final int pointY = (int) (src.top + (des.top - src.top) / 2);

Point controlPoint = new Point(pointX, pointY); //控制点

BezierEvaluator bezierEvaluator = new BezierEvaluator(controlPoint);

ValueAnimator animBezier = ValueAnimator.ofObject(bezierEvaluator,

new Point((int)src.left, (int)src.top),

new Point((int)des.left, (int)des.top));

animBezier.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

Point point = (Point) animation.getAnimatedValue();

RectF step = new RectF(objectList.get(index).rcRangeStep);

step.set(point.x, point.y, point.x + src.width(), point.y + src.height());

objectList.get(index).rcRangeStep.set(step);

invalidate();

}

});

//缩放动画

ValueAnimator animScale = ValueAnimator.ofFloat(1.0f, 0.1f);

animScale.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()

{

@Override

public void onAnimationUpdate(ValueAnimator animation)

{

float scaleSize = (Float) animation.getAnimatedValue();

RectF step = new RectF(objectList.get(index).rcRangeStep);

step.set(step.left, step.top, step.left + step.width() * scaleSize,

step.top + step.height() * scaleSize);

objectList.get(index).rcRangeStep.set(step);

}

});

aniDisappear.setDuration(1200);

aniDisappear.setInterpolator(new AccelerateInterpolator());

aniDisappear.play(animBezier).with(animScale);

animators.add(aniDisappear);

}

AnimatorSet animatorSet = new AnimatorSet();

animatorSet.addListener(new Animator.AnimatorListener()

{

@Override

public void onAnimationStart(Animator animation)

{

}

@Override

public void onAnimationEnd(Animator animation)

{

endAnimation();

}

@Override

public void onAnimationCancel(Animator animation)

{

endAnimation();

}

@Override

public void onAnimationRepeat(Animator animation)

{

}

});

animatorSet.playTogether(animators);

animatorSet.start();

}

}

public class BezierEvaluator implements TypeEvaluator

{

private Point controlPoint;

public BezierEvaluator(Point controlPoint)

{

this.controlPoint = controlPoint;

}

@Override

public Point evaluate(float t, Point startValue, Point endValue)

{

int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * controlPoint.x + t * t * endValue.x);

int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * controlPoint.y + t * t * endValue.y);

return new Point(x, y);

}

}

}

ps:其中动画文本的运动轨迹使用了贝塞尔曲线,关于贝塞尔曲线的TypeEvaluator,参考了

https://www.jianshu.com/p/d9a3ae9e806d

android 字体 动画,android 对绘制的文本添加动画相关推荐

  1. android字体好看,Android:更好的自定义字体方案

    摘要: 在一个应用中,我需要在所有的UI组件中使用客户提供的字体.这听起来似乎是个很稀松平常的任务,不是吗?是的,我当时也是这么想的.然后我震惊了,Android竟然没有提供一个简单优雅的方式来做这件 ...

  2. android 字体百分比,android 解决百分比布局适配时Textview的字体Textsize比例缩放问题...

    在使用百分比布局的过程中,大家可能会遇到一个问题,Textview的控件大小是由百分比分数算出来的,但是字体大小Textsize却没法确定.于是我想到继承textview写一个自定义的PercentT ...

  3. android 字体编程,Android编程之Calligraphy:Android 自定义字体库

    Calligraphy是android 自定义字体库 添加依赖 Download from Maven Central (.jar) OR Java dependencies { compile 'u ...

  4. android字体行距,android textview设置字体的行距和字间距

    字间距 textView有一个属性android:textScaleX是调节字间距的,它的值是一个float型.查看源代码,默认textView 此属性是使用的是: android.internal. ...

  5. android字体ratingbar,Android 自定义View之自定义评分选择器RatingBar

    DtRatingBar 一个使用在android上的RatingBar.GitHub地址:DtRatingBar 使用依赖: implementation 'com.yetland.dtratingb ...

  6. android字体行距,android中怎么调整字体的间距和行间距

    在网页中都是很轻松的就可以调整间距的.在android中,我个人并没有去设置过. 下面就来说说android中的间距问题. 原文:http://blog.csdn.net/fancylovejava/ ...

  7. android 字体倒影,Android开发中怎么实现一个文字倒影效果

    Android开发中怎么实现一个文字倒影效果 发布时间:2020-11-25 17:18:19 来源:亿速云 阅读:140 作者:Leah 这期内容当中小编将会给大家带来有关Android开发中怎么实 ...

  8. android 桌面动画,Android 如何在Launcher的桌面滑动时添加动画效果? M

    正文 目前的Launcher桌面滑动时,是没有动画的.如何在Lancher的桌面滑动时添加动画效果?Demo: 请修改Workspace.java的screenScrolled方法,如下: @Over ...

  9. Android字体权重,android – 如何设置自定义字体权重?

    就像主题暗示的那样,我想以编程方式更改/设置任意字体的font weight. 编辑:我的意思是一般用于绘制字符串的字体. 谢谢你的任何信息. 解决方法: 据我所知,加载字体后无法修改.您可以从现有字 ...

最新文章

  1. Postfix无法正常发送邮件故障的排除
  2. NA实战视频学习笔记(第一课)
  3. MyBatis动态SQL小结
  4. python3安装scrapy问题解决
  5. GraphPad轻松绘制配对比较图和双向柱状图
  6. 设计模式(Design Pattern)
  7. ITK:过滤器Filter和ParallelizeImageRegion比较
  8. 光纤光缆市场需求高于预期 我国将迎来流量经济
  9. C# 8中的范围类型(Range Type)
  10. 世界上最好用的浏览器Chrome 10周岁生日,迎来一大波更新!
  11. 二分匹配和一般图匹配
  12. python 图形库有哪些_Python基本图形绘制库——turtle
  13. Tomcat 服务器状态监控显示PS Survivor Space 99%
  14. python操作Access .mdb数据库环境配置
  15. vb2010 java,连接用vb成功连接access2010
  16. 【科研论文】找到中文论文的英文引用格式
  17. 首都师范 博弈论 9 5 6引入精神奖励后的博弈模型
  18. 理解多线程(四)--原子量和原子操作
  19. 游戏测试成长之路02-测试用例
  20. 一元三次方程重根判别式_一元三次方程快速解法

热门文章

  1. 飞秋爱好者(WZ132)
  2. 今天的天气格外地好的dabeicun
  3. C# 定义了 7 种变量类别:静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量
  4. 企业即时通讯市场增长500%
  5. 怎样成为一名更优秀的程序员?我总结出 7 条建议,希望对你们有帮助!
  6. 双十一,没有买卖就没有伤害!
  7. “嘿,我们又见面了!”
  8. 生信宝典之傻瓜式 (五) - 文献挖掘查找指定基因调控网络
  9. 使用 Finder预览功能,让你可以快速浏览多个文件
  10. OrionX Panel for Mac(ps摄影自动化工作流插件)支持ps2021 big sur11 汉化版