问题
I have an Android app which has the following settings in its theme:
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
The top View in the hierarchy overrides onApplyWindowsInsets()
. The implementation just logs the WindowInsets
it was passed, then calls through to the superclass method, logs the result and returns it—nothing is modified. The log also shows that the superclass method does not modify the insets.
Now I am getting an odd-looking top inset, which always seems to be some 56–72 dp more than what I would expect it to be:
Platform DPI Layout Inset Bar Difference
Android x86_64 8.1 MDPI Landscape 88 px/dp 24 px/dp 64 px/dp
Landscape, split 80 px/dp 24 px/dp 56 px/dp
Anbox MDPI Window 64 px/dp None 64 px/dp
LineageOS 15.1 XXHDPI Portrait 240 px/80 dp 72 px/24 dp 168 px/56 dp
Portrait, split/top 216 px/73 dp 72 px/24 dp 144 px/48 dp
Portrait, split/bottom 144 px/48 dp None 144 px/48 dp
SDK Emulator (6.0) HDPI Portrait 120 px/80 dp 36 px/24 dp 84 px/56 dp
Note: in landscape mode, there are two consecutive calls to onApplyWindowInsets()
, only the last ones are reported here.
The view is not actually extended beyond the top of what is visible: If I draw a 24-px area at the top of my view, it appears at the top of the visible area (i.e. underneath the status bar if there is one), never off-screen.
If I were to trust these inset values and offset content by the number of pixels indicated to keep it clear of the system bars, I would end up with a gap at the top of the view.
Left/right/bottom values seem OK as far as I can tell.
How can I correct this? Am I overlooking something?
回答1:
As I still have no explanation for the values reported, I assume this is either a bug or a poorly documented feature, which is nevertheless present in many (if not all) versions of Android.
Luckily, at least on API 23+, there is another method to get window insets, namely by calling View#getWindow().getRootView().getRootWindowInsets()
. This seems to have some bugs of its own, namely reporting incorrect insets for the navigation bar, but merging the two results has worked for me.
- Store the insets passed to
onApplyWindowsInsets()
, discarding only the top one. - Override
onSizeChanged()
for the view, and in it callView#getRootWindowInsets()
to get the top inset (ignore the others).
The combined values have worked for me.
Unfortunately this will not work on API 20–22, which does not implement View#getRootWindowInsets()
. Here, you will need to do some guesswork. Fortunately, this is easier as these APIs do not implement split screen yet—the top inset will be the height of the status bar, unless you’ve explicitly requested that it be hidden (in which case it is 0). If the status bar is visible, its height can be determined as follows:
Resources resources = view.getResources();
int shid = resources.getIdentifier("status_bar_height", "dimen", "android");
padding_top = (shid > 0) ? resources.getDimensionPixelSize(shid) : 0;
来源:https://stackoverflow.com/questions/54558503/onapplywindowinsets-called-with-strange-top-inset