日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android 字体 动画,android 对绘制的文本添加动画

發(fā)布時間:2025/3/15 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 字体 动画,android 对绘制的文本添加动画 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

場景:

存在較多繪制內(nèi)容的區(qū)域需要某些動畫效果,

需要盡量少修改視圖的繪制方法,做到動畫與繪制分離。

看個簡單例子:

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

}

}

}

現(xiàn)在需要做刪除最后一個單詞的動作并為之添加動畫。

一個實現(xiàn)方法:

將屬性動畫結(jié)合到自己的視圖(View)中,通過數(shù)值發(fā)生器(ValueAnimator)和估值器(TypeEvaluator)不斷產(chǎn)生出的位置信息,修改單詞繪制時的位置和大小,進行重新繪制。

這種做法會產(chǎn)生兩個問題:

動畫和繪制結(jié)合得太緊密,繪制的邏輯和動畫的邏輯攪和在一起,導(dǎo)致代碼復(fù)雜且混亂

頻繁的直接繪制會導(dǎo)致屏幕閃爍。(當(dāng)然,這個可以用雙緩沖解決)

解決思路:

在執(zhí)行動畫時,可以在視圖上再蓋一層PopupWindow。強制調(diào)用原視圖的繪制方法,讓其在PopupWindow視圖的canvas上重新繪制一遍文本,來參與動畫的顯示(雙緩沖):

image

對于動畫的繪制思路可參考下圖來理解:

image

說明:我們先把最終的狀態(tài)的Bitmap對象繪制到動畫視圖(PopupWindow的視圖)上,然后再把原始狀態(tài)Bitmap上的動畫字符區(qū)域經(jīng)過縮放繪制到動畫數(shù)值發(fā)生器給出的具體位置上

為了實現(xiàn)動畫,我們需要一些數(shù)據(jù)的支持:

動畫文本在默認(rèn)繪制時的坐標(biāo)位置及寬(原始位置)

通過數(shù)值發(fā)生器(ValueAnimator)和估值器(TypeEvaluator)不斷產(chǎn)生出的動畫文本的位置信息

首先,我們需要修改原始視圖的繪制方法,添加文本位置的搜集器:

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

}

}

為了方便控制,我們創(chuàng)建一個動畫控制器類:TextAnimationController,所有的數(shù)據(jù)導(dǎo)入、動畫生成、彈層、控制都在這個類里。

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)

{

//創(chuàng)建動畫視圖層

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

{

//關(guān)閉動畫層

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

{

mAnimationWindow.dismiss();

mAnimationWindow = null;

}

}

static class TextObject

{

//原始尺寸

RectF rcRangeSrc;

//目標(biāo)尺寸

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:其中動畫文本的運動軌跡使用了貝塞爾曲線,關(guān)于貝塞爾曲線的TypeEvaluator,參考了

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

總結(jié)

以上是生活随笔為你收集整理的android 字体 动画,android 对绘制的文本添加动画的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。