2012-07-24 5 views
2

J'anime trois vues empilées les unes sur les autres. Lorsque je tape sur une vue qui n'est pas la vue de face, une ou deux vues vont glisser vers le haut ou vers le bas pour découvrir la vue tapée, amener la vue tapée à l'avant, puis tout ramener à leur position d'origine. La plupart d'entre eux fonctionnent bien. Ce n'est que lorsque j'apporte une vue à l'avant que je m'anime juste que j'obtiens un scintillement perceptible.Android bringToFront provoque le scintillement

J'ai lu au moins une centaine de messages mais aucun ne contient la solution. Je poste ceci pour consolider chaque solution suggérée dans un endroit et trouver avec optimisme une solution.

Je sais que l'animation n'anime pas la vue elle-même, mais juste une image. La vue reste à sa position d'origine. C'est certainement lié à cela. Cela n'arrive que lorsqu'on amène une vue à l'avant qui vient de bouger.

Le déplacement de la vue vers la position finale de l'animation avant le démarrage de l'animation ou après la fin de l'animation n'aide pas un bit.

Il n'est pas non plus lié au bogue AnimationListener.onAnimationEnd, puisque j'ai dérivé mes propres vues et intercepte onAnimationEnd là. J'utilise Animation.setFillAfter et Animation.setFillEnabled pour conserver l'image finale à l'emplacement final de l'animation.

J'ai essayé d'utiliser Animation.setZAdjustment mais celui-ci ne fonctionne que pour des écrans entiers, pas des vues dans un écran. De ce que j'ai appris je suspecte que le problème est bringToFront() lui-même, qui fait un removeChild()/addChild() sur la vue de parent. Peut-être que le removeChild provoque le redessin montrant la vue sans l'enfant retiré brièvement.

Donc, mes questions: Quelqu'un voit-il quelque chose que j'ai raté qui pourrait résoudre ce problème? Est-ce que Android peut avoir une commande pour arrêter temporairement le dessin et reprendre le dessin plus tard. Quelque chose comme une paire setUpdateScreen(false)/setUpdateScreen(true)? Cela me permettrait d'ignorer l'étape scintillement.

Le code minimal pour démo l'effet suit. Appuyez sur le blanc pour voir le rouge remonter et redescendre derrière le blanc sans scintillement (le blanc arrive à l'avant mais ne bouge pas). Ensuite, appuyez sur le rouge pour voir le rouge remonter derrière le blanc et le scintillement quand il est amené à l'avant juste avant qu'il ne glisse vers le bas sur le blanc. Chose étrange est que la même chose ne se produit pas toujours lorsque vous utilisez bleu au lieu de rouge.

MainActivity.java

package com.example.testapp; 

import com.example.testapp.ImagePanel.AnimationEndListener; 
import android.app.Activity; 
import android.os.Bundle; 
import android.view.View; 
import android.view.animation.TranslateAnimation; 

public class MainActivity extends Activity 
{ 
    private static final int ANIMATION_TIME = 1000; 

    private ImagePanel mRed; 
    private ImagePanel mWhite; 
    private ImagePanel mBlue; 
    private int mFrontPanelId; 

    private void animate(final ImagePanel panel, final int yFrom, final int yTo, 
     final AnimationEndListener animationListener) 
    { 
    final TranslateAnimation anim = new TranslateAnimation(0, 0, 0, 0, 0, yFrom, 0, yTo); 
    anim.setDuration(ANIMATION_TIME); 
    anim.setFillAfter(true); 
    anim.setFillEnabled(true); 
    if (animationListener != null) 
    { 
     panel.setAnimListener(animationListener); 
    } 
    panel.startAnimation(anim); 
    } 

    public void onClick(final View v) 
    { 
    final int panelId = v.getId(); 
    if (mFrontPanelId == panelId) 
    { 
     return; 
    } 
    final ImagePanel panel = (ImagePanel) v; 

    final int yTop = mWhite.getTop() - mRed.getBottom(); 
    final int yBot = mWhite.getBottom() - mBlue.getTop(); 

    final boolean moveRed = panelId == R.id.red || mFrontPanelId == R.id.red; 
    final boolean moveBlue = panelId == R.id.blue || mFrontPanelId == R.id.blue; 

    animate(mBlue, 0, moveBlue ? yBot : 0, null); 
    animate(mRed, 0, moveRed ? yTop : 0, new AnimationEndListener() 
    { 
     public void onBegin() 
     { 
     } 

     public void onEnd() 
     { 
     // make sure middle panel always stays visible 
     if (moveRed && moveBlue) 
     { 
      mWhite.bringToFront(); 
     } 

     panel.bringToFront(); 

     animate(mBlue, moveBlue ? yBot : 0, 0, null); 
     animate(mRed, moveRed ? yTop : 0, 0, new AnimationEndListener() 
     { 
      public void onBegin() 
      { 
      } 

      public void onEnd() 
      { 
      } 
     }); 

     mFrontPanelId = panelId; 
     } 
    }); 
    } 

    @Override 
    public void onCreate(final Bundle savedInstanceState) 
    { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    mRed = (ImagePanel) findViewById(R.id.red); 
    mWhite = (ImagePanel) findViewById(R.id.white); 
    mBlue = (ImagePanel) findViewById(R.id.blue); 
    mFrontPanelId = R.id.red; 
    } 
} 

ImagePanel.java

package com.example.testapp; 

import android.content.Context; 
import android.util.AttributeSet; 
import android.widget.ImageView; 

public class ImagePanel extends ImageView 
{ 
    public interface AnimationEndListener 
    { 
    public void onBegin(); 

    public void onEnd(); 
    } 

    private AnimationEndListener mAnim = null; 

    public ImagePanel(final Context context) 
    { 
    super(context); 
    } 

    public ImagePanel(final Context context, final AttributeSet attrs) 
    { 
    super(context, attrs); 
    } 

    public ImagePanel(final Context context, final AttributeSet attrs, final int defStyle) 
    { 
    super(context, attrs, defStyle); 
    } 

    @Override 
    protected void onAnimationEnd() 
    { 
    super.onAnimationEnd(); 
    clearAnimation(); 
    if (mAnim != null) 
    { 
     final AnimationEndListener anim = mAnim; 
     mAnim = null; 
     anim.onEnd(); 
    } 
    } 

    @Override 
    protected void onAnimationStart() 
    { 
    super.onAnimationStart(); 
    if (mAnim != null) 
    { 
     mAnim.onBegin(); 
    } 
    } 

    public void setAnimListener(final AnimationEndListener anim) 
    { 
    mAnim = anim; 
    } 
} 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/main" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" > 

    <com.example.testapp.ImagePanel 
     android:id="@+id/blue" 
     android:layout_width="300dp" 
     android:layout_height="300dp" 
     android:layout_alignParentBottom="true" 
     android:layout_centerHorizontal="true" 
     android:background="#000080" 
     android:src="@drawable/testpattern" 
     android:onClick="onClick" /> 

    <com.example.testapp.ImagePanel 
     android:id="@+id/white" 
     android:layout_width="300dp" 
     android:layout_height="300dp" 
     android:layout_centerHorizontal="true" 
     android:layout_centerVertical="true" 
     android:background="#808080" 
     android:src="@drawable/testpattern" 
     android:onClick="onClick" /> 

    <com.example.testapp.ImagePanel 
     android:id="@+id/red" 
     android:layout_width="300dp" 
     android:layout_height="300dp" 
     android:layout_alignParentTop="true" 
     android:layout_centerHorizontal="true" 
     android:adjustViewBounds="true" 
     android:background="#800000" 
     android:src="@drawable/testpattern" 
     android:onClick="onClick" /> 

</RelativeLayout> 

testpattern.xml

<?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="oval" > 

    <gradient 

     android:startColor="#00000000" 
     android:endColor="#ffffffff" /> 

</shape> 

Répondre

0

Avez-vous besoin de toutes les images soient visibles à tout moment? vous pouvez définir leur visibilité comme invisible afin qu'ils ne vous dérangent pas. vous les rendez visibles une fois que vous en avez besoin.

+0

Il y a seulement 3 images dans l'application de démonstration et des parties de chacune sont visibles en tout temps. En fait, l'application que je construis qui a ce problème a encore plus d'images et je fais les invisibles. –

0

Essayez d'appeler animation.cancel() dans onAnimationEnd. Je me souviens d'un moment, j'ai eu un problème similaire avec animation clignotant après l'exécution du code dans onAnimationEnd et cela a fait l'affaire.

+0

Merci. J'ai essayé ça aussi. J'ai oublié de mentionner celui-là. C'est certainement le 'bringToFront()' qui provoque le scintillement. –

0

J'ai observé les mêmes problèmes lors de l'appel bringToFront() lors d'une animation.

Je pouvais résoudre mon problème en utilisant setChildrenDrawingOrderEnabled(boolean enabled) et getChildDrawingOrder(int childCount, int i) sur le ViewGroup qui contenait les enfants que j'animais.

+0

J'ai essayé ceci. J'ai étendu la disposition Racine et implémenté getChildDrawingOrder. Mais pour que l'écran reflète le nouvel ordre, vous devez toujours appeler invalidate() depuis votre bringChildToFront - ce qui provoque toujours un scintillement pour toute animation en cours. Ou avez-vous fait différemment? – slott

Questions connexes