Android - Animate a TopMargin/BottomMargin/etc de uma visualização em linearlayout ou relativayout

StackOverflow https://stackoverflow.com/questions/2239442

Pergunta

Estou tentando criar um menu que deslize a partir da parte inferior. Começa com a visualização do menu apenas visível na parte inferior da tela e, em seguida, clicar, faz com que ele deslize para cima. Eu tentei usar um TranslateAnimation, mas embora os pixels se movam, as áreas de sucesso do menu estão na mesma posição de antes. Então, acho que, se eu puder ajustar as margens do menu após a conclusão da animação, isso realizará o que eu quero. No entanto, não consigo descobrir como ajustar as margens.

Eu tentei criar um LinearLayout.LayoutMargins objetar e, em seguida LinearLayout), mas isso não funciona.

Alguma ideia?

Foi útil?

Solução 2

My solution was to create two LinearLayouts, one in it's up state (set to gone) and the other in the menu's down state. Then when the user clicks on the button to slide the menu up, I call a TranslateAnimation showing the menu slide up. I put a listener on the animation that causes the up state to be visible and the down state to be gone when the animation finishes. I reversed this for the "closing" action.

Not exactly the way I had originally imagined doing it, but it worked.

Outras dicas

The following worked for me. First decide the bottom margins (in dips) for the menu being up (completely visible) or down (most of it hidden).

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

Then, 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;
    }
});

And here comes the secret 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;
}

I use two utility functions:

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!

Use the ViewPropertyAnimator:

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

I feel like this is a bit less of a hack. Basically, it's making your own animator. Below it's setup for only modifying topMargin in a RelativeLayout, but it wouldn't be hard to generalize it.

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

}

This post helped me get my animation working. In my case, I have a full screen webview. On some user action, this webview slides to the right, about 70% and a new webview emerges from the left and takes the available space. End result, a new webview covers the 70% (x-axis) and the old webview the rest. Arne's solution helped me immensely, setting up the margin to the old view as soon as the animation is done. Pseudo code:

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

But I faced a weird behavior, I guess my old webview (which now only occupies 30% space), was reformatting its contents, may be thinking that it is now squeezed into a smaller space, rather than behaving as if its just slid to the right. In other words, as soon as I set this margin, the html layout of the webview changed. Again, I had no idea why and my guess is it thought its parent window size changed. Based on that assumption, I added one more line of code:

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

And that did the trick, no reformatting of the html contents and I can interact with both webviews in parallel.

I am posting this as a big Thank you to Arne for the idea and also to get any inputs for the behavior I saw and my assumptions for it. I actually like the end solution, makes logical sense, but I may be wrong . . . any thoughts and input would be greatly appreciated. Thank you very much.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top