Android drawing an animated line

前端 未结 4 507
时光说笑
时光说笑 2020-12-23 10:24

I\'m currently working with graphics and paths, and I can successufully display whatever I want.

But instead of drawing a line directly on my SurfaceView, I\'d like

相关标签:
4条回答
  • 2020-12-23 11:03

    You will have to add this view to the layout, setting height to 1 and width to match parent. The line will be animated from left to right. The later line will be placed over the first one.

                    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" />
    
    0 讨论(0)
  • 2020-12-23 11:04

    I just have resolve this problem, here what I do:

    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);
         }
      }
    }
    

    This example, is an adaptation of what I've done (to simplify the example). Hope you will understand it. Since I've just made this function working, It lacks of optimizations and things like that.

    Hope it will help ;-)

    0 讨论(0)
  • 2020-12-23 11:09

    here is an alternative solution that worked for me

     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);
    }
    

    }

    and use

     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();
            }
        });
    
    0 讨论(0)
  • 2020-12-23 11:11

    Instead of creating a for loop, you can use the ObjectAnimator class to callback to one of your class's methods every time you'd like to draw a bit more of the path.

    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);
        }
    }
    

    Then, just call init() to begin the animation, like this (or if you'd like it to start as soon as the view is inflated, put the init() call inside the constructors):

    PathView path_view = (PathView) root_view.findViewById(R.id.path);
    path_view.init();
    

    Also see this question here, and this example, which I've based my code on.

    0 讨论(0)
提交回复
热议问题