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绘制动画线的主要内容,如果未能解决你的问题,请参考以下文章

Android中使用SurfaceView和Canvas来绘制动画

在android中的视图下绘制线

android Animation 动画绘制逻辑

Android OpenCV 绘制霍夫线

Android开发-根据起点终点实现直线逐点绘制动画-02

如何使用正确的配置在 android.graphics.Path 中绘制箭头线