【问题标题】:animateLayoutChanges="true" in BottomSheetView showing unexpected behaviourBottomSheetView 中的 animateLayoutChanges="true" 显示意外行为
【发布时间】:2017-06-24 13:39:26
【问题描述】:

我有一个BottomSheetView,其中有animateLayoutChanges="true"。最初它显示得很好。但是,如果将视图的visibility(在BottomSheetView 内)从GONE 更改为VISIBLE,应用程序会打乱计算,我的BottomSheetView 会移动到屏幕顶部。我尝试将layout_gravity=bottom 设置在BottomSheetView 布局的根部。但没有成功。

在更改任何视图的可见性之前,我有我的BottomSheetView 的图像。 (点击图片查看大图)

在我更改视图的可见性(GONEVISIBLEVISIBLEGONE)后,我的 BottomSheetView 移动到顶部。 (点击图片查看大图)

我猜,Android 在计算视图widthheight 的测量时搞砸了。有什么办法解决吗??

我还尝试让我的 BottomSheetView 完全扩展以匹配父视图,但不知何故,这使得 BottomSheetViewheight 比电话屏幕更长,并且在-tun 中产生滚动问题。

预期的解决方案:

1> 即使视图的visibility 发生更改,也防止BottomSheetView 更改其位置。

2>使BottomSheetView 匹配父级,这样在计算混乱后它看起来不会很糟糕。

【问题讨论】:

    标签: android android-layout android-fragments animation bottom-sheet


    【解决方案1】:

    我遇到了同样的问题并决定找到解决方法。我能够找到根本原因,但不幸的是,我目前没有看到很好的解决方法。

    原因: 问题发生在底页行为和 LayoutTransition 之间。创建 LayoutTransition 时,它会在视图上创建一个 OnLayoutChangeListener 以便它可以捕获其 endValues 并设置具有适当值的动画器。此 OnLayoutChangeListener 在第一次调用 parent.onLayout(child) 时在 bottomSheetBehavior 的 onLayout() 调用中触发。父级将像往常一样布局子级,忽略以后行为会改变的任何偏移量。问题就在这里。此时视图的值由 OnLayoutChangeListener 捕获并存储在动画器中。当动画运行时,它将根据这些值进行动画处理,而不是根据您的行为定义的位置进行动画处理。不幸的是,LayoutTransition 类不允许我们访问动画师来更新最终值。

    解决方法: 目前,我没有看到涉及 LayoutTransitions 的优雅修复。我将提交一个错误以获取和更新 LayoutTransition 动画师的方法。现在,您可以使用 layoutTransition.setAnimateParentHierachy(false) 禁用父容器上的任何 layoutTransition。然后,您可以自己为更改设置动画。我会尽快用一个工作示例更新我的答案。

    【讨论】:

    • 你提交了这个错误吗?你找到优雅的方式了吗?
    • > 我将提交一个错误以获取和更新 LayoutTransition 动画师的方法 你能发布你提交的错误吗?我想关注更新:)
    【解决方案2】:

    BottomSheetBehavior 目前不适用于LayoutTransition (animateLayoutChanges="true")。我会努力解决的。

    目前,您可以改用Transition。像这样的东西会淡化里面的视图并动画底部表的大小。

    ViewGroup bottomSheet = ...;
    View hidingView = ...;
    
    TransitionManager.beginDelayedTransition(bottomSheet);
    hidingView.setVisibility(View.GONE);
    

    您可以参考Applying a Transition了解更多信息,包括如何自定义动画。

    【讨论】:

    • 完美解决方案。此外,例如,如果想要更改持续时间,请将以下转换作为第二个参数添加到 beginDelayedTransitionnew AutoTransition().setDuration(150)
    • 所以这里是修复?我正在使用库的最新版本 com.android.support:design 并且仍在发生.. 在这里查看:github.com/extmkv/BottomSheetExample
    • 还想知道是否有修复。是否有问题提交到我们可以查看的地方?
    • 对于那些试图为 BottomSheetDialog 的内容更改设置动画的人 - 您必须找到像 dialog.findViewById<ViewGroup>(com.google.android.material.R.id.design_bottom_sheet) 这样的工作表视图组
    • 自此问题报告以来已过去 4 年,到 2021 年仍然存在
    【解决方案3】:

    在BottomSheetDialog默认布局(design_bottom_sheet_dialog)中,对话框的design_bottom_sheetFrameLayout上有一个TOP重力:

     android:layout_gravity="center_horizontal|top"
    

    我真的不知道为什么 BottomSheetDialog 的重力是顶部。

    您需要在项目中创建相同的布局文件(相同的内容和名称)并将这一行替换为:

    android:layout_gravity="center_horizontal|bottom"
    

    【讨论】:

    • 它不适用于具有编辑文本的 BottomSheetDialog。奇怪的行为。
    【解决方案4】:

    这个问题是在两年多以前提出的,但不幸的是问题仍然存在。

    我终于找到了一个解决方案,在拥有animateLayoutChanges="true" 的同时,将addViewremoveView 函数的调用保留在BottomSheet 中。

    BottomSheetBehavior在变化时无法计算出正确的高度,所以高度必须保持不变。为此,我将BottomSheet 的高度设置为match_parent,并将其分为两个子项:内容和根据内容高度改变高度的Space

    为了最好地模仿BottomSheet 的真实行为,您还需要添加一个TouchToDismiss 视图,当BottomSheet 扩展时使背景变暗,并且当用户在内容之外按下时关闭BottomSheet

    代码如下:

    activity.xml

    <androidx.coordinatorlayout.widget.CoordinatorLayout 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"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/show_bottom_sheet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Show bottom sheet"/>
    
        <View
            android:id="@+id/touch_to_dismiss"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            android:background="#9000"/>
    
        <LinearLayout
            android:id="@+id/bottom_sheet"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
    
            <Space
                android:id="@+id/space"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="1"/>
    
            <LinearLayout
                android:id="@+id/bottom_sheet_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:animateLayoutChanges="true">
    
                <Button
                    android:id="@+id/add_or_remove_another_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Add another view"/>
    
                <TextView
                    android:id="@+id/another_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Another view"/>
    
            </LinearLayout>
    
        </LinearLayout>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    

    activity.java

    BottomSheetBehavior bottomSheetBehavior;
    View touchToDismiss;
    LinearLayout bottomSheet;
    Button showBottomSheet;
    Space space;
    LinearLayout bottomSheetContent;
    Button addOrRemoveAnotherView;
    TextView anotherView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        touchToDismiss = findViewById(R.id.touch_to_dismiss);
        touchToDismiss.setVisibility(View.GONE);
        touchToDismiss.setOnClickListener(this);
    
        bottomSheet = findViewById(R.id.bottom_sheet);
    
        bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
        bottomSheetBehavior.setPeekHeight(0);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_HIDDEN || newState == BottomSheetBehavior.STATE_COLLAPSED) {
                    touchToDismiss.setVisibility(View.GONE);
                }else {
                    touchToDismiss.setVisibility(View.VISIBLE);
                }
            }
    
            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                touchToDismiss.setAlpha(getRealOffset());
            }
        });
    
        showBottomSheet = findViewById(R.id.show_bottom_sheet);
        showBottomSheet.setOnClickListener(this);
    
        space = findViewById(R.id.space);
    
        bottomSheetContent = findViewById(R.id.bottom_sheet_content);
    
        addOrRemoveAnotherView = findViewById(R.id.add_or_remove_another_view);
        addOrRemoveAnotherView.setOnClickListener(this);
    
        anotherView = findViewById(R.id.another_view);
        bottomSheetContent.removeView(anotherView);
    }
    
    @Override
    public void onClick(View v) {
        if (v == showBottomSheet)
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        else if (v == addOrRemoveAnotherView) {
            if (anotherView.getParent() == null)
                bottomSheetContent.addView(anotherView);
            else
                bottomSheetContent.removeView(anotherView);
        }
        else if (v == touchToDismiss)
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    }
    
    /**
     * Since the height does not change and remains at match_parent, it is required to calculate the true offset.
     * @return Real offset of the BottomSheet content.
     */
    public float getRealOffset() {
        float num = (space.getHeight() + bottomSheetContent.getHeight()) - (bottomSheet.getY() + space.getHeight());
    
        float den = bottomSheetContent.getHeight();
    
        return (num / den);
    }
    

    这是使用此代码获得的结果:

    希望它对某人有用,因为问题仍然存在!

    【讨论】:

      【解决方案5】:

      如果您的视图允许,请将可见性设为 INVISIBLE 而不是 GONE。

      【讨论】:

        【解决方案6】:

        在我们的例子中,我们在按钮组件上显示了一个进度条。这是隐藏按钮文本并显示进度条。这导致底部片断的跳跃。使用 Invisible 而不是 Gone 解决了这个问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-07-30
          • 2018-03-11
          • 2017-08-16
          • 1970-01-01
          • 1970-01-01
          • 2021-12-31
          • 2013-02-04
          • 2022-06-21
          相关资源
          最近更新 更多