Skip to content

Commit 480b514

Browse files
committed
适配 Android 15 EdgeToEdge 特性
1 parent ce893fb commit 480b514

19 files changed

Lines changed: 204 additions & 31 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.hjq.demo.action;
2+
3+
import android.view.View;
4+
import androidx.annotation.Nullable;
5+
import com.hjq.bar.OnTitleBarListener;
6+
7+
/**
8+
* author : Android 轮子哥
9+
* github : https://github.com/getActivity/AndroidProject
10+
* time : 2025/12/13
11+
* desc : 沉浸式意图
12+
*/
13+
public interface ImmersionAction extends OnTitleBarListener {
14+
15+
/**
16+
* 获取需要沉浸的顶部 View 对象
17+
*/
18+
@Nullable
19+
default View getImmersionTopView() {
20+
return null;
21+
}
22+
23+
/**
24+
* 获取需要沉浸的底部 View 对象
25+
*/
26+
@Nullable
27+
default View getImmersionBottomView() {
28+
return null;
29+
}
30+
}

app/src/main/java/com/hjq/demo/app/AppActivity.java

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package com.hjq.demo.app;
22

33
import android.content.Intent;
4+
import android.graphics.Insets;
45
import android.view.View;
6+
import android.view.View.OnApplyWindowInsetsListener;
7+
import android.view.WindowInsets;
58
import androidx.annotation.NonNull;
69
import androidx.annotation.Nullable;
710
import androidx.annotation.StringRes;
811
import com.gyf.immersionbar.ImmersionBar;
912
import com.hjq.bar.TitleBar;
1013
import com.hjq.base.BaseActivity;
1114
import com.hjq.demo.R;
15+
import com.hjq.demo.action.ImmersionAction;
1216
import com.hjq.demo.action.TitleBarAction;
1317
import com.hjq.demo.action.ToastAction;
1418
import com.hjq.demo.http.model.HttpData;
19+
import com.hjq.demo.other.AndroidVersion;
1520
import com.hjq.demo.ui.dialog.common.WaitDialog;
1621
import com.hjq.http.config.IRequestApi;
1722
import com.hjq.http.listener.OnHttpListener;
@@ -24,7 +29,7 @@
2429
* desc : Activity 业务基类
2530
*/
2631
public abstract class AppActivity extends BaseActivity
27-
implements ToastAction, TitleBarAction, OnHttpListener<Object> {
32+
implements ToastAction, TitleBarAction, ImmersionAction, OnHttpListener<Object> {
2833

2934
/** 标题栏对象 */
3035
private TitleBar mTitleBar;
@@ -105,9 +110,39 @@ protected void initLayout() {
105110
getStatusBarConfig().init();
106111
}
107112

108-
View immersionView = getImmersionView();
109-
if (immersionView != null) {
110-
ImmersionBar.setTitleBar(this, immersionView);
113+
// 适配 Android 15 EdgeToEdge 特性,这里你可能好奇为什么判断的是 Android 16?
114+
// 因为我在主题样式中注册了一个 windowOptOutEdgeToEdgeEnforcement 属性,
115+
// 代表跳过在 Android 15 的 EdgeToEdge 特性适配,但到了 Android 16 上面就失效了。
116+
if (AndroidVersion.isAndroid16()) {
117+
getWindow().getDecorView().setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
118+
119+
@NonNull
120+
@Override
121+
public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) {
122+
Insets systemBars = insets.getInsets(WindowInsets.Type.systemBars());
123+
View immersionTopView = getImmersionTopView();
124+
View immersionBottomView = getImmersionBottomView();
125+
if (immersionTopView != null && immersionTopView == immersionBottomView) {
126+
immersionTopView.setPadding(immersionTopView.getPaddingLeft(), systemBars.top,
127+
immersionTopView.getPaddingRight(), systemBars.bottom);
128+
return insets;
129+
}
130+
if (immersionTopView != null) {
131+
immersionTopView.setPadding(immersionTopView.getPaddingLeft(), systemBars.top,
132+
immersionTopView.getPaddingRight(), immersionTopView.getPaddingBottom());
133+
}
134+
if (immersionBottomView != null) {
135+
immersionBottomView.setPadding(immersionBottomView.getPaddingLeft(), immersionBottomView.getPaddingTop(),
136+
immersionBottomView.getPaddingRight(), systemBars.bottom);
137+
}
138+
return insets;
139+
}
140+
});
141+
} else {
142+
View immersionTopView = getImmersionTopView();
143+
if (immersionTopView != null) {
144+
ImmersionBar.setTitleBar(this, immersionTopView);
145+
}
111146
}
112147
}
113148

@@ -169,16 +204,21 @@ public void setTitle(CharSequence title) {
169204
}
170205
}
171206

172-
@Override
173207
@Nullable
208+
@Override
174209
public TitleBar getTitleBar() {
175210
if (mTitleBar == null) {
176211
mTitleBar = obtainTitleBar(getContentView());
177212
}
178213
return mTitleBar;
179214
}
180215

181-
public View getImmersionView() {
216+
/**
217+
* 获取需要沉浸的顶部 View 对象
218+
*/
219+
@Nullable
220+
@Override
221+
public View getImmersionTopView() {
182222
return getTitleBar();
183223
}
184224

app/src/main/java/com/hjq/demo/app/TitleBarFragment.java

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package com.hjq.demo.app;
22

3+
import android.graphics.Insets;
34
import android.os.Bundle;
45
import android.view.View;
6+
import android.view.View.OnApplyWindowInsetsListener;
57
import android.view.ViewGroup;
8+
import android.view.WindowInsets;
69
import androidx.annotation.NonNull;
710
import androidx.annotation.Nullable;
811
import com.gyf.immersionbar.ImmersionBar;
912
import com.hjq.bar.TitleBar;
1013
import com.hjq.demo.R;
14+
import com.hjq.demo.action.ImmersionAction;
1115
import com.hjq.demo.action.TitleBarAction;
16+
import com.hjq.demo.other.AndroidVersion;
1217

1318
/**
1419
* author : Android 轮子哥
@@ -17,7 +22,7 @@
1722
* desc : 带标题栏的 Fragment 业务基类
1823
*/
1924
public abstract class TitleBarFragment<A extends AppActivity>
20-
extends AppFragment<A> implements TitleBarAction {
25+
extends AppFragment<A> implements TitleBarAction, ImmersionAction {
2126

2227
/** 标题栏对象 */
2328
private TitleBar mTitleBar;
@@ -39,9 +44,39 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
3944
getStatusBarConfig().init();
4045
}
4146

42-
View immersionView = getImmersionView();
43-
if (immersionView != null) {
44-
ImmersionBar.setTitleBar(this, immersionView);
47+
// 适配 Android 15 EdgeToEdge 特性,这里你可能好奇为什么判断的是 Android 16?
48+
// 因为我在主题样式中注册了一个 windowOptOutEdgeToEdgeEnforcement 属性,
49+
// 代表跳过在 Android 15 的 EdgeToEdge 特性适配,但到了 Android 16 上面就失效了。
50+
if (AndroidVersion.isAndroid16()) {
51+
view.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
52+
53+
@NonNull
54+
@Override
55+
public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) {
56+
Insets systemBars = insets.getInsets(WindowInsets.Type.systemBars());
57+
View immersionTopView = getImmersionTopView();
58+
View immersionBottomView = getImmersionBottomView();
59+
if (immersionTopView != null && immersionTopView == immersionBottomView) {
60+
immersionTopView.setPadding(immersionTopView.getPaddingLeft(), systemBars.top,
61+
immersionTopView.getPaddingRight(), systemBars.bottom);
62+
return insets;
63+
}
64+
if (immersionTopView != null) {
65+
immersionTopView.setPadding(immersionTopView.getPaddingLeft(), systemBars.top,
66+
immersionTopView.getPaddingRight(), immersionTopView.getPaddingBottom());
67+
}
68+
if (immersionBottomView != null) {
69+
immersionBottomView.setPadding(immersionBottomView.getPaddingLeft(), immersionBottomView.getPaddingTop(),
70+
immersionBottomView.getPaddingRight(), systemBars.bottom);
71+
}
72+
return insets;
73+
}
74+
});
75+
} else {
76+
View immersionTopView = getImmersionTopView();
77+
if (immersionTopView != null) {
78+
ImmersionBar.setTitleBar(this, immersionTopView);
79+
}
4580
}
4681
}
4782

@@ -103,7 +138,9 @@ public TitleBar getTitleBar() {
103138
return mTitleBar;
104139
}
105140

106-
public View getImmersionView() {
141+
@Nullable
142+
@Override
143+
public View getImmersionTopView() {
107144
return getTitleBar();
108145
}
109146
}

app/src/main/java/com/hjq/demo/ui/activity/AboutActivity.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.hjq.demo.ui.activity;
22

3+
import android.view.View;
4+
import androidx.annotation.Nullable;
35
import com.hjq.demo.R;
46
import com.hjq.demo.app.AppActivity;
57

@@ -21,4 +23,10 @@ protected void initView() {}
2123

2224
@Override
2325
protected void initData() {}
26+
27+
@Nullable
28+
@Override
29+
public View getImmersionBottomView() {
30+
return findViewById(R.id.tv_about_copyright);
31+
}
2432
}

app/src/main/java/com/hjq/demo/ui/activity/DialogActivity.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.view.Gravity;
44
import android.view.View;
55
import android.widget.Button;
6+
import androidx.annotation.Nullable;
67
import com.hjq.bar.TitleBar;
78
import com.hjq.base.BaseDialog;
89
import com.hjq.demo.R;
@@ -67,6 +68,12 @@ protected void initData() {
6768

6869
}
6970

71+
@Nullable
72+
@Override
73+
public View getImmersionBottomView() {
74+
return findViewById(R.id.ll_dialog_content);
75+
}
76+
7077
@SingleClick
7178
@Override
7279
public void onClick(View view) {

app/src/main/java/com/hjq/demo/ui/activity/GuideActivity.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.view.animation.Animation;
55
import android.view.animation.ScaleAnimation;
66
import androidx.annotation.NonNull;
7+
import androidx.annotation.Nullable;
78
import androidx.viewpager2.widget.ViewPager2;
89
import com.gyf.immersionbar.ImmersionBar;
910
import com.hjq.demo.R;
@@ -84,6 +85,12 @@ protected void initData() {
8485
mIndicatorView.setViewPager(mViewPager);
8586
}
8687

88+
@Nullable
89+
@Override
90+
public View getImmersionBottomView() {
91+
return findViewById(R.id.fl_guide_control);
92+
}
93+
8794
@SingleClick
8895
@Override
8996
public void onClick(View view) {

app/src/main/java/com/hjq/demo/ui/activity/HomeActivity.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import android.content.Context;
55
import android.content.Intent;
66
import android.os.Bundle;
7+
import android.view.View;
78
import androidx.annotation.NonNull;
9+
import androidx.annotation.Nullable;
810
import androidx.core.content.ContextCompat;
911
import androidx.recyclerview.widget.RecyclerView;
1012
import androidx.viewpager.widget.ViewPager;
@@ -88,6 +90,12 @@ protected void initData() {
8890
onNewIntent(getIntent());
8991
}
9092

93+
@Nullable
94+
@Override
95+
public View getImmersionBottomView() {
96+
return mNavigationView;
97+
}
98+
9199
@Override
92100
protected void onNewIntent(Intent intent) {
93101
super.onNewIntent(intent);

app/src/main/java/com/hjq/demo/ui/activity/SettingActivity.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.view.Gravity;
44
import android.view.View;
55
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
67
import com.hjq.base.BaseDialog;
78
import com.hjq.demo.R;
89
import com.hjq.demo.aop.SingleClick;
@@ -72,6 +73,12 @@ protected void initData() {
7273
mPasswordView.setRightText("密码强度较低");
7374
}
7475

76+
@Nullable
77+
@Override
78+
public View getImmersionBottomView() {
79+
return findViewById(R.id.ll_setting_content);
80+
}
81+
7582
@SingleClick
7683
@Override
7784
public void onClick(View view) {

app/src/main/java/com/hjq/demo/ui/activity/account/LoginActivity.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import android.widget.ImageView;
1616
import android.widget.TextView;
1717
import androidx.annotation.NonNull;
18+
import androidx.annotation.Nullable;
1819
import com.gyf.immersionbar.ImmersionBar;
1920
import com.hjq.demo.R;
2021
import com.hjq.demo.aop.Log;
@@ -137,6 +138,18 @@ protected void initData() {
137138
mPasswordView.setText(getString(INTENT_KEY_IN_PASSWORD));
138139
}
139140

141+
@Nullable
142+
@Override
143+
public View getImmersionTopView() {
144+
return findViewById(R.id.fl_login_bar);
145+
}
146+
147+
@Nullable
148+
@Override
149+
public View getImmersionBottomView() {
150+
return findViewById(R.id.ll_login_platform);
151+
}
152+
140153
@SingleClick
141154
@Override
142155
public void onClick(View view) {
@@ -356,9 +369,4 @@ protected ImmersionBar createStatusBarConfig() {
356369
// 指定导航栏背景颜色
357370
.navigationBarColor(R.color.white);
358371
}
359-
360-
@Override
361-
public View getImmersionView() {
362-
return findViewById(R.id.fl_login_bar);
363-
}
364372
}

app/src/main/java/com/hjq/demo/ui/activity/account/RegisterActivity.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.widget.EditText;
99
import android.widget.TextView;
1010
import androidx.annotation.NonNull;
11+
import androidx.annotation.Nullable;
1112
import com.gyf.immersionbar.ImmersionBar;
1213
import com.hjq.base.BaseActivity;
1314
import com.hjq.demo.R;
@@ -100,6 +101,17 @@ protected void initData() {
100101
mSecondPassword.setText(getString(INTENT_KEY_PASSWORD));
101102
}
102103

104+
@Override
105+
public View getImmersionTopView() {
106+
return findViewById(R.id.fl_register_container);
107+
}
108+
109+
@Nullable
110+
@Override
111+
public View getImmersionBottomView() {
112+
return findViewById(R.id.fl_register_container);
113+
}
114+
103115
@SingleClick
104116
@Override
105117
public void onClick(View view) {
@@ -235,11 +247,6 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
235247
return false;
236248
}
237249

238-
@Override
239-
public View getImmersionView() {
240-
return findViewById(R.id.fl_register_container);
241-
}
242-
243250
/**
244251
* 注册监听
245252
*/

0 commit comments

Comments
 (0)