web-dev-qa-db-ja.com

Android 4.4 —半透明のステータス/ナビゲーションバー— fitsSystemWindows / clipToPaddingがフラグメントトランザクションで機能しない

新しいAndroid 4.4 KitKat APIの半透明ステータスとナビゲーションバーを使用する場合、fitsSystemWindows="true"およびclipToPadding="false" to a ListViewは最初は機能します。 fitsSystemWindows="true"は、アクションバーの下とナビゲーションバーの上にリストを保持しますclipToPadding="false"を使用すると、リストを透明なナビゲーションバーの下にスクロールして、リストの最後のアイテムを、ナビゲーションバーを通過するのに十分なだけ上にスクロールできます。

ただし、コンテンツをFragmentを介して別のFragmentTransactionに置き換えると、fitsSystemWindowsの効果がなくなり、フラグメントがアクションバーとナビゲーションバーの下に表示されます。

ここには、例としてダウンロード可能なAPKとともにデモソースコードのコードベースがあります: https://github.com/afollestad/KitKat-transparency-demo 。私が話していることを確認するには、KitKatを実行しているデバイスからデモアプリを開き、リスト内の項目(別のアクティビティを開きます)をタップし、開いた新しいアクティビティ内の項目をタップします。コンテンツを置き換えるフラグメントはアクションバーの下にあり、clipToPaddingは正しく機能しません(下にスクロールすると、ナビゲーションバーがリストの最後の項目を覆います)。

何か案は?説明が必要ですか?雇用主のために開発されている個人用アプリのスクリーンショットの前と後を投稿しました。

OneTwo

27
afollestad

半透明ステータスバーの色を設定するライブラリを使用して問題を解決しました。

SystemBarTintのSystemBarConfigクラス(ここに示す https://github.com/jgilfelt/SystemBarTint#systembarconfig )を使用すると、すべてのリストのパディングとして設定したインセットを取得できますリストでのclipToPadding="false"の使用とともに、フラグメント。

私はこの投稿で私がやったことの詳細を持っています: http://mindofaandroiddev.wordpress.com/2013/12/28/making-the-status-bar-and-navigation-bar-transparent-with -a-listview-on-Android-4-4-KitKat /

9
afollestad

昨日も同じ問題で苦労しました。よく考えた後、私はこの問題のエレガントな解決策を見つけました。

最初に、メソッド requestFitSystemWindows() on ViewParentを確認し、フラグメントのonActivityCreated()でそれを呼び出そうとしました(フラグメントがアタッチされた後)ビュー階層)が、悲しいことに効果がありませんでした。その使い方の具体例を知りたい。

それから私はきちんとした回避策を見つけました:レイアウトでフラグメントコンテナーとして使用するカスタムFitsSystemWindowsFrameLayoutをドロップとして作成しました従来のFrameLayoutの代わりに。 fitSystemWindows()がシステムによって呼び出されたときにウィンドウのインセットを記憶し、フラグメントが追加またはアタッチされるとすぐに、その呼び出しを子レイアウト(フラグメントレイアウト)に再度伝播します。

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

public class FitsSystemWindowsFrameLayout extends FrameLayout {

    private Rect windowInsets = new Rect();
    private Rect tempInsets = new Rect();

    public FitsSystemWindowsFrameLayout(Context context) {
        super(context);
    }

    public FitsSystemWindowsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FitsSystemWindowsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
        windowInsets.set(insets);
        super.fitSystemWindows(insets);
        return false;
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        tempInsets.set(windowInsets);
        super.fitSystemWindows(tempInsets);
    }
}

これは、時間の経過とともに変化する可能性のある非表示のシステムプロパティにアクセスしてUI要素のサイズを決定し、手動で要素にパディングを適用するハックよりもはるかにシンプルで堅牢だと思います。

12
BladeCoder

さて、これは信じられないほど奇妙です。私は最近、同じ問題に遭遇しましたが、私の場合はソフトキーボードが関係しています。最初は機能しますが、フラグメントトランザクションを追加すると、Android:fitsSystemWindows="true"は機能しなくなりました。ここですべての解決策を試しましたが、どれもうまくいきませんでした。

これが私の問題です: enter image description here

ビューのサイズを変更する代わりに、ビューを押し上げるので、それが問題です。

しかし、私は運が良かったので、偶然にうまくいった答えに出くわしました!

だからここにあります:

まず、私のアプリのテーマは、Theme.AppCompat.Light.NoActionBarです(もしそれが関連しているとしたら、おそらくAndroidは奇妙です)。

モーリシーはここで非常に興味深いことを指摘したので、私は彼の言ったことが真実かどうかをテストしたいと思いました。彼が言ったことは私の場合にも当てはまります... Androidアプリのマニフェストでアクティビティにこの属性を追加しない限り:

enter image description here

追加したら:

Android:windowSoftInputMode="adjustResize" 

あなたの活動に、Android:fitsSystemWindows="true"は、フラグメントトランザクション後に無視されなくなりました。

ただし、Android:fitsSystemWindows="true"フラグメントのルートレイアウトにはありません。この問題が発生する最も大きな場所の1つは、EditTextまたはListViewがある場合です。私のようにこの窮地に陥っている場合は、Android:fitsSystemWindows="true"次のように、ルートレイアウトの子で:

enter image description here

はい、このソリューションはすべてのLollipopおよびLollipop以前のデバイスで機能します。

そしてここに証明があります: enter image description here

レイアウトを押し上げる代わりに、サイズを変更します。うまくいけば、私は私と同じ船に乗っている人を助けました。

どうもありがとうございました!

8
Yoosuf

この問題に遭遇している一部の人々に向かいます。

多くの作業を行うfitSystemWindowsメソッドの重要な情報:

この関数の階層内のトラバーサルは、深さ優先です。同じコンテンツインセットオブジェクトが階層の下に伝達されるため、それに加えられた変更は、すべてのビューで表示されます(これは、深さ優先トラバーサルであるため、階層の上のビューを含む可能性があります)。 trueを返す最初のビューは、全探索を中止します。

したがって、fitsSystemWindowsがtrueに設定されているコンテンツビューを持つ他のフラグメントがある場合、フラグは無視される可能性があります。可能であれば、フラグメントコンテナーにfitsSystemWindowsフラグを含めることを検討します。それ以外の場合は、手動でパディングを追加します。

5
Maurycy

私もこれでかなり苦労しています。私はここですべての応答を見てきました。残念ながら、それらのどれも私の問題を100%解決しませんでした。一部のデバイスでバーを検出できないため、SystemBarConfigが常に機能しているとは限りません。私はソースコードを見て、インセットがウィンドウ内のどこに保存されているかを見つけました。

        Rect insets = new Rect();
        Window window = getActivity().getWindow();
        try {
            Class clazz = Class.forName("com.Android.internal.policy.impl.PhoneWindow");
            Field field = clazz.getDeclaredField("mDecor");
            field.setAccessible(true);
            Object decorView = field.get(window);
            Field insetsField = decorView.getClass().getDeclaredField("mFrameOffsets");
            insetsField.setAccessible(true);
            insets = (Rect) insetsField.get(decorView);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

これはそれらを取得する方法です。どうやらAndroid= Lでは、これらのインセットを取得するためのNiceメソッドがあるでしょうが、それまではこれが良い解決策になるかもしれません。

2

同じ問題が発生しました。フラグメントを置き換えるとき。 「fitsSystemWindows」は機能しません。

フラグメントにコードを追加して修正しました

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    AndroidUtil.runOnUIThread(new Runnable() {
        @Override
        public void run() {
            ((ViewGroup) getView().getParent()).setFitsSystemWindows(true);
        }
    });
}
0
user1198954

@BladeCoderの回答と組み合わせて、2つのことを行うFittedFrameLayoutクラスを作成しました。

  • それ自体のパディングは追加されません
  • コンテナー内のすべてのビューをスキャンし、それらにパディングを追加しますが、最下位のレイヤーで停止します(fitssystemwindowsフラグが見つかった場合、子はより深くスキャンされませんが、同じ深さまたはそれ以下でスキャンされます)。

    public class FittedFrameLayout extends FrameLayout {
        private Rect insets = new Rect();
    
        public FittedFrameLayout(Context context) {
            super(context);
        }
    
        public FittedFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public FittedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.Lollipop)
        public FittedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        protected void setChildPadding(View view, Rect insets){
            if(!(view instanceof ViewGroup))
                return;
    
            ViewGroup parent = (ViewGroup) view;
            if (parent instanceof FittedFrameLayout)
                ((FittedFrameLayout)parent).fitSystemWindows(insets);
            else{
                if( ViewCompat.getFitsSystemWindows(parent))
                    parent.setPadding(insets.left,insets.top,insets.right,insets.bottom);
                else{
                    for (int i = 0, z = parent.getChildCount(); i < z; i++)
                        setChildPadding(parent.getChildAt(i), insets);
                }
            }
        }
    
        @Override
        protected boolean fitSystemWindows(Rect insets) {
            this.insets = insets;
            for (int i = 0, z = getChildCount(); i < z; i++)
                setChildPadding(getChildAt(i), insets);
    
            return true;
        }
    
        @Override
        public void addView(View child, int index, ViewGroup.LayoutParams params) {
            super.addView(child, index, params);
            setChildPadding(child, insets);
        }
    }
    
0

この質問は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);
}
0
Kejie Yuan