Android - 第一个动画出现的行为不同

Android - first animation occurrence acts differently

我在我的应用程序中创建简单动画时遇到了一些问题,为此苦苦挣扎了几个小时。

我有一个 LinearLayout,其中的元素隐藏在图像视图后面,应该在单击事件时显示(通过在布局宽度上设置动画)。如果我从 LinearLayout 中删除元素,一切都表现良好,但是因为布局的 children 正在推动它的宽度(这意味着即使布局宽度设置为 0,它仍然被推到宽度 +/- 100dp里面的元素)我有隐藏布局的问题。

然而,执行动画时情况并非如此 - 当我将布局设置为宽度 0 时,它会正确裁剪 children。但是一旦动画完成 - 布局就会被 children 推动。我有一个想法,默认情况下将布局裁剪为 children,但我尝试了多种解决方案,其中 none 有效。然后我想也许我会在动画完成后隐藏元素(可见性消失)。它 "kinda" 解决了问题 - 但我仍然遇到第一次出现 "showing" 动画的问题。

我正在做的是 - 一开始我将布局宽度设置为 0 并将其与其 children 一起隐藏(将其设置为可见性消失)。然后在 "showing" 动画上,我正在制作布局,它的 children 在 onAnimationStart 方法中可见,然后动画将布局宽度从 0 增加到 156dp。 "Hiding" 动画已还原 - 我正在将布局宽度减小到 0 并且 onAnimationEnd 我正在隐藏布局及其内容。问题是由于某种原因,第一次显示动画被调用并且我在 onAnimationStart 中使布局和视图可见,这段代码与动画实际开始的时刻之间存在差距,这使得视图在一瞬间可见动画开始。

这是它的样子: hidden, shown。这是它的 xml 代码:

<LinearLayout
    android:id="@+id/linSettingsPopup"
    android:layout_width="156dp"
    android:layout_height="56dp"
    app:layout_constraintTop_toTopOf="@id/imgProfilePicture"
    app:layout_constraintBottom_toBottomOf="@id/imgProfilePicture"
    app:layout_constraintRight_toRightOf="@id/imgProfilePicture"
    android:layout_marginEnd="24dp"
    android:background="@drawable/settings_popup_container"
    android:orientation="horizontal"
    android:gravity="center|start"
    android:visibility="visible">

    <ImageButton
        android:id="@+id/btnLogout"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="22dp"
        android:background="@drawable/logout"
        android:visibility="visible">

    </ImageButton>

    <ImageButton
        android:id="@+id/btnSettings"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:background="@drawable/settings"
        android:visibility="visible">

    </ImageButton>

</LinearLayout>

为了更好地理解正在发生的事情,这里是所有内容的代码。 在onCreate方法中设置视图(当然此时视图已经赋值给变量):

private void initializeSettingsPopup()
{
    settingsPopupWidth = linSettingsPopup.getLayoutParams().width;
    linSettingsPopup.getLayoutParams().width = 0;
    linSettingsPopup.requestLayout();
    btnLogout.setVisibility(View.GONE);
    btnSettings.setVisibility(View.GONE);
    linSettingsPopup.setVisibility(View.GONE);
}

动画:

public class ResizeWidthAnimation extends Animation {
private int mWidth;
private int mStartWidth;
private View mView;

public ResizeWidthAnimation(View view, int width) {
    mView = view;
    mWidth = width;
    mStartWidth = view.getWidth();
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    int newWidth = mStartWidth + (int) ((mWidth - mStartWidth) * interpolatedTime);

    mView.getLayoutParams().width = newWidth;
    mView.requestLayout();
}

@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
    super.initialize(width, height, parentWidth, parentHeight);
}

@Override
public boolean willChangeBounds() {
    return true;
}

}

调用动画:

private void showSettingsPopup()
{
    linSettingsPopup.setVisibility(View.INVISIBLE);
    settingsPopupShown = true;
    ResizeWidthAnimation resizeAnimation = new ResizeWidthAnimation(linSettingsPopup, settingsPopupWidth);
    resizeAnimation.setDuration(350);
    resizeAnimation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
            linSettingsPopup.setVisibility(View.VISIBLE);
            btnLogout.setVisibility(View.VISIBLE);
            btnSettings.setVisibility(View.VISIBLE);
        }

    });
    linSettingsPopup.startAnimation(resizeAnimation);
}

private void hideSettingsPopup()
{
    settingsPopupShown = false;
    ResizeWidthAnimation resizeAnimation = new ResizeWidthAnimation(linSettingsPopup, 0);
    resizeAnimation.setDuration(350);
    resizeAnimation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationEnd(Animation animation) {
            linSettingsPopup.setVisibility(View.GONE);
            btnLogout.setVisibility(View.GONE);
            btnSettings.setVisibility(View.GONE);
        }
    });
    linSettingsPopup.startAnimation(resizeAnimation);
}

我还要补充一点,我设法通过稍微延迟 "showing" 动画的 onAnimationStart 方法中的代码来解决这个问题。然而,感觉更像是在避免问题而不是解决问题,所以我希望有人能指出我做错了什么,并知道我该如何解决这个问题。

过了一段时间我终于找到了一个不需要对视图可见性进行任何操作的"solution"。事实证明,如果布局宽度为 0dp,内部的视图将继续推动它,这使得布局的宽度达到适合其子项的要求。但是,如果我将布局宽度设置为 1dp,它实际上会剪切其中的视图。因此,我没有在隐藏时将布局宽度从 156dp 动画化到 0dp,而是将它从 156dp 动画化到 1dp,它工作正常,没有任何问题。这是它的工作代码:

private static final int SETTINGS_POPUP_ANIM_DURATION = 350;
private static final int SETTINGS_POPUP_HIDDEN_WIDTH = 1;

private void initializeSettingsPopup()
{
    settingsPopupWidth = linSettingsPopup.getLayoutParams().width;
    linSettingsPopup.getLayoutParams().width = SETTINGS_POPUP_HIDDEN_WIDTH;
    linSettingsPopup.requestLayout();
}

private void showSettingsPopup()
{
    settingsPopupShown = true;
    ResizeWidthAnimation resizeAnimation = new ResizeWidthAnimation(linSettingsPopup, settingsPopupWidth);
    resizeAnimation.setDuration(SETTINGS_POPUP_ANIM_DURATION);
    linSettingsPopup.startAnimation(resizeAnimation);
}

private void hideSettingsPopup()
{
    settingsPopupShown = false;
    ResizeWidthAnimation resizeAnimation = new ResizeWidthAnimation(linSettingsPopup, SETTINGS_POPUP_HIDDEN_WIDTH);
    resizeAnimation.setDuration(SETTINGS_POPUP_ANIM_DURATION);
    linSettingsPopup.startAnimation(resizeAnimation);
}