我有ScrollView
,其中包含多个RelativeLayouts
和LinearLayouts
,如下所示:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<RelativeLayout >
</RelativeLayout>
<LinearLayout >
</LinearLayout>
</ScrollView>
现在,当我点击其中一个布局时,我希望它能够扩展并显示更多信息。我设法通过使用PropertyAnimator
:
relativeLayout.animate().scaleY(100).setDuration(duration).start();
同时,我使用另一个PropertyAnimator
将Views
移动到垂直展开的Views
之下,以便为展开的布局留出足够的空间。到目前为止它正在运作。
不幸的是,ScrollView
以某种方式移动到Views
的视口之外,所以我无法向下滚动并查看android:fillViewPort="true"
中的信息。从本质上讲,这些视图的垂直平移使其下部无法访问,因为视口也不会扩展。
我在ScrollView
上设置了setFillViewPort()
。我也尝试用{{1}}以编程方式进行,但都没有任何效果。
出了什么问题?为什么不起作用?
答案 0 :(得分:5)
当您在Views
上执行翻译动画时,那些Views
实际上并未在布局中移动。它只是为用户提供视觉效果,但在布局和/或测量时,忽略任何翻译值。始终好像Views
根本没有翻译。
我猜你现在正在做的是:
View
。 View
。Views
上的翻译动画。Views
离开了屏幕。这种方法实际上永远不会奏效。您始终需要记住,布局中的Views
位置由布局确定只是。您的所有翻译基本上只是为了展示。因此,当您尝试执行上述操作时,这就是实际发生的事情:
View
。 Views
已经位于扩展后的位置,因此翻译动画只会将Views
进一步向下移动,超出它们应该的位置。Views
似乎没有明显的理由离开屏幕。那么你能做些什么呢?基本上你需要以相反的方式解决这个问题。正如我上面提到的,Android已经为您计算了所有Views
的新大小和位置,您可以利用它来获得优势。
您的问题有两种基本解决方案。您可以让Android使用LayoutTransitions
为您执行动画,也可以手动执行动画。两种方式都使用称为ViewTreeObserver
的东西。它可用于监听布局或新绘图过程中的更改。
但首要的是:ScrollView
应该只与一个孩子一起工作。因此,为了防止将来出现任何错误或问题,请将ScrollView
内的所有项目放在另一个LinearLayout
的垂直方向内。
这仅适用于API Level 16及更高版本。在API级别16下方,可以自动处理可见性动画和翻译动画,但要获得动画高度更改,您需要具有API级别16。
我必须提到的一件重要事情是:
LayoutTransition
为您设置动画效果。因此,如果您使用它,您可以删除所有自定义动画。如果您将自己的动画留在自己中,则只会与LayoutTransition
执行的动画产生冲突。 LayoutTransition
执行的动画,您可以自定义它们!我将在下面解释如何进一步做到这一点。我通常使用这样的辅助方法来设置LayoutTransition
。
public static void animateLayoutChanges(ViewGroup container) {
final LayoutTransition transition = new LayoutTransition();
transition.setDuration(300);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
transition.enableTransitionType(LayoutTransition.CHANGING);
transition.enableTransitionType(LayoutTransition.APPEARING);
transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
transition.enableTransitionType(LayoutTransition.DISAPPEARING);
transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
}
container.setLayoutTransition(transition);
}
这将启用API Level 16及更高版本上的所有可能的自动转换,并且只使用下面默认启用的转换。只需使用它:
AnimatorUtils.animateLayoutChanges(linearLayout);
如果您在LinearLayout
的{{1}}上调用此方法,则ScrollView
及其直接子项的高度/宽度/可见度的所有更改都将为您设置动画。还会为您处理项目添加/删除动画。
要启用各种转换,例如调整动画大小,您需要在代码中设置LinearLayout
,但您可以通过设置属性启用基本转换,例如项目添加/删除动画
LayoutTransitions
在xml布局中的android:animateLayoutChanges="true"
上。
ViewGroup
上只有最少的文档,但基本知识涵盖here。
如果您愿意,可以自定义每个事件的动画,例如添加/删除LayoutTransitions
或更改View
这样的内容:
View
这是一个DevByte视频,更详细地解释了// APPEARING handles items being added to the ViewGroup
transition.setAnimator(LayoutTransition.APPEARING, someAnimator);
// CHANGING handles among other things height or width changes of items in the ViewGroup
transition.setAnimator(LayoutTransition.CHANGING, someOtherAnimator);
:
还要注意,当父级的高度发生变化时,容器视图基本上可以切断部分动画。由于LayoutTransition
具有固定大小并且未根据ScrollView
内的子项调整大小,但您在ScrollView
中实现了类似的内容,因此在您的情况下不会发生这种情况。 } ViewGroup
然后您需要在您尝试设置动画的wrap_content
上方的所有容器上设置android:clipChildren="false"
。您也可以在代码中使用View
。
这比使用setClipChildren()
要困难得多,主要是因为你必须对布局和测量过程有很多了解,否则你会引起问题。然而,一旦掌握了它,你就可以执行各种自定义动画。
基本过程是这样的:
LayoutTransition
州。View
从旧位置动画到新位置此过程的核心是侦听视图层次结构中的更改。这是使用View
完成的。您可以使用多种可能的回调,例如ViewTreeObserver
或OnPreDrawListener
。通常你会像这样实现它们:
OnGlobalLayoutListener
final Animator animator = setupAnimator();
animator.setTarget(view);
// Record the current state
animator.setupStartValues();
modifyChildrenOfLinearLayout();
linearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Remove the callback immediately we only need to catch it this one time.
linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Record the new state
animator.setupEndValues();
// Start the animation
animator.start();
}
});
更适合捕捉布局更改,因为它在布局过程完成后调用。在绘制下一帧之前调用OnGlobalLayoutListener
,但它们不能保证布局过程已经完成。但在实践中,这种差异可以忽略不计。更重要的是,在较旧的较慢设备上,在新状态下布局可能会短暂闪烁,因为它们需要一些时间来处理每个步骤。您可以使用OnPreDrawListener
并返回OnPreDrawListener
一次来防止这种情况发生。由于false
也仅在较新的API级别上完全可用,因此在大多数情况下您应使用OnGlobalLayoutListener
。
如果OnPreDrawListener
没有为您的问题提供足够的解决方案并且您想要/想要手动实现动画,那么学习如何有效地执行动画非常重要。您可以查看LayoutTransitions
here的源代码。 LayoutTransition
的实现基本上完全符合我在此解释的内容,它是一种最佳实践实现。我经常发现自己正在查看LayoutTransition
包的源代码,以了解有关如何有效制作动画的新内容,如果您想了解Android上的动画,我建议您也这样做!
您还可以观看一些关于此动画的Android DevBytes视频:
在此视频中,他解释了如何使用android.animation
为ListView
中的扩展单元设置动画。
永远记住,Layouting Engine是你的朋友。不要尝试重新发明轮子并手动完成布局过程已经为你做的事情。并且在执行动画时不要致电OnPreDrawListener
!
答案 1 :(得分:0)
来自Android开发者网站: ScrollView是一个FrameLayout,意味着你应该在其中放置一个包含整个内容的子项进行滚动;这个子本身可能是一个具有复杂对象层次结构的布局管理器。经常使用的子项是垂直方向的LinearLayout,呈现用户可以滚动的顶级项目的垂直数组。
我可以看到您在Scroll View中添加了多个布局作为子项,请添加一个线性布局并在该LinearLayout中添加其余布局。 希望它能解决你的问题
答案 2 :(得分:0)
试试这个。
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout >
</RelativeLayout>
<LinearLayout >
</LinearLayout>
</LinearLayout>
</ScrollView>
Scrollview必须只有一个孩子。所以我只创建了一个LinearLayout子项并在其中添加了其余的代码。
答案 3 :(得分:0)
只需将android:clipChildren="false"
添加到您的父动画视图中,动画就可以在视图外工作。