11

我正在尝试创建一个从底部向上滑动的菜单。它从屏幕底部可见的菜单视图开始,然后单击它会使其向上滑动。我尝试使用 a TranslateAnimation,但尽管像素移动,但菜单的命中区域与以前的位置相同。所以我认为如果我可以在动画完成后调整菜单的边距,这将完成我想要的。但是,我不知道如何调整边距。

我尝试创建一个LinearLayout.LayoutMargins对象,然后设置其边距并将其应用于菜单的视图(即 a LinearLayout),但这不起作用。

有任何想法吗?

4

5 回答 5

21

以下对我有用。首先确定菜单的底部边距(下降)是向上(完全可见)还是向下(大部分隐藏)。

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

然后,在 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;
    }
});

秘诀来了;-)

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

我使用两个实用功能:

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

瞧!

于 2012-03-05T15:29:46.837 回答
3

我的解决方案是创建两个线性布局,一个处于向上状态(设置为消失),另一个处于菜单的向下状态。然后,当用户单击按钮向上滑动菜单时,我调用TranslateAnimation显示菜单向上滑动。我在动画上放置了一个监听器,当动画结束时,向上状态可见,向下状态消失。我将其反转为“关闭”动作。

不完全是我最初想象的方式,但它确实有效。

于 2010-03-10T17:47:32.467 回答
3

使用ViewPropertyAnimator

if (view.getY() != margin) {
    view.animate().y(margin).setDuration(150).start();
}
于 2015-05-20T15:16:46.007 回答
2

我觉得这不是一个黑客。基本上,它正在制作自己的动画师。下面的设置仅用于修改topMargina RelativeLayout,但概括起来并不难。

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

}
于 2011-09-30T06:42:21.027 回答
1

这篇文章帮助我让我的动画工作。就我而言,我有一个全屏网络视图。在一些用户操作中,这个 webview 向右滑动,大约 70%,一个新的 webview 从左边出现并占用可用空间。最终结果是,一个新的 webview 覆盖了 70%(x 轴),而旧的 webview 则覆盖了其余部分。Arne 的解决方案极大地帮助了我,在动画完成后立即设置旧视图的边距。伪代码:

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

但是我遇到了一个奇怪的行为,我猜我的旧 webview(现在只占用 30% 的空间)正在重新格式化它的内容,可能认为它现在被挤到了一个更小的空间中,而不是表现得好像它只是滑到了对。换句话说,只要我设置了这个边距,webview 的 html 布局就改变了。同样,我不知道为什么,我的猜测是它认为它的父窗口大小发生了变化。基于这个假设,我又添加了一行代码:

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

这就是诀窍,没有重新格式化 html 内容,我可以同时与两个 webview 交互。

我将此发布为非常感谢 Arne 的想法,并为我看到的行为和我的假设获得任何输入。我实际上喜欢最终解决方案,合乎逻辑,但我可能错了。. . 任何想法和意见将不胜感激。非常感谢你。

于 2012-06-12T17:41:34.763 回答