BottomSheetDialogFragment实现弹出和消失自定义Spring动画

541 阅读2分钟

想要给BottomSheetDialogFragment的弹出和消失加spring动画

1、遇到的问题

由于是spring的动效,无法使用下面设置windowAnimations的方法来

@Override
public void onActivityCreated(Bundle arg0) {
    super.onActivityCreated(arg0);
    getDialog().getWindow()
    .getAttributes().windowAnimations = R.style.DialogAnimation;
}
<style name="DialogAnimation">
    <item name="android:windowEnterAnimation">@anim/slide_up</item>
    <item name="android:windowExitAnimation">@anim/slide_down</item></style>

无法在xml中使用spring的插值器。

2、解决方式

只能通过动态的方式来设置动效

animator.setInterpolator(new SpringInterpolator(4f));
animator.setDuration(407);

1、设置弹出动效

可以在bottomSheetDialogFragment中获取dialog然后对dialog设置如下监听:

dialog?.setOnShowListener {
when (animationStyle()) {
        AnimationStyle.Low -> {
            showAnimal(SHOW_LOW_TIME)
        }
        AnimationStyle.Height -> {
            showAnimal(SHOW_LONG_TIME)
        }
        AnimationStyle.Middle -> {
            showAnimal(SHOW_MIDDLE_TIME)
        }
    }
} 

Show animal动效

private fun showAnimal(time: Long) {
    root?.let {
it.alpha = 0f
        it.translationY = it.height.toFloat() - ScreenUtils.getNavigationBarHeight(activity)
        it.animate().alpha(1f).translationY(0f).setDuration(time)
            .setInterpolator(SpringInterpolator(4f))
            .start()
    }
}

2、设置消失动画

由于BottomSheetDialogFragment的消失事件可能是cancel导致的也可能第dismiss方法调用导致的,并且在调试过程中发现当调用BottomSheetDialogFragment的onDismiss时弹框已经消失,所以不能在BottomSheetDialogFragment中通过设置监听和override ondismiss等方法来重新动画。

debug的时候发现BottomSheetDialogFragment消失时,都会调用到BottomSheetDialog中的dismiss方法,所以可以通过重写BottomSheetDialog的dismiss方法来实现消失动效。

1、如何在BottomSheetDialogFragment中重写BottomSheetDialog的dismiss方法?

自己创建BottomSheetDialogFragment中的BottomSheetDialog

通过查看BottomSheetDialogFragment代码可以发现BottomSheetDialog是

public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
  return new BottomSheetDialog(getContext(), getTheme());
}

创建的,所以可以重写这个方法来创建BottomSheetDialog

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    //这个地方创建自己的dialog
    return AnimalBottomSheetDialog(requireContext(), theme,animationStyle())
}

其中AnimalBottomSheetDialog继承BottomSheetDialog,并且重写了dismiss方法

override fun dismiss() {
    //添加动画,做完动画之后再调用super的dismiss
    dismissAnimal(time) {
super.dismiss()
    }
}

private fun dismissAnimal(time: Long, callback: () -> Unit) {
    root?.let {
height = sheet?.height?.toFloat() ?: 0f
        LogDelegator.d(
            TAG,
            "translationY ${it.translationY} height ${it.height} height1 $height"
        )
        it.animate().alpha(0f).translationY(height).setDuration(time)
            .setInterpolator(SpringInterpolator(4f)).withEndAction {
callback.invoke()
            } .start()
    }
}
问题

主动调用BottomSheetFragment的dismiss或者dismissAllowingStateLoss方法时,会调用到DialogFragment的到dismissInternal中:

mDialog.dismiss();
    if (!fromOnDismiss) {
        // onDismiss() is always called on the main thread, so
        // we mimic that behavior here. The difference here is that
        // we don't post the message to ensure that the onDismiss()
        // callback still happens before onDestroy()
        if (Looper.myLooper() == mHandler.getLooper()) {
            onDismiss(mDialog);
        } else {
            mHandler.post(mDismissRunnable);
        }
    }
}
mViewDestroyed = true;
if (mBackStackId >= 0) {
    getParentFragmentManager().popBackStack(mBackStackId,
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    mBackStackId = -1;
} else {
    FragmentTransaction ft = getParentFragmentManager().beginTransaction();
    ft.remove(this);
    if (allowStateLoss) {
        ft.commitAllowingStateLoss();
    } else {
        ft.commit();
    }
}

会调用到BottomSheetDialogFragment的onStop()方法,DialogFragment的onStop方法如下

public void onStop() {
    super.onStop();
    if (mDialog != null) {
        mDialog.hide();
    }
}

由于onStop中调用了mDialog.hide()方法,导致dismiss方法中动画没做完就消失了,所以这里动效会有问题。

解决方法

重写RoundTopBottomSheetDialogFragment的dismiss和dismissAllowingStateLoss方法如下

override fun dismiss() {
    //做完动画再调用这个方法
    dismissAnimal(getAnimalTime()) {
super.dismiss()
    }
}
override fun dismissAllowingStateLoss() {
    dismissAnimal(getAnimalTime()) {
super.dismissAllowingStateLoss()
    }
}