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