Android Dev Study

Android 15 Edge to Edge 하위 호환성 유지하기

Daejeong Kim 2025. 5. 29. 19:58

targetSDK 35로 지정하고 Android 15 이상에서는 기본으로 Edge to Edge 가 적용된다. Link

앱의 구조가 간단하여 굳이 이것을 적용하고 싶지 않을 경우도 많을텐데 왜 edge-to-edge를 강제적용시키는지... 하는 불만이 생긴다. 

1. Androd 15에서 optOut하기. 

Android 16부터는 어쩔 수 없이 대응해야하겠지만 15까지는 예외적으로 적용을 빼주는 방법이 있다.  테마에 windowOptOutEdgeToEdgeEnforcement 를 true로 설정한다.

values-v35/styles.xml

<resources>
    <!-- Base application theme. values-v35 -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
    </style>
</resources>

values-v35-night/styles.xml

<resources>
    <!-- Base application theme. values-night-v35 -->
    <style name="AppTheme" parent="Theme.AppCompat.DayNight">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
    </style>
</resources>

외부 라이브러리에 있는 Activity의 경우 theme가 다르므로 windowOptOutEdgeToEdgeEnforcement 가 적용되지 않는다. 이 경우에는 아래 방법으로 별도로 처리해줘야 한다. 

 

2. 특정 Activity 나 모든 Activity에서 하위 호환성 유지하기

windowOptOutEdgeToEdgeEnforcement를 사용하지 않고 코드로만 호환성을 유지한다. edge-to-edge가 활성화되면 window.statusBarColor = myColor는 동작하지 않는 것 같다.  tagetSdk가 36 이상이라면 이 방법만 먹힐 것 같다. 

EdgeToEdgeOptOutCallbacks.java

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;

import gun0912.tedimagepicker.TedImagePickerActivity;

public class EdgeToEdgeOptOutCallbacks implements Application.ActivityLifecycleCallbacks {

    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {

    }

    @Override
    public void onActivityPostCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        Application.ActivityLifecycleCallbacks.super.onActivityPostCreated(activity, savedInstanceState);
        // top, bottom padding
        View contentView = activity.findViewById(android.R.id.content);
        ViewCompat.setOnApplyWindowInsetsListener(contentView, new OnApplyWindowInsetsListener() {
            @NonNull
            @Override
            public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) {
                Insets innerPadding = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
                v.setPadding(0, innerPadding.top, 0, innerPadding.bottom);
                return insets;
            }
        });

        // <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> 를 사용하지 않는다면 상태바 icon color를 조정하자
        if (!(activity instanceof TedImagePickerActivity)) {
            boolean isLightStatusBars = AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES;
            WindowInsetsControllerCompat compat = new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView());
            compat.setAppearanceLightStatusBars(isLightStatusBars);
            compat.setAppearanceLightNavigationBars(isLightStatusBars);
        }
    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {

    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {

    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {

    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {

    }
}

 

MyApplication.java

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 35) {
            registerActivityLifecycleCallbacks(new EdgeToEdgeOptOutCallbacks());
        }
    }
    
    ...