Android:为什么根据FEATURE_NO_TITLE,ContentView.getTop()会有差异?

在我看来,Android的坐标系存在问题.当我有普通视图时(不请求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进行编译,并在装有Android 2.3.4的Samsung Galaxy S上运行.

布局: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

解决方法:

is this an Android bug?

不会.在这两种情况下,系统构建的视图层次结构都略有不同,这可以避免不必要的深度(出于性能原因,更多(嵌套)布局元素意味着更多开销).

这是带有标题栏的层次结构的外观:

注意:我的像素值与您的像素值略有不同,因为我是在较小的仿真器上运行样本的.在这种情况下这无关紧要.

full size

基本的LinearLayout包含标题栏,内容布局将填充整个显示.这里的填充也为25px,因为状态栏覆盖了普通的UI层次结构.因此,必须通过填充保留一些空间.

然后跟随标题栏作为LinearLayout的第一个元素,其高度也为25 px.这意味着findViewById(Window.ID_ANDROID_CONTENT).getTop();总共返回50,因为contentview FrameLayout距离其父级LinearLayout的顶部50像素(还是25个填充25个标题).哪个是对的.

那么,删除标题栏时会发生什么?

full size

系统会完全剥离外部LinearLayout,这意味着contentview现在会填满整个屏幕. findViewById(Window.ID_ANDROID_CONTENT).getTop();应该在这里返回0,实际上它是做什么的.这没错.但是必须再次为状态栏保留空间.系统在此处将该填充分配给contentview.

如何修复

我认为解决方案非常简单:您必须包括contentview的padding才能获得准确的结果.例如.更改

int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();

View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);
int contentViewTop = contentView.getTop() + contentView.getPaddingTop();

这会为我打印正确的结果.

上一篇:android-显示/隐藏状态栏不应影响活动状态


下一篇:如何忽略android 3.0中“最近活动按钮”的按钮单击