【问题标题】:How to draw a path on an Android canvas with animation?如何在带有动画的 Android 画布上绘制路径?
【发布时间】:2012-08-15 18:52:04
【问题描述】:

我正在制作一个 Android 应用程序,但我有一件棘手的事情要做。 我需要在画布上绘制一条路径,但绘图应该是动画的(即,一点一点地绘制,并稍有延迟)。

是否可以使用 Android SDK 制作类似的东西? 如果没有,我怎么能产生这种效果?

【问题讨论】:

    标签: android android-animation android-canvas


    【解决方案1】:

    试试这段代码,我用它用PathCanvas画了一个心跳:

    public class TestActivity extends Activity {
    
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(new HeartbeatView(this));
    
        }
    
        public static class HeartbeatView extends View {
    
            private static Paint paint;
            private int screenW, screenH;
            private float X, Y;
            private Path path;
            private float initialScreenW;
            private float initialX, plusX;
            private float TX;
            private boolean translate;
            private int flash;
            private Context context;
    
    
            public HeartbeatView(Context context) {
                super(context);
    
                this.context=context;
    
                paint = new Paint();
                paint.setColor(Color.argb(0xff, 0x99, 0x00, 0x00));
                paint.setStrokeWidth(10);
                paint.setAntiAlias(true);
                paint.setStrokeCap(Paint.Cap.ROUND);
                paint.setStrokeJoin(Paint.Join.ROUND);
                paint.setStyle(Paint.Style.STROKE);
                paint.setShadowLayer(7, 0, 0, Color.RED);
    
    
                path= new Path();
                TX=0;
                translate=false;
    
                flash=0;
    
            }
    
            @Override
            public void onSizeChanged (int w, int h, int oldw, int oldh) {
                super.onSizeChanged(w, h, oldw, oldh);
    
                screenW = w;
                screenH = h;
                X = 0;
                Y = (screenH/2)+(screenH/4)+(screenH/10);
    
                initialScreenW=screenW;
                initialX=((screenW/2)+(screenW/4));
                plusX=(screenW/24);
    
                path.moveTo(X, Y);
    
            }
    
    
    
            @Override
            public void onDraw(Canvas canvas) {
                super.onDraw(canvas);
    
                //canvas.save();    
    
    
                flash+=1;
                if(flash<10 || (flash>20 && flash<30))
                {
                    paint.setStrokeWidth(16);
                    paint.setColor(Color.RED);
                    paint.setShadowLayer(12, 0, 0, Color.RED);
                }
                else
                {
                    paint.setStrokeWidth(10);
                    paint.setColor(Color.argb(0xff, 0x99, 0x00, 0x00));
                    paint.setShadowLayer(7, 0, 0, Color.RED);
                }
    
                if(flash==100)
                {
                    flash=0;
                }
    
                path.lineTo(X,Y);
                canvas.translate(-TX, 0);
                if(translate==true)
                {
                    TX+=4;
                }
    
                if(X<initialX)
                {
                    X+=8;
                }
                else
                {
                    if(X<initialX+plusX)
                    {
                        X+=2;
                        Y-=8;
                    }
                    else
                    {
                        if(X<initialX+(plusX*2))
                        {
                            X+=2;
                            Y+=14;
                        }
                        else
                        {
                            if(X<initialX+(plusX*3))
                            {
                                X+=2;
                                Y-=12;
                            }
                            else
                            {
                                if(X<initialX+(plusX*4))
                                {
                                    X+=2;
                                    Y+=6;
                                }
                                else
                                {
                                    if(X<initialScreenW)
                                    {
                                        X+=8;
                                    }
                                    else
                                    {
                                        translate=true;
                                        initialX=initialX+initialScreenW;
                                    }
                                }
                            }
                        }
                    }
    
                }
    
                canvas.drawPath(path, paint);
    
    
                //canvas.restore(); 
    
                invalidate();
            }
        }
    
    }
    

    它使用使用计数器的几个效果逐点绘制路径。你可以把你需要的东西转移到更高效的SurfaceView。

    【讨论】:

    • 这看起来不错,但我在您的代码中看不到任何动画。这不是静态图吗?
    • 我的错,我刚刚尝试了你的代码,它太棒了!干得好!
    • 是的,它看起来是静态的,但实际上随着全局变量而变化,函数invalidate() 就像你有一个动画并且你调用下一帧,在此之前你正在增加你想要的东西,那就是动画是如何生成的。编码快乐!
    • 这有时可能会起作用,但这是为您的路径设置动画的一种不好的方法。不应使用 onDraw() 方法进行实际更新,因为如果设备忙于渲染其他内容,那么您的动画将变慢。如果设备无法处理,动画应该会丢帧。 onDraw() 应该只绘制当前状态,并且您应该有一个单独的线程/ValueAnimator 对控制绘制大小/位置的实例变量进行更改。
    • 对不起,HeartbeatView 的来源在哪里,你的 github 链接吗
    【解决方案2】:

    我希望这是您正在寻找的。它在用户触摸上绘制路径,你可以简单地调整它来实现你想要的。

    public class MyCanvas extends Activity implements OnTouchListener{
    
            DrawPanel dp;
            private ArrayList<Path> pointsToDraw = new ArrayList<Path>();
            private Paint mPaint;
            Path path;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                dp = new DrawPanel(this);
                dp.setOnTouchListener(this);
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
                requestWindowFeature(Window.FEATURE_NO_TITLE);
                mPaint = new Paint();
                mPaint.setDither(true);
                mPaint.setColor(Color.WHITE);
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeJoin(Paint.Join.ROUND);
                mPaint.setStrokeCap(Paint.Cap.ROUND);
                mPaint.setStrokeWidth(30);
    
                FrameLayout fl = new FrameLayout(this);  
                fl.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
                fl.addView(dp);  
                setContentView(fl);  
    
            }
    
            @Override
            protected void onPause() {
                // TODO Auto-generated method stub
                super.onPause();
                dp.pause();
            }
    
    
    
            @Override
            protected void onResume() {
                // TODO Auto-generated method stub
                super.onResume();
                dp.resume();
            }
    
    
    
            public class DrawPanel extends SurfaceView implements Runnable{
    
                Thread t = null;
                SurfaceHolder holder;
                boolean isItOk = false ;
    
                public DrawPanel(Context context) {
                    super(context);
                    // TODO Auto-generated constructor stub
                    holder = getHolder();
                }
    
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    while( isItOk == true){
    
                        if(!holder.getSurface().isValid()){
                            continue;
                        }
    
                        Canvas c = holder.lockCanvas();
                        c.drawARGB(255, 0, 0, 0);
                        onDraw(c);
                        holder.unlockCanvasAndPost(c);
                    }
                }
    
                @Override
                protected void onDraw(Canvas canvas) {
                    // TODO Auto-generated method stub
                    super.onDraw(canvas);
                                synchronized(pointsToDraw)
                                {
                    for (Path path : pointsToDraw) {
                        canvas.drawPath(path, mPaint);
                    }
                                }
                }
    
                public void pause(){
                    isItOk = false;
                    while(true){
                        try{
                            t.join();
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }
                        break;
                    }
                    t = null;
                }
    
                public void resume(){
                    isItOk = true;  
                    t = new Thread(this);
                    t.start();
    
                }
    
    
    
            }
    
    
            @Override
            public boolean onTouch(View v, MotionEvent me) {
                // TODO Auto-generated method stub
                        synchronized(pointsToDraw)
                        {
                if(me.getAction() == MotionEvent.ACTION_DOWN){
                    path = new Path();
                    path.moveTo(me.getX(), me.getY());
                    //path.lineTo(me.getX(), me.getY());
                    pointsToDraw.add(path);
                }else if(me.getAction() == MotionEvent.ACTION_MOVE){
                    path.lineTo(me.getX(), me.getY());
                }else if(me.getAction() == MotionEvent.ACTION_UP){
                    //path.lineTo(me.getX(), me.getY());
                }
                }       
                return true;
    
            }
    
        }
    

    【讨论】:

    • 我认为这是迄今为止最好的答案,但这并不是我想要的。在您的情况下,这将起作用,因为从手指绘制将提供足够的点来生成动画。就我而言,没有与用户手指的交互。路径是应用程序提供的东西。点可能太远了,那么就没有动画了。
    • 您可以先检查屏幕大小,然后将点数限制在该范围内。
    • 是的,我认为如果我们在这里结合这两个答案,我们会得到一些很酷的东西。干杯!
    • 你有任何解决方案来为画布路径设置动画吗?如果可以,请分享您的观点。
    【解决方案3】:

    我已经用 ObjectAnimator 做到了。 我们有任何 Path 和 CustomView(我们将在其中绘制路径)

        private CustomView view;
        private Path path;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            view = findViewById(R.id.custom_view);
            path = new Path();
            path.moveTo(0f, 0f);
            path.lineTo(getResources().getDimension(R.dimen.point_250), 0f);
            path.lineTo(getResources().getDimension(R.dimen.point_250), getResources().getDimension(R.dimen.point_150));
    
            findViewById(R.id.btnStart).setOnClickListener(v -> {
                test();
            });
        }
    
        private void test() {
            ValueAnimator pathAnimator = ObjectAnimator.ofFloat(view, "xCoord", "yCoord", path);
            pathAnimator.setDuration(5000);
            pathAnimator.start();
        }
    

    只需将我们的“xCoord”和“yCoord”传递给 CustomView

    public class CustomView extends View {
    
        private Paint paint;
        private float xCoord;
        private float yCoord;
    
        private Path path = new Path();
    
        public void setXCoord(float xCoord) {
            this.xCoord = xCoord;
        }
    
        public void setYCoord(float yCoord) {
            this.yCoord = yCoord;
            path.lineTo(xCoord, yCoord);
            invalidate();
        }
    
        public CustomView(Context context) {
            super(context);
            init();
        }
    
        public CustomView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        void init() {
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setDither(true);
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeCap(Paint.Cap.ROUND);
            paint.setStrokeWidth(20);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.drawPath(path, paint);
    
        }
    
    }
    

    【讨论】:

    • 感谢您帮助我完成一些修改的任务
    【解决方案4】:

    这可能会有所帮助...它绘制相邻的圆圈而不是路径来模拟可动画的路径。

    public class PathAnimatable {
    private final float CIRCLE_SIZE = 2.5f;
    public float SPPED_SCALE = 1f;
    private float steps = 0;
    private float pathLength;
    private PathMeasure pathMeasure;
    private float totalStepsNeeded;
    private float[] point = new float[]{0f, 0f};
    private float stride;
    
    public PathAnimatable() {
      this(null);
    }
    
    public PathAnimatable(Path path) {
      super(path);
      init();
    }
    
    private void init() {
      pathMeasure = new PathMeasure(path, false);
      pathLength = pathMeasure.getLength();
      stride = CIRCLE_SIZE * 0.5f;
      totalStepsNeeded = pathLength / stride;
      steps = 0;
    }
    
    @Override
    public void setPath(Path path) {
      super.setPath(path);
      init();
    }
    
    // Called this from your locked canvas loop function
    public void drawShape(Canvas canvas, Paint paint) {
      if (steps <= pathLength) {
        for (float i = 0; i < steps ; i += stride) {
          pathMeasure.getPosTan(i, point, null);
          canvas.drawCircle(point[0], point[1], CIRCLE_SIZE, paint);
        }
        steps += stride * SPPED_SCALE;
      } else {
        steps = 0;
      }
    }
    }
    

    【讨论】:

    • 你能分享一下如何使用这个帮助类
    • 这个类应该继承自另一个类,因为我发现`super(path);`
    猜你喜欢
    • 1970-01-01
    • 2021-06-25
    • 2019-04-12
    • 2013-05-18
    • 1970-01-01
    • 2013-12-20
    • 2014-10-01
    • 1970-01-01
    相关资源
    最近更新 更多