Почему запуск второго viewpropertyanimation для представления прерывает прослушивание анимации?

У меня есть странная проблема, когда мой onAnimationEnd вызывается неоднократно (поэтому моя анимация продолжает работать снова и снова, хотя я не вызываю ее явно).

Вот запись экрана того, что происходит: https://youtu.be/TfGiLvwLdBM

Ниже приведен мой код:

public class MainActivity extends AppCompatActivity {

private static final String TAG = "TT_MainActivity";

ActivityMainBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    setupViews();
    showLogin();
}

private void setupViews() {
    // Login views
    binding.loginContainer.setTranslationY(Utils.getScreenHeight(this));
    binding.tvNotRegistered.setTranslationY(Utils.getScreenHeight(this));
    binding.tvLoginTitle.setTranslationY(-Utils.dpToPx(500));
    binding.etEmail.setTranslationY(-Utils.dpToPx(500));
    binding.etPassword.setTranslationY(-Utils.dpToPx(500));
    binding.btnLogin.setTranslationY(-Utils.dpToPx(500));

    // Signup views
    binding.signupContainer.setTranslationY(-Utils.getScreenHeight(this));
    binding.tvAlreadyHaveAAccount.setTranslationY(-Utils.getScreenHeight(this));
    binding.tvSignupTitle.setTranslationY(-Utils.dpToPx(500));
    binding.etEmailSignup.setTranslationY(-Utils.dpToPx(500));
    binding.etPasswordSignup.setTranslationY(-Utils.dpToPx(500));
    binding.btnSignup.setTranslationY(-Utils.dpToPx(500));

    // Click listeners
    setOnClickListeners();
}

private void setOnClickListeners() {
    // LOGIN
    binding.btnLogin.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "login()...");
        }
    });

    binding.tvNotRegistered.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "showSignup()..");
            binding.tvNotRegistered.animate()
                    .translationY(Utils.getScreenHeight(MainActivity.this))
                    .setInterpolator(new AccelerateInterpolator());
            binding.loginContainer.animate()
                    .translationY(Utils.getScreenHeight(MainActivity.this))
                    .setInterpolator(new AccelerateInterpolator())
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            Log.d(TAG, "onAnimationEnd: showSignup()");
                            binding.loginContainer.setVisibility(View.GONE);
                            binding.tvNotRegistered.setVisibility(View.GONE);
                            showSignup();
                        }
                    });
        }
    });

    binding.btnSignup.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "signup()...");
        }
    });

    // SIGNUP
    binding.tvAlreadyHaveAAccount.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "showLogin()..");
            binding.tvAlreadyHaveAAccount.animate()
                    .translationY(-Utils.getScreenHeight(MainActivity.this))
                    .setInterpolator(new AccelerateInterpolator());
            binding.signupContainer.animate()
                    .translationY(-Utils.getScreenHeight(MainActivity.this))
                    .setInterpolator(new AccelerateInterpolator())
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            Log.d(TAG, "onAnimationEnd: showLogin()");
                            binding.signupContainer.setVisibility(View.GONE);
                            binding.tvAlreadyHaveAAccount.setVisibility(View.GONE);
                            showLogin();
                        }
                    });
        }
    });
}


// SIGNUP
private void showSignup() {
    binding.signupContainer.setVisibility(View.VISIBLE);
    binding.tvAlreadyHaveAAccount.setVisibility(View.VISIBLE);
    binding.signupContainer.animate()
            .setDuration(300)
            .setInterpolator(new OvershootInterpolator(1.0f))
            .translationY(0)
            .setStartDelay(300);
    binding.tvAlreadyHaveAAccount.animate()
            .setDuration(300)
            .setStartDelay(400)
            .translationY(0)
            .setInterpolator(new OvershootInterpolator(1.0f))
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    animateSignupContainerContents();
                }
            });
}

private void animateSignupContainerContents() {
    binding.tvSignupTitle.animate()
            .translationY(0)
            .setDuration(400)
            .setInterpolator(new DecelerateInterpolator());

    binding.etEmailSignup.animate()
            .translationY(0)
            .setDuration(300)
            .setInterpolator(new DecelerateInterpolator());

    binding.etPasswordSignup.animate()
            .translationY(0)
            .setDuration(200)
            .setInterpolator(new DecelerateInterpolator());

    binding.btnSignup.animate()
            .translationY(0)
            .setDuration(100)
            .setInterpolator(new DecelerateInterpolator());
}

// LOGIN
private void showLogin() {
    binding.loginContainer.setVisibility(View.VISIBLE);
    binding.tvNotRegistered.setVisibility(View.VISIBLE);
    binding.loginContainer.animate()
            .setDuration(300)
            .setInterpolator(new OvershootInterpolator(1.0f))
            .translationY(0)
            .setStartDelay(300);
    binding.tvNotRegistered.animate()
            .setDuration(300)
            .setStartDelay(400)
            .translationY(0)
            .setInterpolator(new OvershootInterpolator(1.0f))
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    animateLoginContainerContents();
                }
            });
}

private void animateLoginContainerContents() {
    binding.tvLoginTitle.animate()
            .translationY(0)
            .setDuration(400)
            .setInterpolator(new DecelerateInterpolator());

    binding.etEmail.animate()
            .translationY(0)
            .setDuration(300)
            .setInterpolator(new DecelerateInterpolator());

    binding.etPassword.animate()
            .translationY(0)
            .setDuration(200)
            .setInterpolator(new DecelerateInterpolator());

    binding.btnLogin.animate()
            .translationY(0)
            .setDuration(100)
            .setInterpolator(new DecelerateInterpolator());
}

}

С соответствующим xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="mango.matts.MainActivity">

<RelativeLayout
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_login_gradient">

    <include layout="@layout/anchors"/>

    <!--Start login-->

    <LinearLayout android:id="@+id/loginContainer"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@android:color/white"
        android:elevation="@dimen/material_dialog_elevation"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginEnd="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/login_container_padding_vertical"
        android:paddingBottom="@dimen/login_container_padding_vertical"
        android:paddingEnd="@dimen/login_container_padding_horizontal"
        android:paddingStart="@dimen/login_container_padding_horizontal"
        android:layout_marginBottom="@dimen/login_container_margin_bottom">

        <TextView android:id="@+id/tvLoginTitle"
            style="@style/MaterialTypography.Regular.Title"
            android:layout_gravity="center_horizontal"
            android:layout_width="@dimen/login_content_width"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:textAlignment="center"
            android:text="@string/login"/>

        <EditText android:id="@+id/etEmail"
            android:inputType="textEmailAddress"
            android:hint="@string/email"
            android:layout_width="match_parent"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:layout_height="wrap_content" />

        <EditText android:id="@+id/etPassword"
            android:inputType="textPassword"
            android:hint="@string/password"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button android:id="@+id/btnLogin"
            android:text="@string/login"
            android:textAllCaps="true"
            style="@style/MaterialTypography.Regular.Button"
            android:textColor="@android:color/white"
            android:backgroundTint="@color/colorAccent"
            android:layout_width="match_parent"
            android:layout_height="@dimen/btn_login_height" />

    </LinearLayout>

    <TextView android:id="@+id/tvNotRegistered"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/loginContainer"
        style="@style/MaterialTypography.Regular"
        android:textColor="@android:color/white"
        android:text="@string/notRegisteredSignup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <!--End login-->
    <!--Start Signup-->
    <LinearLayout android:id="@+id/signupContainer"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@android:color/white"
        android:elevation="@dimen/material_dialog_elevation"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginEnd="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/login_container_padding_vertical"
        android:paddingBottom="@dimen/login_container_padding_vertical"
        android:paddingEnd="@dimen/login_container_padding_horizontal"
        android:paddingStart="@dimen/login_container_padding_horizontal"
        android:layout_marginBottom="@dimen/login_container_margin_bottom">

        <TextView android:id="@+id/tvSignupTitle"
            style="@style/MaterialTypography.Regular.Title"
            android:layout_gravity="center_horizontal"
            android:layout_width="@dimen/login_content_width"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:textAlignment="center"
            android:text="@string/signup"/>

        <EditText android:id="@+id/etEmailSignup"
            android:inputType="textEmailAddress"
            android:hint="@string/email"
            android:layout_width="match_parent"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:layout_height="wrap_content"/>

        <EditText android:id="@+id/etPasswordSignup"
            android:inputType="textPassword"
            android:hint="@string/password"
            android:layout_marginBottom="@dimen/login_text_margin_bottom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button android:id="@+id/btnSignup"
            android:text="@string/signup"
            android:textAllCaps="true"
            style="@style/MaterialTypography.Regular.Button"
            android:textColor="@android:color/white"
            android:backgroundTint="@color/colorAccent"
            android:layout_width="match_parent"
            android:layout_height="@dimen/btn_login_height" />

    </LinearLayout>

    <TextView android:id="@+id/tvAlreadyHaveAAccount"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/signupContainer"
        style="@style/MaterialTypography.Regular"
        android:textColor="@android:color/white"
        android:text="@string/alreadyAMemberLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>


person TomTaila    schedule 28.10.2016    source источник


Ответы (1)


это происходит потому, что вызов animate() всегда возвращает один и тот же объект ViewPropertyAnimator. Это внутренний объект этого конкретного представления.

Я считаю, что по соображениям эффективности нет необходимости создавать новый объект при каждом вызове.

Для нас, разработчиков, это означает, что все параметры, которые мы устанавливаем для этого объекта, сохраняются между вызовами. Таким образом, если вы вызовете setDuration(1234), а позже будет другой вызов анимации, он все равно будет использовать 1234 мс в качестве длительности. То же самое для задержки, интерполятора или setListener.

Таким образом, способ заставить его работать — всегда сбрасывать любые параметры, которые вы не используете для этой анимации. Это означает, что вы должны вызывать .setListener(null) для любой анимации, которая не использует прослушиватель.

Вы также можете создать вспомогательный метод, например:

static ViewPropertyAnimator animate(View view){
    return view.animate()
        .setListener(null)
        .setDuration(DEFAULT_DURATION)
        .setStartDelay(0)
        .setInterpolator(DEFAULT_INTERPOLATOR);
}
person Budius    schedule 28.10.2016