2010-02-10 5 views
11

Ich versuche ein Menü zu erstellen, das von unten nach oben gleitet. Es beginnt mit der Ansicht des Menüs, die gerade am unteren Rand des Bildschirms sichtbar ist, und dann, wenn Sie darauf klicken, wird es nach oben verschoben. Ich versuchte es mit einem TranslateAnimation, aber obwohl die Pixel bewegen, sind die Trefferbereiche des Menüs in der gleichen Position wie zuvor. Ich denke also, wenn ich die Ränder des Menüs einstellen kann, nachdem die Animation abgeschlossen ist, wird dies erreichen, was ich will. Ich kann jedoch nicht herausfinden, wie die Ränder angepasst werden.Android - Animiere eine Ansicht topMargin/bottomMargin/etc in LinearLayout oder RelativeLayout

Ich habe versucht, ein LinearLayout.LayoutMargins Objekt zu erstellen und dann seine Ränder festlegen und es auf die Menüansicht anwenden (das ist ein LinearLayout), aber das funktioniert nicht.

Irgendwelche Ideen?

+0

Es gibt keine 'LinearLayout.LayoutMargins' Klasse. Ich nehme an, du meinst 'LinearLayout.LayoutParams'. Wenn Sie den Code eingeben könnten, in dem Sie die Parameter anpassen, können wir Ihnen bei der Beantwortung Ihrer Frage helfen. – CommonsWare

+0

Sorry, du hast recht, ich meinte 'LayoutParams'. Ich benutzte eine andere Methode, um das zu erreichen, was ich wollte. Ich habe zwei Ansichten erstellt, eine für den Status "Menü geöffnet" und eine für den Status "Menü geschlossen". Ich benutzte dann eine Übersetzungs-Animation, um das offene Menü nach oben zu schieben und das geschlossene Menü zu verstecken, und umgekehrt. – karnage

Antwort

3

Meine Lösung bestand darin, zwei LinearLayouts zu erstellen, von denen eines in den Status "up" (deaktiviert) und das andere in den Status "down" des Menüs gesetzt wurde. Wenn der Benutzer dann auf die Schaltfläche klickt, um das Menü nach oben zu verschieben, rufe ich eine TranslateAnimation auf, die das Menü nach oben zeigt. Ich habe einen Listener auf die Animation gesetzt, der dazu führt, dass der Status oben angezeigt wird und der Status deaktiviert ist, wenn die Animation beendet ist. Ich habe das für die "Closing" -Aktion umgekehrt.

Nicht genau so, wie ich es mir ursprünglich vorgestellt hatte, aber es hat funktioniert.

+0

Scheint so ein Hack ... aber danke! – speedplane

2

Ich fühle mich wie ein bisschen weniger von einem Hack. Im Grunde macht es deinen eigenen Animator. Unten ist es eingerichtet nur topMargin in einem RelativeLayout zu ändern, aber es wäre nicht schwer, es zu verallgemeinern.

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

Hat hier nicht funktioniert, auf Logcat kann ich sehen, dass es den Rand mehrmals eingestellt hat, aber nur der letzte hat Auswirkungen auf die Ansicht. Ich habe versucht, eine mV.invalidate() nach mV.setLayoutParams (p), aber es hat nicht funktioniert, irgendwelche Tipps? –

+0

Ich nenne es so: MarginAnimator ma = neuer MarginAnimator (EfeitosActivity.this, img2, lp.topMargin, 150, 500, neuer AccelerateInterpolator()); ma.run(); –

+0

Ich hatte das gleiche Problem - mit der gleichen Idee in einer AsyncTask arbeitete für mich. Der Code in der run-Funktion sollte in doInBackground ausgeführt werden, und der Code in der uiThread runnable sollte in der on ProgressUpdate-Funktion ausgeführt werden. –

21

Folgendes funktionierte für mich. Bestimmen Sie zuerst die unteren Ränder (in Dips) für das Menü, das gerade geöffnet wird (vollständig sichtbar) oder nicht (das meiste davon ist ausgeblendet).

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

Dann in 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; 
    } 
}); 

Und hier kommt das Geheimnis Sauce ;-)

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

ich zwei Hilfsfunktionen:

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

Danke Arne, das ist super !! – Tom

+0

Große Antwort. Ist es jedoch möglich, eine andere Art von 'Animation' zu verwenden, die den Rand in jedem Frame verändert? Ich mache eine 'Fragment'-Folie von unten mit Ihrer Methode und sie erscheint nur am Ende der Animation, wenn der Rand auf' onAnimationEnd' eingestellt ist (ich denke, dass die Größe während der Animation nicht verändert wird). Funktioniert gut, um es heraus zu schieben. – Jukurrpa

+0

@Jukurrpa Tut mir leid, ich habe keine Ahnung. Ich mache gerade Android-Programmierung. –

1

Dieser Beitrag hat mir geholfen, meine Animation funktioniert zu bekommen. In meinem Fall habe ich ein Vollbild-Webview. Bei einigen Benutzeraktionen gleitet diese Webansicht nach rechts, etwa 70%, und ein neues Webview erscheint von links und nimmt den verfügbaren Platz ein. Endergebnis, ein neuer Webview deckt den 70% (x-Achse) und den alten Webview den Rest ab. Arnes Lösung hat mir enorm geholfen, den Rand der alten Ansicht zu setzen, sobald die Animation fertig ist. Pseudo-Code:

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

Aber ich stand vor einem seltsames Verhalten, ich denke, meine alte webview (die jetzt nur noch 30% Raum einnimmt), wurde ihren Inhalt Neuformatierung werden kann denken, dass es jetzt in einen kleineren Raum zusammengedrückt wird, anstatt sich so zu verhalten, als wäre es einfach nach rechts gerutscht. Mit anderen Worten, sobald ich diesen Rand gesetzt habe, änderte sich das HTML-Layout des Webview. Wieder hatte ich keine Ahnung warum und meine erraten ist es dachte seine Elternfenstergröße geändert.Basierend auf dieser Annahme, fügte ich noch eine Codezeile:

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

Und das war der Trick, keine Neuformatierung der HTML-Inhalte, und ich kann mit beiden WebViews parallel zu interagieren.

Ich poste dies als ein großes Dankeschön an Arne für die Idee und auch irgendwelche Inputs für das Verhalten, das ich sah und meine Annahmen dafür. Ich mag eigentlich die Endlösung, macht logischen Sinn, aber ich kann mich irren. . . alle Gedanken und Eingaben würden sehr geschätzt werden. Vielen Dank.

2

Verwenden Sie die ViewPropertyAnimator:

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

was ist die große lösung mann !! Danke – bebosh

Verwandte Themen