Android: Why is there a discrepancy in ContentView.getTop() depending on FEATURE_NO_TITLE?
android, custom-titlebar, layout, statusbar, titlebar
Solution
is this an Android bug?
No. The system just builds the view hierachy slightly different in both cases do avoid unneccessary depth (for performance reasons, more (nested) layout elements mean more overhead).
Here is how the hierachy looks with a titlebar:
Note: my pixel values are slightly different from yours because I ran your sample on a smaller emulator. That shouldn't matter for this case.
full size
The basic LinearLayout that holds the titlebar and your content layout fills the whole display. It also has a padding of 25px here, because the status bar overlays the normal UI hierachy. So there has to be some space reserved via padding.
Then follows the titlebar as the first element of the LinearLayout, also with 25 px height. Which means that `findViewById(Window.ID_ANDROID_CONTENT).getTop();` returns 50 in total, because the contentview FrameLayout is 50 pixels from the top of it's parent LinearLayout (again 25 padding + 25 title). Which is correct.
So, what happens when the titlebar gets removed?
full size
The system strips the outer LinearLayout completely, which means that the contentview now fills the whole screen. `findViewById(Window.ID_ANDROID_CONTENT).getTop();` should return 0 here, what it infact does. This is not wrong. But there has to be reserved space for the statusbar again. The system assigned that padding to the contentview here.
How to fix it
I think the solution is pretty straightforward: You have to include the padding of the contentview to get exact results. E.g. change
int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
to
View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);
int contentViewTop = contentView.getTop() + contentView.getPaddingTop();
This prints the correct results for me.
Problem
It appears to me that there is a problem in Android's coordinate system. When I have a normal view (without requesting `FEATURE_NO_TITLE`), I then retrieve `int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();`. This gives me `76px`: `38px` for the status bar, and `38px` for the title bar. However, if I request `FEATURE_NO_TITLE` and then repeat the procedure, `getTop()` returns `0px`, despite the fact that the status bar is still visible! This discrepancy shouldn't make a difference, because we don't usually care where the content view starts. However, it does matter to me because I position views on the decor view -- which covers the entire visible window. I know that this is not a trick of the titlebar and the density of the device, because if I request a custom titlebar, and give it `0` height, then `getTop()` returns `38px`. The solution/workaround is to add 38 pixels manually when requesting `FEATURE_NO_TITLE`. My question is: is this an Android bug? Or is there something I'm not understanding about how layouts work that would make this behavior understandable? Thanks in advance! Here is a minimal program that reproduces the problem. Run it twice, and uncomment the indicated line. I am compiling against Android SDK 7, and running on a Samsung Galaxy S with Android 2.3.4. Layout: `main.xml` ``` <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layoutParent" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:id="@+id/someId" android:layout_width="fill_parent" android:layout_height="fill_parent" > </LinearLayout> </RelativeLayout> ``` Code: `TestStatusBarActivity.java` ``` package com.test.teststatusbar; import android.app.Activity; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.Window; import android.widget.RelativeLayout; public class TestStatusBarActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Uncomment the following line to see the alternate behavior //requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); RelativeLayout layoutParent = (RelativeLayout) findViewById(R.id.layoutParent); View something = findViewById(R.id.someId); something.setBackgroundColor(Color.CYAN); ViewTreeObserver vto = layoutParent.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // decor window top Rect rectgle = new Rect(); Window window = getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rectgle); int StatusBarHeight = rectgle.top; // "content view" top int contentViewTop = window.findViewById( Window.ID_ANDROID_CONTENT).getTop(); int TitleBarHeight = contentViewTop - StatusBarHeight; Log.i("STATUSBARTEST", "StatusBar Height = " + StatusBarHeight + " , TitleBar Height = " + TitleBarHeight + ", Content top = " + contentViewTop); } }); } } ``` References - Similar question that seems to sidestep the issue? - Source question that helped make this example