5

For my activity i use 3 custom views stacked. the lower one is a SurfaceView fullscreen, the middle one is derived from GridView and overrides onDraw to add custom graphic.

The top one is derived directly from View, sits in a corner of the screen and act as a knob to control the others two views (this is the problematic view).

to add a custom animation to this view, i used this pattern:

public class StubKnobView extends View{

    private Drawable knob;
    private Point knobPos;
    private float rotation;
    private boolean needsToMove;

    public void moveKnob(){
            /* ...various calculations... */
            this.needsToMove=true;
            invalidate();   //to notify the intention to start animation
    }

    private void updateAnimation(){
        /* ........... */
        this.needsToMove= !animationComplete;
        invalidate();        
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int saveCount=canvas.getSaveCount();
        canvas.save();
        canvas.rotate(this.rotation, this.knobPos.x, this.knobPos.y );
        this.knob.draw(canvas);
        canvas.restoreToCount(saveCount);
        if(this.needsToMove){
            updateAnimation();
        }
    }
}

ideally, if there is an animation pending, after each drawing cycle the view should auto invalidate.

right now this doesn't work, to force the animation i have to touch the screen to cause a onDraw cycle. Using "show screen updates" of Dev tools I see that no screen invalidate/update cycle happen , apart when i click the screen. specifying the dirty rect also ha no effect.

So, any idea where to look to know why this invalidate/draw cycle does not work the way is intended?

andijcr
  • 427
  • 3
  • 13

2 Answers2

2

I encontered this situation, and also doubted that invalidate() doesn't make onDraw() called again.

After simplified the business codes, it's turn out that there is a dead loop - exactly too much iterations, which blocks the UI thread.

So, my advice is that make sure the UI thread going smoothly first.

Marvin Pinto
  • 30,138
  • 7
  • 37
  • 54
odys
  • 21
  • 2
0

Try something like this with a private class in your View. The idea is not to call invalidate() from within onDraw(). Instead, post it on the running queue. Instantiate the private class in your View constructor and then just call animateKnob.start() when you want the animation. The onDraw() can then just focus on drawing.

public void moveKnob(){

  // update the state to draw... KnobPos, rotation
  invalidate()

}


private class AnimateKnob{
   public void start(){
      // Do some calculations if necessary 
      // Start the animation
      MyView.this.post( new Runnable() {
        stepAnimation()
      });
   }

   public void stepAnimation(){
     // update the animation, maybe use an interpolator.
     // Here you could also determine if the animation is complete or not

     MyView.this.moveKnob();

     if( !animationComplete ){
       // Call it again
       MyView.this.post( new Runnable() {
          stepAnimation()
       });
     }

   } 

}
dbryson
  • 6,047
  • 2
  • 20
  • 20