在我看来,安卓的坐标系有问题。当我有一个普通视图(没有请求FEATURE_NO_TITLE
)时,我将检索int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
。这给了我状态栏的76px
:38px
和标题栏的38px
。
但是,如果我请求FEATURE_NO_TITLE
然后重复该过程,getTop()
将返回0px
,尽管状态栏仍然可见!
这种差异不应该有什么区别,因为我们通常不关心内容视图从哪里开始。不过,这对我来说确实很重要,因为我将视图放置在装饰视图上——它覆盖了整个可见窗口。
我知道这不是标题栏和设备密度的技巧,因为如果我请求自定义标题栏,并给它0
高度,那么getTop()
返回38px
。
解决方案/解决方法是在请求FEATURE_NO_TITLE
时手动添加38个像素。我的问题是:这是一个android错误吗?或者有什么我不理解的东西,布局是如何工作的,使这种行为可以理解?
提前谢谢!
这是一个极小的程序,再现了这个问题。运行两次,并取消注释指定的行。我是根据android sdk 7编译的,运行在三星galaxy s和android 2.3.4上。
布局: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>
代码:
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);
}
});
}
}
工具书类
Similar question that seems to sidestep the issue?
Source question that helped make this example
最佳答案
这是安卓系统的错误吗?
不。系统只是构建视图层次结构,两者略有不同,这样做可以避免不必要的深度(出于性能原因,更多(嵌套的)布局元素意味着更多的开销)。
下面是标题栏的层次结构:
注意:我的像素值与您的略有不同,因为我在一个较小的模拟器上运行了您的示例。这件事不重要。
full size
包含标题栏和内容布局的基本线性布局将填充整个显示。这里还有一个25px的填充,因为状态栏覆盖了正常的ui层次结构。所以必须通过填充保留一些空间。
然后跟随标题栏作为线性布局的第一个元素,同样具有25 px的高度。这意味着findViewById(Window.ID_ANDROID_CONTENT).getTop();
总共返回50,因为contentview framelayout距离其父linearlayout的顶部有50个像素(同样是25个padding+25个title)。这是正确的。
那么,当车头标被移除时会发生什么?
full size
系统将完全删除外部线性布局,这意味着ContentView现在将填充整个屏幕。findViewById(Window.ID_ANDROID_CONTENT).getTop();
应该在这里返回0,它实际上是做什么的。这不是错的。但必须为状态栏再次预留空间。系统将该填充分配给此处的ContentView。
如何修复
我认为解决方案非常简单:必须包含contentview的填充以获得准确的结果。例如改变
int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
到
View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);
int contentViewTop = contentView.getTop() + contentView.getPaddingTop();
这给我打印了正确的结果。