ConstraintLayout Slide Transition 动画在动画结束前显示视图的全高
Posted
技术标签:
【中文标题】ConstraintLayout Slide Transition 动画在动画结束前显示视图的全高【英文标题】:ConstraintLayout Slide Transition animation is displaying the view's full height before the animation ends 【发布时间】:2021-06-07 08:46:17 【问题描述】:我有以下活动和布局:
import android.os.Bundle
import android.view.Gravity
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.transition.Slide
import androidx.transition.TransitionManager
import com.tests.playground.R
class ConstraintLayoutTransitionActivity : AppCompatActivity()
private val rootView: ConstraintLayout by lazy findViewById(R.id.root)
private val bottomBlueView: View by lazy findViewById(R.id.bottomBlue)
private val switchButton: View by lazy findViewById(R.id.switchButton)
private var isBottomBlueViewShowing = true
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_constraint_layout_transition)
switchButton.setOnClickListener(::onSwitchButtonClicked)
@Synchronized
private fun onSwitchButtonClicked(view: View)
isBottomBlueViewShowing = !isBottomBlueViewShowing
val constraintSet = ConstraintSet()
constraintSet.clone(rootView)
val transition = Slide()
transition.duration = 1000L
if (isBottomBlueViewShowing)
constraintSet.setVisibility(bottomBlueView.id, View.VISIBLE)
transition.slideEdge = Gravity.BOTTOM
else
constraintSet.setVisibility(bottomBlueView.id, View.GONE)
transition.slideEdge = Gravity.BOTTOM
TransitionManager.beginDelayedTransition(rootView, transition)
constraintSet.applyTo(rootView)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_
android:layout_>
<View
android:id="@+id/topRed"
android:layout_
android:layout_
android:background="#F00"
app:layout_constraintBottom_toTopOf="@+id/bottomBlue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/switchButton"
android:layout_
android:layout_
android:padding="16dp"
android:text="Switch"
app:layout_constraintBottom_toBottomOf="@+id/topRed"
app:layout_constraintEnd_toEndOf="@+id/topRed"
app:layout_constraintStart_toStartOf="@+id/topRed"
app:layout_constraintTop_toTopOf="@+id/topRed" />
<View
android:id="@+id/bottomBlue"
android:layout_
android:layout_
android:background="#00F"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
当我尝试显示/隐藏底部蓝色时,会发生以下情况:
如你所见,
当我隐藏视图时,按钮会直接向下而没有动画(bottomBlueView 动画没问题; 当我显示视图时,按钮也会直接向上而不显示动画。但最让我烦恼的是,在动画开始之前会显示一个白色区域并一直停留在那里直到结束。有没有办法解决这些动画问题?
我已经尝试了很多方法(使用ConstraintSet
和Animation
),但都没有按预期工作。
【问题讨论】:
【参考方案1】:您可以使用AutoTransition
进行转换。
TransitionManager.beginDelayedTransition(rootView, AutoTransition())
结果会是这样的:
AutoTransition
使用Fade 过渡来改变蓝色区域的可见性。
如果您需要蓝色区域来滑动,您应该提供自定义过渡。红色区域和蓝色按钮应为ChangeBounds
过渡,蓝色区域应为Slide
过渡:
private val topRed: View by lazy findViewById(R.id.topRed)
private fun onSwitchButtonClicked(view: View)
isBottomBlueViewShowing = !isBottomBlueViewShowing
val transitionSet = TransitionSet()
transitionSet.ordering = TransitionSet.ORDERING_TOGETHER
//blue btn
val blueBtnTransition = Slide(Gravity.BOTTOM)
blueBtnTransition.addTarget(bottomBlueView)
//red area + switch btn
val topRedTransition = ChangeBounds()
topRedTransition.addTarget(topRed)
topRedTransition.addTarget(switchButton)
transitionSet.addTransition(blueBtnTransition)
transitionSet.addTransition(topRedTransition)
val blueBtnVisibility = if (isBottomBlueViewShowing)
View.VISIBLE
else
View.GONE
val constraintSet = ConstraintSet()
constraintSet.clone(rootView)
constraintSet.setVisibility(bottomBlueView.id, blueBtnVisibility)
TransitionManager.beginDelayedTransition(rootView, transitionSet)
constraintSet.applyTo(rootView)
结果如下:
如您所见,动画时红色和蓝色区域之间仍有白色区域。您可以通过在转换中添加延迟来解决它:
blueBtnTransition.startDelay = if (isBottomBlueViewShowing) 0 else 100
topRedTransition.startDelay = if (isBottomBlueViewShowing) 100 else 0
这是结果。恕我直言,延迟看起来好多了:
【讨论】:
非常感谢您的详细解释。我刚刚在模拟器和物理设备上尝试过,但我得到的结果就像第二个 gif 一样。你还记得你是否做过其他事情吗?再次感谢^^ 您需要将LinearInterpolator
设置为蓝色和红色过渡,只有在此之后它们的边界边缘才会准确地动画,没有视觉上的白色间隙。但这里的问题是Slide
转换使用AccelerateInterpolator
/DecelerateInterpolator
进行隐藏/显示动画,正如我从源代码中看到的那样,您无法通过setInterpolator
方法更改该行为。这里最好的解决方案是编写自定义的Slide
转换,它将支持LinearInterpolator
,或者尝试通过延迟、不同的动画持续时间等来隐藏这个差距以上是关于ConstraintLayout Slide Transition 动画在动画结束前显示视图的全高的主要内容,如果未能解决你的问题,请参考以下文章
android.view.InflateException:膨胀类 androidx.constraintlayout.widget.ConstraintLayout 时出错
ConstraintLayout系列:ConstraintLayout实现局部垂直居中