2010-02-10 11 views
11

Je suis en train de créer un menu qui glisse vers le haut à partir du bas. Il commence avec la vue du menu juste visible au bas de l'écran, puis en cliquant dessus, il fait glisser vers le haut. J'ai essayé d'utiliser un TranslateAnimation, mais bien que les pixels se déplacent, les zones touchées du menu sont dans la même position qu'avant. Donc, je pense que si je peux ajuster les marges du menu une fois l'animation terminée, cela va accomplir ce que je veux. Cependant, je ne peux pas comprendre comment ajuster les marges.Android - Animer un View de TopMargin/BottomMargin/etc dans LinearLayout ou RelativeLayout

J'ai essayé de créer un objet LinearLayout.LayoutMargins, puis de définir ses marges et de l'appliquer à la vue du menu (LinearLayout), mais cela ne fonctionne pas.

Des idées?

+0

Il n'y a pas classe 'LinearLayout.LayoutMargins'. Je suppose que vous voulez dire 'LinearLayout.LayoutParams'. Si vous pouviez poster le code où vous ajustez les paramètres, cela pourrait nous aider à répondre à votre question. – CommonsWare

+0

Désolé, vous avez raison, je voulais dire «LayoutParams». J'ai fini par utiliser une autre méthode pour accomplir ce que je voulais. J'ai construit deux vues, une pour l'état "menu ouvert" et une pour l'état "menu fermé". J'ai ensuite utilisé une animation de traduction pour faire glisser le menu ouvert et masquer le menu fermé, et vice versa. – karnage

Répondre

3

Ma solution a été de créer deux LinearLayouts, un dans c'est l'état (mis à parti) et l'autre dans l'état bas du menu. Ensuite, lorsque l'utilisateur clique sur le bouton pour faire glisser le menu, j'appelle un TranslateAnimation montrant le menu glisser vers le haut. Je mets un écouteur sur l'animation qui fait que l'état haut soit visible et que l'état bas soit parti quand l'animation se termine. J'ai inversé cela pour l'action "fermeture".

Pas exactement comme je l'avais imaginé au départ le faire, mais cela a fonctionné.

+0

On dirait un tel bidouillage ... mais merci! – speedplane

2

Je me sens comme cela est un peu moins d'un hack. Fondamentalement, c'est faire votre propre animateur. Ci-dessous est configuré pour modifier seulement topMargin dans un RelativeLayout, mais il ne serait pas difficile de le généraliser.

import java.util.Calendar; 

import android.app.Activity; 
import android.util.Log; 
import android.view.View; 
import android.view.animation.Interpolator; 
import android.widget.RelativeLayout.LayoutParams; 

public class MarginAnimation extends Thread { 
    final String TAG = "MarginAnimation"; 
    long mStartTime; 
    long mTotalTime; 
    Interpolator mI; 

    int mStartMargin; 
    int mEndMargin; 

    View mV; 
    Activity mA; 
    public MarginAnimation(Activity a, View v, 
      int startMargin, int endMargin, int totaltime, 
      Interpolator i) { 
     mV = v; 
     mStartMargin = startMargin; 
     mEndMargin = endMargin; 
     mTotalTime = totaltime; 
     mI = i; 
     mA = a; 
    } 

    @Override 
    public void run() { 
     mStartTime = Calendar.getInstance().getTimeInMillis(); 
     while(!this.isInterrupted() && 
       Calendar.getInstance().getTimeInMillis() - mStartTime < mTotalTime) { 

      mA.runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        if(MarginAnimation.this.isInterrupted()) 
         return; 
        long cur_time = Calendar.getInstance().getTimeInMillis(); 
        float perc_done = 1.0f*(cur_time-mStartTime)/mTotalTime; 

        final int new_margin = (int)(1.0f*(mEndMargin-mStartMargin)*mI.getInterpolation(perc_done)) + mStartMargin; 
        LayoutParams p = (LayoutParams) mV.getLayoutParams(); 
        Log.v(TAG, String.format("Setting Margin to %d", new_margin)); 
        p.topMargin = new_margin; 
        mV.setLayoutParams(p);     
       } 
      }); 

      try { 
       Thread.sleep(50); 
      } catch (InterruptedException e) { 
       return; 
      } 
     } 
    } 

} 
+0

N'a pas fonctionné ici, sur Logcat je peux voir qu'il a mis la marge plusieurs fois, mais seulement le dernier a des effets sur la vue. J'ai essayé un mV.invalidate() après mV.setLayoutParams (p) mais cela n'a pas fonctionné, des conseils? –

+0

Je l'appelle comme ceci: MarginAnimator ma = new MarginAnimator (EfeitosActivity.this, img2, lp.topMargin, 150, 500, nouveau AccelerateInterpolator()); ma.run(); –

+0

J'ai eu le même problème - en utilisant la même idée dans un AsyncTask travaillé pour moi. Le code de la fonction run doit être exécuté dans doInBackground, et le code dans le runnable uiThread doit être exécuté dans la fonction on ProgressUpdate. –

21

Ce qui suit a fonctionné pour moi. Déterminez d'abord les marges du bas (en creux) pour que le menu soit en haut (complètement visible) ou en bas (la plus grande partie est cachée).

private static final int BOTTOM_MARGIN_UP = -50; // My menu view is a bit too tall. 
private static final int BOTTOM_MARGIN_DOWN = -120; 

Puis, en onCreate():

menuLinearLayout = (LinearLayout)findViewById(R.id.menuLinearLayout); 
setBottomMargin(menuLinearLayout, BOTTOM_MARGIN_DOWN); 

upAnimation = makeAnimation(BOTTOM_MARGIN_DOWN, BOTTOM_MARGIN_UP); 
downAnimation = makeAnimation(BOTTOM_MARGIN_UP, BOTTOM_MARGIN_DOWN); 

Button toggleMenuButton = (Button)findViewById(R.id.toggleMenuButton); 
toggleMenuButton.setOnTouchListener(new View.OnTouchListener() 
{ 
    public boolean onTouch(View view, MotionEvent motionEvent) 
    { 
     if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) return false; 
     ViewGroup.MarginLayoutParams layoutParams = 
      (ViewGroup.MarginLayoutParams)menuLinearLayout.getLayoutParams(); 
     boolean isUp = layoutParams.bottomMargin == dipsToPixels(BOTTOM_MARGIN_UP); 
     menuLinearLayout.startAnimation(isUp ? downAnimation : upAnimation); 
     return true; 
    } 
}); 

Et ici vient la sauce secrète ;-)

private TranslateAnimation makeAnimation(final int fromMargin, final int toMargin) 
{ 
    TranslateAnimation animation = 
     new TranslateAnimation(0, 0, 0, dipsToPixels(fromMargin - toMargin)); 
    animation.setDuration(250); 
    animation.setAnimationListener(new Animation.AnimationListener() 
    { 
     public void onAnimationEnd(Animation animation) 
     { 
      // Cancel the animation to stop the menu from popping back. 
      menuLinearLayout.clearAnimation(); 

      // Set the new bottom margin. 
      setBottomMargin(menuLinearLayout, toMargin); 
     } 

     public void onAnimationStart(Animation animation) {} 

     public void onAnimationRepeat(Animation animation) {} 
    }); 
    return animation; 
} 

J'utilise deux fonctions utilitaires:

private void setBottomMargin(View view, int bottomMarginInDips) 
{ 
    ViewGroup.MarginLayoutParams layoutParams =  
     (ViewGroup.MarginLayoutParams)view.getLayoutParams(); 
    layoutParams.bottomMargin = dipsToPixels(bottomMarginInDips); 
    view.requestLayout(); 
} 

private int dipsToPixels(int dips) 
{ 
    final float scale = getResources().getDisplayMetrics().density; 
    return (int)(dips * scale + 0.5f); 
} 

Voila!

+0

Merci Arne, c'est génial !! – Tom

+0

Bonne réponse. Cependant, est-il possible d'utiliser un autre type d'animation qui modifie la marge de chaque image? Je fais une diapositive 'Fragment' à partir du bas en utilisant votre méthode et elle n'apparaît qu'à la fin de l'animation, quand la marge est définie dans' onAnimationEnd' (je pense qu'elle n'est pas redimensionnée pendant l'animation). Fonctionne bien pour le faire glisser cependant. – Jukurrpa

+0

@Jukurrpa Désolé, je n'ai pas la moindre idée. Je fais de la programmation Android pour le moment. –

1

Ce message m'a aidé à faire fonctionner mon animation. Dans mon cas, j'ai un webview en plein écran. Sur certaines actions de l'utilisateur, cette page web glisse vers la droite, environ 70% et une nouvelle vue Web émerge de la gauche et prend l'espace disponible. Résultat final, un nouveau webview couvre les 70% (axe des x) et l'ancien webview le reste. La solution d'Arne m'a énormément aidé, en mettant en place la marge à l'ancienne vue dès que l'animation est terminée. Code Pseudo:

 marginParams.leftMargin = 336; //(70% of 480px) 

Mais je fait face à un comportement bizarre, je suppose que mon vieux WebView (qui occupe aujourd'hui seulement 30% d'espace), a été reformater son contenu, on peut penser qu'il est maintenant pressé dans un plus petit espace, plutôt que de se comporter comme s'il venait de glisser vers la droite. En d'autres termes, dès que j'ai défini cette marge, la disposition html du webview a changé. Encore une fois, je n'avais aucune idée de pourquoi et mon devinez est-il pensé que sa taille de fenêtre parent a changé.Sur la base de cette hypothèse, j'ajouté une ligne de code:

 marginParams.rightMargin = -336; //(same amount, but negative margin!) 

Et qui a fait l'affaire, sans reformatage du contenu html et je peux communiquer avec les deux webviews en parallèle.

Je poste ceci comme un grand merci à Arne pour l'idée et aussi pour obtenir des entrées pour le comportement que j'ai vu et mes hypothèses pour cela. En fait, j'aime la solution finale, logique, mais je peux me tromper. . . Toute pensée et contribution serait grandement appréciée. Merci beaucoup.

2

Utilisez le ViewPropertyAnimator:

if (view.getY() != margin) { 
    view.animate().y(margin).setDuration(150).start(); 
} 
+0

quelle est la bonne solution homme !! Je vous remercie – bebosh