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

Posted

技术标签:

【中文标题】Android - 第一个动画出现的行为不同【英文标题】:Android - first animation occurrence acts differently 【发布时间】:2020-09-07 20:32:49 【问题描述】:

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

我有一个 LinearLayout,其中包含隐藏在 Image View 后面的元素,并且应该在 click 事件上显示(通过在布局宽度上设置动画)。如果我从 LinearLayout 内部删除元素,一切都运行良好,但是因为布局的子级正在推动其宽度(这意味着即使布局宽度设置为 0,它仍会被内部元素推至宽度 +/- 100dp)我在隐藏布局时遇到问题。

但是,执行动画时并非如此 - 当我将布局设置为宽度为 0 时,它会正确裁剪子项。但是一旦动画完成 - 布局由孩子们推动。我有一个想法让布局默认裁剪它的孩子,但我尝试了多种解决方案,但都没有奏效。然后我想也许我会在动画完成后隐藏元素(可见性消失)。它“有点”解决了这个问题 - 但我仍然遇到第一次出现“显示”动画的问题。

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

这是它的外观: hidden,shown。这是它的xml代码:

<LinearLayout
    android:id="@+id/linSettingsPopup"
    android:layout_
    android:layout_
    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_
        android:layout_
        android:layout_marginStart="24dp"
        android:layout_marginEnd="22dp"
        android:background="@drawable/logout"
        android:visibility="visible">

    </ImageButton>

    <ImageButton
        android:id="@+id/btnSettings"
        android:layout_
        android:layout_
        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);

我还要补充一点,我设法通过延迟一点“显示”动画的 onAnimationStart 方法中的代码来解决这个问题。但是感觉更像是避免问题而不是解决问题,所以我希望有人能指出我做错了什么,并有一些好主意我可以如何解决这个问题。

【问题讨论】:

【参考方案1】:

一段时间后,我终于找到了一个不需要对视图可见性进行任何操作的“解决方案”。事实证明,如果布局宽度为 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);

【讨论】:

以上是关于Android - 第一个动画出现的行为不同的主要内容,如果未能解决你的问题,请参考以下文章

使用 [UIView animateKeyframesWithDuration] 为 UIView 设置动画的奇怪行为

Android ListView 项目动画 - 仅动画第一个可见项目

Android——浅析Activity过渡动画

基本 jQuery 动画:省略号(三个点依次出现)

SVG的JQuery函数,一旦第一个动画完成就执行第二个动画?

JavaFX Glitches 动画非标准小部件