web-dev-qa-db-ja.com

プログラムでfitSystemWindowsをステータスバーの透明度に設定

私のアプリには、セクションごとに異なるフラグメントをホストする1つのアクティビティがあります。私は最近、fitSystemWindowstrueに設定してステータスバーを半透明にしました。これにより、アプリの背景色に設定されます。これは、次のように、色が一致するツールバーがあるフラグメントの場合は問題ありません。

enter image description here

ただし、フラグメントの1つに写真と半透明のツールバーがあるため、背景色ではなく、写真がステータスバーのスペースも占めるようにしたいと思います。

その解決策は、そのフラグメントに対してのみfitSystemWindowsfalseに設定し、手動で半透明ツールバーにパディングを追加することだと思います。これをプログラムで実行しても効果がないようですが、何が問題でしょうか?

これが私の主な活動レイアウトです:

<RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/main_parent_view"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical"
    Android:fitsSystemWindows="true">

<!-- Container for various fragment layouts, including nav drawer and tool bar -->

</RelativeLayout>

そして、私のフラグメントのonCreateView()内から:

RelativeLayout daddyLayout = (RelativeLayout)getActivity().findViewById(R.id.main_parent_view);
daddyLayout.setFitsSystemWindows(false);
daddyLayout.invalidate();

これは次のように効果がないようです:

enter image description here

main_parent_viewfitSystemWindowsをfalseに設定すると、ステータスバーのパディングがなくなり、機能しますが、明らかにすべてのフラグメントに影響しますです。

12
Daniel Wilson

同じ問題を見た。アプリで、アクティビティ宣言からfitSystemWindowsを削除し、フラグメントにpaddingTopを追加することで解決しました。明らかに理想的なソリューションではありませんが、機能しているようです。

4
vkislicins

さて、あなたはそこでジレンマの状況にあります。なぜなら、一方ではインセットを適用する必要があるため(Toolbarは正しく埋め込まれる必要があるため)、他方ではしない必要があるためですインセットを適用します(ステータスバーの下にImageViewを描画するため)。

その場合のためにフレームワークによって提供される素晴らしいAPIがあることがわかります:

ViewCompat.setOnApplyWindowInsetsListener(toolbar, (v, insets) -> {
    ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin =
            insets.getSystemWindowInsetTop();
    return insets.consumeSystemWindowInsets();
});

ルートレイアウトにAndroid:fitsSystemWindows="true"、適切なインセットがToolbarではなくImageViewonlyに適用されるようになりました。

しかし、問題があります。

問題は、ルートレイアウトがRelativeLayoutであり、インセットに関する情報を子にディスパッチしないことです。また、兄弟レイアウト(LinearLayoutFrameLayout)も行いません。

ルートレイアウトとして「マテリアリッシュ」レイアウト(CoordinatorLayoutDrawerLayout)のいずれかがあった場合、子はそれらのウィンドウインセットにディスパッチされます。

もう1つのオプションは、RelativeLayoutをサブクラス化し、WindowInsetsを手動で子にディスパッチすることです。

@TargetApi(Build.VERSION_CODES.KitKat_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    int childCount = getChildCount();
    for (int index = 0; index < childCount; index++)
        getChildAt(index).dispatchApplyWindowInsets(insets); // let children know about WindowInsets

    return insets;
}

あなたは この答え を見て、あなたが持っているのとまったく同じ要件の詳細な説明を見ることができます。

enter image description here

12
azizbekian

この質問は4.4で解決しました

if(test){
    Log.d(TAG, "fit true ");
    relativeLayout.setFitsSystemWindows(true);
    relativeLayout.requestFitSystemWindows();
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}else {
    Log.d(TAG, "fit false");
    relativeLayout.setFitsSystemWindows(false);
    relativeLayout.requestFitSystemWindows();
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
7
Kejie Yuan

CoordinatorLayoutをアクティビティルートビューとして使用すると、setFitsSystemWindows(boolean)が機能します。

これは、この ブログ投稿 で説明されているように、DrawerLayoutCoordinatorLayoutの両方にfitsSystemWindowsがどのように適用されるかについて異なるルールがあるためです-どちらも使用します子ビューを挿入するだけでなく、各子でdispatchApplyWindowInsets()を呼び出し、fitsSystemWindows="true"プロパティへのアクセスを許可します。

これは、FrameLayoutなどのレイアウトのデフォルトの動作との違いです。fitsSystemWindows="true"を使用すると、すべてのインセットが消費され、子ビューに通知せずに盲目的にパディングが適用されます(これは、ブログ投稿)。

4
John