实现单个FrameLayout滑出、两个FrameLayout滑入的动画切换咨询
Hey, I’ve worked on similar bottom menu animation scenarios before, and here are two reliable approaches to get the slide-out/slide-in effect you need for your three FrameLayouts. Let’s break them down:
Option 1: Property Animators (ValueAnimator)
This gives you precise control over each view’s movement, perfect for customizing the animation flow.
Step 1: Initialize Menu States
First, set up the initial visibility and position in your onCreate (or onViewCreated for fragments). We’ll hide the secondary and tertiary menus off-screen below the bottom edge:
// Kotlin example; Java works the same way val primaryMenu = findViewById<FrameLayout>(R.id.primaryMenu) val secondaryMenu = findViewById<FrameLayout>(R.id.secondaryMenu) val tertiaryMenu = findViewById<FrameLayout>(R.id.tertiaryMenu) // Wait for views to finish measuring before setting positions primaryMenu.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { primaryMenu.viewTreeObserver.removeOnGlobalLayoutListener(this) // Push secondary/tertiary below the screen initially secondaryMenu.translationY = secondaryMenu.height.toFloat() tertiaryMenu.translationY = tertiaryMenu.height.toFloat() secondaryMenu.visibility = View.INVISIBLE tertiaryMenu.visibility = View.INVISIBLE } })
Step 2: Implement the Switch Animation
Create a function to handle the transition when a menu item is clicked. This will slide the current menu down off-screen, then slide the other two up into view:
fun switchToSecondaryAndTertiary() { val primaryMenu = findViewById<FrameLayout>(R.id.primaryMenu) val secondaryMenu = findViewById<FrameLayout>(R.id.secondaryMenu) val tertiaryMenu = findViewById<FrameLayout>(R.id.tertiaryMenu) val animDuration = 300L // Adjust speed as needed // Slide primary menu down out of view ValueAnimator.ofFloat(0f, primaryMenu.height.toFloat()).apply { addUpdateListener { anim -> primaryMenu.translationY = anim.animatedValue as Float } addListener(object : Animator.AnimatorListener { override fun onAnimationStart(anim: Animator?) {} override fun onAnimationEnd(anim: Animator?) { primaryMenu.visibility = View.INVISIBLE } override fun onAnimationCancel(anim: Animator?) {} override fun onAnimationRepeat(anim: Animator?) {} }) duration = animDuration interpolator = AccelerateDecelerateInterpolator() // Smooth start/end start() } // Slide secondary menu up into view secondaryMenu.visibility = View.VISIBLE ValueAnimator.ofFloat(secondaryMenu.height.toFloat(), 0f).apply { addUpdateListener { anim -> secondaryMenu.translationY = anim.animatedValue as Float } duration = animDuration interpolator = AccelerateDecelerateInterpolator() start() } // Slide tertiary menu up into view tertiaryMenu.visibility = View.VISIBLE ValueAnimator.ofFloat(tertiaryMenu.height.toFloat(), 0f).apply { addUpdateListener { anim -> tertiaryMenu.translationY = anim.animatedValue as Float } duration = animDuration interpolator = AccelerateDecelerateInterpolator() start() } }
To switch back, just reverse the animations: slide secondary/tertiary down, and primary back up into view.
Option 2: Android Transition API (Cleaner & More Declarative)
The Transition API handles view visibility changes with built-in animations, making your code more concise.
Step 1: Define Transitions
Create slide-out and slide-in transitions targeted at your menus:
// Slide current menu down out of the screen val slideOutDown = Slide(Gravity.BOTTOM).apply { duration = 300L addTarget(R.id.primaryMenu) interpolator = AccelerateDecelerateInterpolator() } // Slide secondary/tertiary menus up from below the screen val slideInUp = Slide(Gravity.BOTTOM).apply { duration = 300L mode = Slide.MODE_IN // Specify this is an "in" animation addTarget(R.id.secondaryMenu, R.id.tertiaryMenu) interpolator = AccelerateDecelerateInterpolator() }
Step 2: Trigger the Transition
Use TransitionManager to animate the visibility change:
fun switchMenus() { val primaryMenu = findViewById<FrameLayout>(R.id.primaryMenu) val secondaryMenu = findViewById<FrameLayout>(R.id.secondaryMenu) val tertiaryMenu = findViewById<FrameLayout>(R.id.tertiaryMenu) // Begin transition on the parent layout (control_area) TransitionManager.beginDelayedTransition(findViewById(R.id.control_area), TransitionSet().apply { addTransition(slideOutDown) addTransition(slideInUp) }) // Toggle visibility - the transition will handle the animation primaryMenu.visibility = View.INVISIBLE secondaryMenu.visibility = View.VISIBLE tertiaryMenu.visibility = View.VISIBLE }
Key Notes
- View Measurement: Always wait for views to finish measuring before getting their height (using
ViewTreeObserver), otherwise you’ll get a height of 0 initially. - Interpolators: Using
AccelerateDecelerateInterpolatormakes the animation feel more natural instead of a linear movement. - State Tracking: Keep a variable to track which menus are currently visible so you don’t trigger incorrect animations when switching back and forth.
Either of these approaches will give you the smooth slide transition you’re looking for. Pick the one that fits your coding style best!
内容的提问来源于stack exchange,提问作者milan kumar

