Android—过渡按钮的简单实现
- 前言
- 准备工作
- 登录页面(activity_main.xml)
- 登录成功页面(activity_new.xml)
- 主要代码
- 给登录按钮设置监听事件(MainActivity.xml)
- 点击登录按钮出现加载动画(TransitionButton.java)
- 当isSuccessful判断为true时(MainActivity.xml)
- 加载动画结束时切入跳转新页面动画(TransitionButton.java)
- 当isSuccessful判断为false时(MainActivity.xml)
- 加载动画结束时按钮复原并切入抖动动画(TransitionButton.java)
- 效果展示
- 登录成功
- 登录失败
前言
Android 包含过渡框架,它使开发者能够轻松地为两个视图层次结构之间的变化设置动画。该框架通过随时间更改视图的某些属性值,在运行时为视图设置动画。该框架包括用于常见效果的内置动画,并允许开发者创建自定义动画和过渡生命周期回调。本项目基于此实现了一个过渡按钮。
准备工作
登录页面(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="#00BCD4"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginStart="32dp"
android:layout_marginTop="80dp"
android:layout_marginEnd="32dp"
android:background="@drawable/rounded_background"
android:hint="@string/main_email_hint"
android:paddingLeft="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:background="@drawable/rounded_background"
android:hint="@string/main_password_hint"
android:paddingLeft="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />
<Button
android:id="@+id/transition_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:text="@string/main_button"
android:textColor="@android:color/white"
app:defaultColor="#FF5722"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText2"
app:loaderColor="@android:color/white" />
</android.support.constraint.ConstraintLayout>
登录成功页面(activity_new.xml)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3894ba"
tools:context=".NewActivity">
</android.support.constraint.ConstraintLayout>
主要代码
给登录按钮设置监听事件(MainActivity.xml)
transitionButton = findViewById(R.id.transition_button);
transitionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
transitionButton.startAnimation();
点击登录按钮出现加载动画(TransitionButton.java)
public void startAnimation() {
currentState = State.PROGRESS;
isMorphingInProgress = true;
initialWidth = getWidth();
initialHeight = getHeight();
initialText = getText().toString();
setText(null);
setClickable(false);
startWidthAnimation(initialHeight, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationCancel(animation);
isMorphingInProgress = false;
}
});
}
private void startWidthAnimation(int to, AnimatorListenerAdapter onAnimationEnd) {
startWidthAnimation(getWidth(), to, onAnimationEnd);
}
private void startWidthAnimation(int from, int to, AnimatorListenerAdapter onAnimationEnd) {
ValueAnimator widthAnimation = ValueAnimator.ofInt(from, to);
widthAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = val;
setLayoutParams(layoutParams);
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(WIDTH_ANIMATION_DURATION);
animatorSet.playTogether(widthAnimation);
if (onAnimationEnd != null)
animatorSet.addListener(onAnimationEnd);
animatorSet.start();
}
ValueAnimator.ofInt():由于值的变化手动对对象的属性赋值导致对象的属性发生改变,从而实现动画效果,也就是说ValueAnimator只关心数值如何发生改变的过程
setDuration(WIDTH_ANIMATION_DURATION):设置每个内部子动画的时长,默认每个子动画使用自己默认的时长,如果AnimatorSet设置了时长,子动画将继承这个时长,而子动画自己设置的duration将失效
playTogether(Animator… items):添加一组动画,播放顺序为一起播放
当isSuccessful判断为true时(MainActivity.xml)
transitionButton = findViewById(R.id.transition_button);
transitionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
transitionButton.startAnimation();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
boolean isSuccessful = true;
if (isSuccessful) {
transitionButton.stopAnimation(TransitionButton.StopAnimationStyle.EXPAND, new TransitionButton.OnAnimationStopEndListener() {
@Override
public void onAnimationStopEnd() {
Intent intent = new Intent(getBaseContext(), NewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
}
});
}
加载动画结束时切入跳转新页面动画(TransitionButton.java)
case EXPAND:
currentState = State.TRANSITION;
startScaleAnimation(new AnimationListenerAdapter() {
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
if (onAnimationStopEndListener != null)
onAnimationStopEndListener.onAnimationStopEnd();
}
});
break;
private void startScaleAnimation(Animation.AnimationListener animationListener) {
float ts = (float) (WindowUtils.getHeight(getContext()) / getHeight() * 2.1);
Animation anim = new ScaleAnimation(1f, ts,
1, ts,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(SCALE_ANIMATION_DURATION);
anim.setFillAfter(true);
anim.setAnimationListener(animationListener);
startAnimation(anim);
}
Animation.RELATIVE_TO_SELF, 0.5f:X轴移动的结束位置
setDuration(long durationMillis):设置动画持续事件(单位:毫秒)
setFillAfter(boolean fillAfter):如果fillAfter设为true,则动画执行后,控件将停留在动画结束的状态
setAnimationListener(animationListener):监听动画执行
startAnimation(anim):动画开始执行
当isSuccessful判断为false时(MainActivity.xml)
else {
transitionButton.stopAnimation(TransitionButton.StopAnimationStyle.SHAKE, null);
}
}
}, 2000);
}
加载动画结束时按钮复原并切入抖动动画(TransitionButton.java)
public void stopAnimation(StopAnimationStyle stopAnimationStyle, final OnAnimationStopEndListener onAnimationStopEndListener) {
switch (stopAnimationStyle) {
case SHAKE:
currentState = State.ERROR;
startWidthAnimation(initialWidth, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setText(initialText);
startShakeAnimation(new AnimationListenerAdapter() {
@Override
public void onAnimationEnd(Animation animation) {
currentState = State.IDLE;
setClickable(true);
if (onAnimationStopEndListener != null)
onAnimationStopEndListener.onAnimationStopEnd();
}
});
}
});
break;
private void startWidthAnimation(int to, AnimatorListenerAdapter onAnimationEnd) {
startWidthAnimation(getWidth(), to, onAnimationEnd);
}
private void startWidthAnimation(int from, int to, AnimatorListenerAdapter onAnimationEnd) {
ValueAnimator widthAnimation = ValueAnimator.ofInt(from, to);
widthAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = val;
setLayoutParams(layoutParams);
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(WIDTH_ANIMATION_DURATION);
animatorSet.playTogether(widthAnimation);
if (onAnimationEnd != null)
animatorSet.addListener(onAnimationEnd);
animatorSet.start();
}
private void startShakeAnimation(Animation.AnimationListener animationListener) {
TranslateAnimation shake = new TranslateAnimation(0, 15, 0, 0);
shake.setDuration(SHAKE_ANIMATION_DURATION);
shake.setInterpolator(new CycleInterpolator(4));
shake.setAnimationListener(animationListener);
startAnimation(shake);
}
setDuration(long durationMillis):设置动画持续事件(单位:毫秒)
setAnimationListener(animationListener):监听动画执行
setInterpolator(Interpolator i):设置动画的变化速度
startAnimation(anim):动画开始执行
效果展示
登录成功
登录失败
作者:李海
原文链接:https://blog.csdn.net/Racdex/article/details/128112563?spm=1001.2014.3001.5502