【问题标题】:Android drawing an animated lineAndroid绘制动画线
【发布时间】: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 图形包?
  • 嘿,佐助我遇到了同样的问题,你找到解决办法了吗???

标签: android graphics animation path canvas


【解决方案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_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" />

【讨论】:

  • 你能补充一点解释这是做什么的吗?
  • 您必须将此视图添加到布局中,将高度设置为 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();
    

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

    【讨论】:

    • 嘿!你的代码对我很有用。不过,我想为我的用例添加一个。我需要在当前路径的起点和终点画圆,直径大于笔划宽度,这意味着笔划帽不起作用。起始圆保持在其位置,但结束圆沿路径移动。我有起始坐标,所以我可以调用 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);
         }
      }
    }
    

    这个例子是对我所做的(为了简化例子)的改编。希望你能理解。由于我刚刚使这个函数工作,它缺乏优化和类似的东西。

    希望它会有所帮助;-)

    【讨论】:

    • 您好,感谢您的精彩帖子。那真正的帮助我。但还有一个问题是如何定义路径数组。我尽力解决,但我不能。你能帮忙吗...!
    猜你喜欢
    • 1970-01-01
    • 2013-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2020-05-25
    • 2014-07-24
    • 1970-01-01
    相关资源
    最近更新 更多