5 votes

invalidate() sur une vue personnalisée ne provoque pas l'appel de onDraw(Canvas c)

Pour mon activité, j'utilise 3 vues personnalisées empilées. La vue du bas est une SurfaceView plein écran, celle du milieu est dérivée de GridView et surcharge onDraw pour ajouter un graphique personnalisé.

La vue supérieure est dérivée directement de View, se trouve dans un coin de l'écran et sert de bouton pour contrôler les deux autres vues (c'est la vue problématique).

Pour ajouter une animation personnalisée à cette vue, j'ai utilisé ce modèle :

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

Idéalement, si une animation est en cours, la vue devrait s'invalider automatiquement après chaque cycle de dessin.

Actuellement, cela ne fonctionne pas, pour forcer l'animation, je dois toucher l'écran pour provoquer un cycle onDraw. En utilisant "show screen updates" dans les outils de développement, je vois qu'aucun cycle d'invalidation/mise à jour de l'écran ne se produit, sauf lorsque je clique sur l'écran. La spécification du rectangle sale n'a également aucun effet.

Alors, une idée de l'endroit où chercher pour savoir pourquoi ce cycle d'invalidation/d'extraction ne fonctionne pas comme prévu ?

2voto

odys Points 21

J'ai rencontré cette situation, et j'ai également douté que invalidate() ne fait pas onDraw() appelé à nouveau.

Après avoir simplifié les codes d'affaires, il s'avère qu'il y a une boucle morte - exactement trop d'itérations, ce qui bloque le thread de l'interface utilisateur.

Donc, mon conseil est de s'assurer que le fil conducteur de l'interface utilisateur fonctionne bien d'abord.

0voto

dbryson Points 2945

Essayez quelque chose comme ceci avec une classe privée dans votre vue. L'idée est de ne pas appeler invalidate() de l'intérieur onDraw() . Au lieu de cela, postez-le dans la file d'attente en cours. Instanciez la classe privée dans le constructeur de votre vue, puis appelez simplement animateKnob.start() quand vous voulez l'animation. Le site onDraw() peut alors se concentrer sur le dessin.

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

   } 

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X