Android绘制动画线

时间:2011-03-20 10:40:11

标签: android graphics animation path canvas

我正在使用图形和路径,我可以成功地显示我想要的任何内容。

但是我不想直接在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;

    }

问号是我被困住的地方。我想逐步绘制路径,并在屏幕上看到它的动画。我在互联网上找不到关于它的很多信息,所以如果你已经遇到过同样的情况,任何线索都会非常感激。我想要创建的最终效果就像铅笔自动逐渐绘制文本一样。

4 个答案:

答案 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();

另请参阅此问题herethis示例,我的代码基于此。

答案 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();
        }
    });
相关问题