我正在使用图形和路径,我可以成功地显示我想要的任何内容。
但是我不想直接在SurfaceView上画一条线,而是想在动画中逐步绘制它。
到目前为止,我所做的是创建一个Path,然后使用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;
}
问号是我被困住的地方。我想逐步绘制路径,并在屏幕上看到它的动画。我在互联网上找不到关于它的很多信息,所以如果你已经遇到过同样的情况,任何线索都会非常感激。我想要创建的最终效果就像铅笔自动逐渐绘制文本一样。
答案 0 :(得分:13)
除了创建for循环之外,每次你想要绘制更多的路径时,都可以使用ObjectAnimator类来回调你的一个类的方法。
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();
答案 1 :(得分:5)
我刚刚解决了这个问题,我在这里做了:
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);
}
}
}
这个例子,是对我所做的改编(简化例子)。希望你能理解它。由于我刚刚使这个功能正常工作,它缺乏优化和类似的东西。
希望它会有所帮助; - )
答案 2 :(得分:1)
您必须将此视图添加到布局中,将height设置为1,将width设置为与parent匹配。该线将从左到右动画。后一行将放在第一行。
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_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View
android:id="@+id/simple_line"
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:background="#E0E0E0" />
<View
android:id="@+id/animator_line"
android:layout_width="match_parent"
android:layout_height="1.5dp"
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" />
答案 3 :(得分:0)
这是一个适合我的替代解决方案
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();
}
});