Android绘制动画线
Posted
技术标签:
【中文标题】Android绘制动画线【英文标题】:Android drawing an animated line 【发布时间】:2011-07-19 02:02:33 【问题描述】:我目前正在处理图形和路径,我可以成功显示我想要的任何内容。
但我不想直接在我的 SurfaceView 上画一条线,而是想在动画中逐步绘制它。
到目前为止,我所做的是创建一个路径,然后使用 PathMeasure 沿路径逐步检索坐标。到目前为止,这基本上是我所做的
PathMeasure pm = new PathMeasure(myPath, false);
float position = 0;
float end = pm.getLength();
float[] coord = 0,0,0,0,0,0,0,0,0;
while (position < end)
Matrix m = new Matrix();
// put the current path position coordinates into the matrix
pm.getMatrix(position, m, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
// put the matrix data into the coord array (coord[2] = x and coord[5] = y)
m.getValues(coord);
????
position += 1;
问号是我卡住的地方。我想逐步绘制路径并在屏幕上看到它的动画。我在互联网上找不到太多关于它的信息,所以如果你已经遇到过同样的情况,任何线索都会非常感激。我想要创建的最终效果就像铅笔画一样自动逐渐变成文本。
【问题讨论】:
没有答案!没有人使用过 Path 和 android 图形包? 嘿,佐助我遇到了同样的问题,你找到解决办法了吗??? 【参考方案1】:您必须将此视图添加到布局中,将高度设置为 1,将宽度设置为与父级匹配。这条线将从左到右进行动画处理。后面的行将放在第一行之上。
public class AnimatorLineView extends RelativeLayout
private View animatorLineView;
private View simpleLineView;
View animatorLine;
private int colorBeforeAnimation;
private int colorAfterAnimation;
private int colorForErrorLine;
public AnimatorLineView(Context context)
super(context);
init();
startAnimation();
public AnimatorLineView(Context context, AttributeSet attrs)
super(context, attrs);
init();
initAttributes(context, attrs);
setColors();
startAnimation();
public AnimatorLineView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
initAttributes(context, attrs);
setColors();
startAnimation();
private void setColors()
simpleLineView.setBackgroundColor(colorBeforeAnimation);
animatorLine.setBackgroundColor(colorAfterAnimation);
public void init()
animatorLineView = inflate(getContext(), R.layout.ainimator_line_view, this);
animatorLine = findViewById(R.id.simple_line);
simpleLineView = findViewById(R.id.animator_line);
public void setColor(int color)
animatorLine.setBackgroundColor(color);
public void startAnimation()
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
public void showErrorLine()
animatorLine.setBackgroundColor(colorForErrorLine);
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
public void hideErrorLine()
animatorLine.setBackgroundColor(colorAfterAnimation);
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
private void initAttributes(Context context, AttributeSet attributeSet)
TypedArray attr = getTypedArray(context, attributeSet, R.styleable.ProgressButton);
if (attr == null)
return;
try
colorBeforeAnimation = attr.getColor(R.styleable.AnimatorLineView_al_color_after_animation,ContextCompat.getColor(getContext(), R.color.animation_line_text_color));
colorAfterAnimation = attr.getColor(R.styleable.ProgressButton_pb_text_color, ContextCompat.getColor(getContext(), R.color.black_color));
colorForErrorLine = attr.getColor(R.styleable.ProgressButton_pb_text_color, ContextCompat.getColor(getContext(), R.color.error_msgs_text_color));
finally
attr.recycle();
protected TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr)
return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
public void resetColor()
animatorLine.setBackgroundColor(colorAfterAnimation);
animatorLine.setVisibility(View.GONE);
<animator_line_view>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="horizontal">
<View
android:id="@+id/simple_line"
android:layout_
android:layout_
android:background="#E0E0E0" />
<View
android:id="@+id/animator_line"
android:layout_
android:layout_
android:background="#000000"
android:visibility="gone" />
</FrameLayout>
<enter_animation_underline>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="@integer/animator_line_duration" />
</set>
---- styles------
<style name="animator_line">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="al_color_before_animation">#E0E0E0</item>
<item name="al_color_after_animation">#0000000</item>
<item name="al_error_line_color">#FF3352</item>
</style>
<declare-styleable name="AnimatorLineView">
<attr name="al_color_before_animation" format="color" />
<attr name="al_color_after_animation" format="color" />
<attr name="al_error_line_color" format="color" />
</declare-styleable>
-------- to be include in the xml
<com.careem.acma.widget.AnimatorLineView
android:id="@+id/animator_line"
style="@style/animator_line" />
【讨论】:
你能补充一点解释这是做什么的吗? 您必须将此视图添加到布局中,将高度设置为 1,宽度设置为与父级匹配。这条线将从左到右进行动画处理。后一行将放在第一行之上。【参考方案2】:这是一个对我有用的替代解决方案
package com.sample;
/**
* Created by Sumit
*/
public class PathView extends View
Paint mPaint;
Path mPath;
int mStrokeColor;
float mStrokeWidth;
float mProgress = 0.0f;
float mLength = 0f;
float mTotal;
public PathView(Context context)
this(context, null);
init();
public PathView(Context context, AttributeSet attrs)
this(context, attrs, 0);
init();
public PathView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
mStrokeColor = Color.RED;
mStrokeWidth = 8.0f;
init();
private void init()
mPaint = new Paint();
mPaint.setColor(mStrokeColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
setPath(new Path());
// setPath2(new Path());
public void setPath(Path p)
mPath = p;
PathMeasure measure = new PathMeasure(mPath, false);
mPathLength = measure.getLength();
public void setPath(List<float[][]> list)
Log.d("Path", "size " + list.size());
Path p = new Path();
p.moveTo(list.get(0)[0][0], list.get(1)[0][1]);
for (int i = 1; i < list.size(); i++)
p.lineTo(list.get(i)[0][0], list.get(i)[0][1]);
//if (i > 100)
//p.moveTo(list.get(i)[0][0], list.get(i)[0][1]);
//p.setFillType(FillType.WINDING);
setPath(p);
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
mTotal = (mLength - mLength * mProgress);
PathEffect pathEffect = new DashPathEffect(new float[] mLength, mLength , mTotal);
Log.d("Path Tag", "length =" + mLength + ", totla=" + mTotal);
mPaint.setPathEffect(pathEffect);
canvas.save();
// canvas.translate(getPaddingLeft(), getPaddingTop());
canvas.drawPath(mPath, mPaint);
canvas.restore();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(widthMeasureSpec);
int measuredWidth, measuredHeight;
if (widthMode == MeasureSpec.AT_MOST)
throw new IllegalStateException("Use MATCH_PARENT");
else
measuredWidth = widthSize;
if (heightMode == MeasureSpec.AT_MOST)
throw new IllegalStateException("Use MATCH_PARENT");
else
measuredHeight = heightSize;
setMeasuredDimension(measuredWidth, measuredHeight);
setPath();
void setPath()
int cX = getWidth() / 2;
int cY = getHeight() / 2;
cY += 50;
cX -= 50;
List<float[][]> list = new ArrayList<float[][]>();
for (int i = 0; i < 50; i++)
list.add(new float[][] cX--, cY++ );
for (int i = 0; i < 100; i++)
list.add(new float[][] cX--, cY-- );
for (int i = 0; i < 100; i++)
list.add(new float[][] cX++, cY-- );
for (int i = 0; i < 200; i++)
list.add(new float[][] cX++, cY++ );
for (int i = 0; i < 100; i++)
list.add(new float[][] cX++, cY-- );
for (int i = 0; i < 100; i++)
list.add(new float[][] cX--, cY-- );
for (int i = 0; i < 100; i++)
list.add(new float[][] cX--, cY++ );
setPath(list);
并使用
final PathView pathView = (PathView) findViewById(R.id.path_view);
pathView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "percentage", 0.0f, 1.0f);
anim.setDuration(2000);
anim.setInterpolator(new LinearInterpolator());
anim.setRepeatCount(Animation.INFINITE);
anim.start();
);
【讨论】:
【参考方案3】:每次您想绘制更多路径时,您可以使用 ObjectAnimator 类回调到您的类的方法之一,而不是创建一个 for 循环。
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;
import android.util.Log;
public class PathView extends View
Path path;
Paint paint;
float length;
public PathView(Context context)
super(context);
public PathView(Context context, AttributeSet attrs)
super(context, attrs);
public PathView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
public void init()
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
path.moveTo(50, 50);
path.lineTo(50, 500);
path.lineTo(200, 500);
path.lineTo(200, 300);
path.lineTo(350, 300);
// Measure the path
PathMeasure measure = new PathMeasure(path, false);
length = measure.getLength();
float[] intervals = new float[]length, length;
ObjectAnimator animator = ObjectAnimator.ofFloat(PathView.this, "phase", 1.0f, 0.0f);
animator.setDuration(3000);
animator.start();
//is called by animtor object
public void setPhase(float phase)
Log.d("pathview","setPhase called with:" + String.valueOf(phase));
paint.setPathEffect(createPathEffect(length, phase, 0.0f));
invalidate();//will calll onDraw
private static PathEffect createPathEffect(float pathLength, float phase, float offset)
return new DashPathEffect(new float[] pathLength, pathLength ,
Math.max(phase * pathLength, offset));
@Override
public void onDraw(Canvas c)
super.onDraw(c);
c.drawPath(path, paint);
然后,只需调用 init() 来开始动画,就像这样(或者如果您希望它在视图膨胀后立即开始,请将 init() 调用放在构造函数中):
PathView path_view = (PathView) root_view.findViewById(R.id.path);
path_view.init();
另请参阅此问题 here 和 this 示例,这是我的代码的基础。
【讨论】:
嘿!你的代码对我很有用。不过,我想为我的用例添加一个。我需要在当前路径的起点和终点画圆,直径大于笔划宽度,这意味着笔划帽不起作用。起始圆保持在其位置,但结束圆沿路径移动。我有起始坐标,所以我可以调用 canvas.drawCircle。但我不知道如何获得当前路径末端的坐标来绘制圆。有什么想法吗? @skunkwerk,感谢您的回答。当我尝试从 Activity 调用 init 时,应用程序崩溃,因为它说路径在 onDraw 中为空。另外,我将如何在这个答案中设置动画监听器? 我希望有一种方法可以避免不断调用“新”来创建路径效果。就像可能是一个路径效果池。它减慢了动画的速度,我想知道它是否是由于“新”关键字造成的。 我无法理解:你在哪里使用间隔?你为什么要编码???? 非常感谢,我花了一点时间来弄清楚它是如何工作的,作为关于 DashPathEffect 的一点颜色:这为我们提供了一种提供偏移量的简洁方法,以便我们可以开始小(大偏移)并在“打开”破折号长度上工作,使其越来越长,从而绘制越来越多的实际路径。【参考方案4】:我刚刚解决了这个问题,我在做什么:
private float[] mIntervals = 0f, 0f ;
private float drawSpeed = 2f;
private int currentPath = -1;
private PathMeasure mPathMeasure = new PathMeasure();
private ArrayList<Path> mListPath = new ArrayList<Path>(this.pathCount);
@Override
protected void onDraw(Canvas canvas)
if (mIntervals[1] <= 0f && currentPath < (pathCount - 1))
// Set the current path to draw
// getPath(int num) a function to return a path.
Path newPath = this.getPath(mListPath.size());
this.mListPath.add(newPath);
this.mPathMeasure.setPath(newPath, false);
mIntervals[0] = 0;
mIntervals[1] = this.mPathMeasure.getLength();
if (mIntervals[1] > 0)
// draw the previous path
int last = this.mListPath.size();
for (int i = 0; i < last; i++)
canvas.drawPath(this.mListPath.get(i), mPaint);
// partially draw the last path
this.mPaint.setPathEffect(new DashPathEffect(mIntervals, 0f));
canvas.drawPath(this.mListPath.get(last), mPaint);
// Update the path effects values, to draw a little more
// on the path.
mIntervals[0] += drawSpeed;
mIntervals[1] -= drawSpeed;
super.invalidate();
else
// The drawing have been done, draw it entirely
for (int i = 0; i < this.mListPath.size(); i++)
canvas.drawPath(this.mListPath.get(i), mPaint);
这个例子是对我所做的(为了简化例子)的改编。希望你能理解。由于我刚刚使这个函数工作,它缺乏优化和类似的东西。
希望它会有所帮助;-)
【讨论】:
您好,感谢您的精彩帖子。那真正的帮助我。但还有一个问题是如何定义路径数组。我尽力解决,但我不能。你能帮忙吗...!以上是关于Android绘制动画线的主要内容,如果未能解决你的问题,请参考以下文章