web-dev-qa-db-ja.com

BottomSheetViewのanimateLayoutChanges = "true"が予期しない動作を示している

animateLayoutChanges="true"を持つBottomSheetViewがあります。最初は問題なく表示されます。しかし、ビュー(visibility内)のBottomSheetViewGONEからVISIBLEに変更すると、アプリは計算と私のBottomSheetViewを台無しにします。画面の上部に移動します。 BottomSheetViewレイアウトのルートにlayout_gravity=bottomを設定してみました。しかし、成功しません。

ここに、ビューの表示を変更する前のBottomSheetViewの画像があります。 (フルサイズは画像をクリックしてください)

enter image description here

ビューの表示を変更した後(GONEからVISIBLEまたはVISIBLEからGONE)、BottomSheetViewが一番上に移動します。 (フルサイズは画像をクリックしてください)

enter image description here

おそらく、Androidはビューwidthheightの測定について計算をしているときに混乱しています。これを解決する方法はありますか?

また、BottomSheetViewを完全に拡張して親ビューに一致させようとしましたが、どういうわけか、heightBottomSheetViewが電話の画面よりも長くなり、スクロールの問題が発生します。

期待される解決策:

1>ビューのBottomSheetViewが変更された場合でも、visibilityがその位置を変更しないようにします。

OR

2> BottomSheetViewを親に一致させて、計算を台無しにした後に見栄えが悪くならないようにします。

24
Srujan Barai

BottomSheetBehaviorはLayoutTransitionanimateLayoutChanges="true") 今のところ。修正に取り組みます。

今のところ、代わりにTransitionを使用できます。このようなものは、内部のビューをフェードさせ、下部シートのサイズをアニメーション化します。

ViewGroup bottomSheet = ...;
View hidingView = ...;

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

アニメーションのカスタマイズ方法などの詳細については、 トランジションの適用 を参照してください。

12
Yuichi Araki

私は同じ問題に遭遇し、修正を見つけることを決心しました。根本的な原因を見つけることができましたが、残念ながら現時点では大きな解決策は見当たりません。

原因:問題はボトムシートの動作とLayoutTransitionの間で発生します。 LayoutTransitionが作成されると、ビューにOnLayoutChangeListenerが作成され、endValuesをキャプチャして、適切な値でアニメーターをセットアップできるようになります。このOnLayoutChangeListenerは、bottomSheetBehaviorのonLayout()呼び出しで、最初にparent.onLayout(child)を呼び出したときにトリガーされます。親は、動作が後で変更されるオフセットを無視して、通常どおりに子をレイアウトします。問題はここにあります。この時点でのビューの値は、OnLayoutChangeListenerによってキャプチャされ、アニメーターに格納されます。アニメーションが実行されると、動作が定義する場所ではなく、これらの値にアニメーション化されます。残念ながら、LayoutTransitionクラスでは、アニメーターにアクセスして終了値を更新することはできません。

修正:現在、LayoutTransitionsを含むエレガントな修正は見当たりません。 LayoutTransitionアニメーターにアクセスして更新する方法のバグを送信します。今のところ、layoutTransition.setAnimateParentHierachy(false)を使用して親コンテナのlayoutTransitionを無効にすることができます。次に、変更を自分でアニメーション化できます。できるだけ早く、実際の例で答えを更新します。

8
artyoda21

質問は2年以上前に行われましたが、残念ながら問題は解決していません。

animateLayoutChanges="true"を使用しながら、BottomSheetでaddView関数とremoveView関数の呼び出しを維持するソリューションをついに手に入れました。

BottomSheetBehaviorは、変更時に正しい高さを計算できないため、高さは同じままである必要があります。これを行うには、BottomSheetの高さをmatch_parentに設定し、コンテンツと、コンテンツの高さに応じて高さを変更するSpaceの2つの子に分割します。

BottomSheetの実際の動作を最もよく模倣するには、BottomSheetが拡張されたときに背景を暗くするだけでなく、BottomSheetを閉じたときに背景を暗くするTouchToDismissビューを追加する必要もあります。ユーザーがコンテンツの外側を押します。

コードは次のとおりです。

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);
}

これは、このコードで得られた結果です: final result

問題がまだ残っているので、誰かに役立つことを願っています!

3
Charles Annic

BottomSheetDialogのデフォルトレイアウト( design_bottom_sheet_dialog )には、[〜#〜] top [〜#〜]の重力があります。ダイアログのdesign_bottom_sheet FrameLayout:

 Android:layout_gravity="center_horizontal|top"

BottomSheetDialogの重力が一番上にある理由はよくわかりません。

プロジェクトに同じレイアウトファイル(同じコンテンツと名前)を作成し、この行を次のように置き換える必要があります。

Android:layout_gravity="center_horizontal|bottom"
1
bitvale