Почему запуск второго 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>
1 ответ
Это происходит потому, что вызов animate()
возвращается всегда одинаково ViewPropertyAnimator
объект. Это внутренний объект этого конкретного взгляда.
Я полагаю, что это так по соображениям эффективности, нет необходимости создавать новый объект при каждом вызове.
Для нас, разработчиков, это означает, что все параметры, которые мы устанавливаем для этого объекта, сохраняются между вызовами. Так что если вы позвоните setDuration(1234)
и позже есть еще один вызов для анимации, он все равно будет использовать 1234ms в качестве продолжительности. То же самое для задержки, интерполятора или setListener
,
Таким образом, способ заставить это работать - всегда сбрасывать любые параметры, которые вы не используете для этой анимации. Значит, тебе следует позвонить .setListener(null)
к любой анимации, которая не использует слушателя.
Вы также можете создать вспомогательный метод, такой как:
static ViewPropertyAnimator animate(View view){
return view.animate()
.setListener(null)
.setDuration(DEFAULT_DURATION)
.setStartDelay(0)
.setInterpolator(DEFAULT_INTERPOLATOR);
}