简体   繁体   English

Android - 在LinearLayout或RelativeLayout中为View的topMargin / bottomMargin / etc设置动画

[英]Android - Animate a View's topMargin/bottomMargin/etc in LinearLayout or RelativeLayout

I am trying to create a menu that slides up from the bottom. 我正在尝试创建一个从底部向上滑动的菜单。 It starts with the menu's view just visible at the bottom of the screen, and then clicking it causes it to slide up. 它从菜单的视图开始,只在屏幕底部可见,然后单击它使其向上滑动。 I tried using a TranslateAnimation , but although the pixels move, the hit areas of the menu are in the same position as before. 我尝试使用TranslateAnimation ,但是虽然像素移动,但菜单的命中区域与之前的位置相同。 So I think that if I can adjust the menu's margins after the animation is complete, this will accomplish what I want. 所以我认为如果我可以在动画完成后调整菜单的边距,这将完成我想要的。 However, I can't figure out how to adjust the margins. 但是,我无法弄清楚如何调整边距。

I've tried to create a LinearLayout.LayoutMargins object and then set its margins and apply it to the menu's view (which is a LinearLayout ), but this doesn't work. 我试图创建一个LinearLayout.LayoutMargins对象,然后设置其边距并将其应用到菜单的视图(这是一个LinearLayout ),但这不起作用。

Any ideas? 有任何想法吗?

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(): 然后,在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! 瞧!

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. 我的解决方案是创建两个LinearLayouts,一个处于up状态(设置为消失),另一个处于菜单的down状态。 Then when the user clicks on the button to slide the menu up, I call a TranslateAnimation showing the menu slide up. 然后当用户点击按钮向上滑动菜单时,我调用TranslateAnimation显示菜单向上滑动。 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. 我在动画上放置了一个监听器,使得up状态可见,当动画结束时,down状态消失。 I reversed this for the "closing" action. 为了“结束”行动,我改变了这一点。

Not exactly the way I had originally imagined doing it, but it worked. 不完全是我原先想象的方式,但它确实有效。

Use the ViewPropertyAnimator : 使用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. 在它下面的设置只修改topMarginRelativeLayout ,但它不会是很难一概而论的。

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. 就我而言,我有一个全屏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. 在某些用户操作中,此Web视图向右滑动,大约70%,并从左侧显示新的Web视图并占用可用空间。 End result, a new webview covers the 70% (x-axis) and the old webview the rest. 最终结果是,新的webview覆盖了70%(x轴)和其余的旧webview。 Arne's solution helped me immensely, setting up the margin to the old view as soon as the animation is done. Arne的解决方案极大地帮助了我,一旦动画完成就设置了旧视图的边距。 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. 但是我遇到了一个奇怪的行为,我想我的旧webview(现在只占30%的空间),正在重新格式化它的内容,可能会认为它现在被挤进一个较小的空间,而不是表现得好像它只是滑到了对。 In other words, as soon as I set this margin, the html layout of the webview changed. 换句话说,一旦我设置了这个边距,webview的html布局就会改变。 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. 这就是诀窍,没有重新格式化html内容,我可以同时与两个webview进行交互。

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. 我发布这篇文章是一个很大的感谢Arne的想法,也为我看到的行为和我的假设得到任何输入。 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. 非常感谢你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM