Android: почему в ContentView.getTop() есть расхождение в зависимости от FEATURE_NO_TITLE?

Мне кажется, что есть проблема в системе координат Android. Когда у меня нормальный вид (без запроса FEATURE_NO_TITLE), Я тогда получаю int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();, Это дает мне 76px: 38px для строки состояния, и 38px для строки заголовка.

Тем не менее, если я прошу FEATURE_NO_TITLE и затем повторите процедуру, getTop() возвращается 0px Несмотря на то, что строка состояния все еще видна!

Это несоответствие не должно иметь значения, потому что мы обычно не заботимся о том, где начинается представление контента. Тем не менее, это имеет значение для меня, потому что я размещаю представления на виде декора, который охватывает все видимое окно.

Я знаю, что это не уловка заголовка и плотности устройства, потому что, если я запрашиваю пользовательский заголовок и даю его 0 высота, то getTop() возвращается 38px,

Решение / обходной путь заключается в добавлении 38 пикселей вручную при запросе FEATURE_NO_TITLE, Мой вопрос: это ошибка Android? Или я что-то не понимаю в том, как работают макеты, которые делают это поведение понятным?

Заранее спасибо!

Вот минимальная программа, которая воспроизводит проблему. Запустите его дважды и раскомментируйте указанную строку. Я работаю на Android SDK 7 и работаю на Samsung 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);
            }
        });

    }
}

Рекомендации

2 ответа

Решение

это ошибка Android?

Нет. Система просто строит иерархию представления, немного отличающуюся в обоих случаях, избегая ненужной глубины (по соображениям производительности, больше (вложенных) элементов макета означают больше накладных расходов).

Вот как выглядит иерархия с заголовком:

Примечание: мои значения пикселей немного отличаются от ваших, потому что я запустил ваш образец на меньшем эмуляторе. Это не должно иметь значения для этого случая.

полный размер

Основной LinearLayout, который содержит заголовок и ваш макет контента, заполняет весь экран. Здесь также есть отступ 25px, потому что строка состояния перекрывает нормальную иерархию пользовательского интерфейса. Таким образом, должно быть некоторое пространство, зарезервированное с помощью отступов.

Затем следует заголовок заголовка в качестве первого элемента LinearLayout, также с высотой 25 пикселей. Который означает, что findViewById(Window.ID_ANDROID_CONTENT).getTop(); возвращает 50 всего, потому что Contentview FrameLayout находится в 50 пикселях от вершины его родительского LinearLayout (опять же 25 отступов + 25 заголовков). Что правильно.

Итак, что происходит, когда заголовок удаляется?

полный размер

Система полностью удаляет внешний LinearLayout, что означает, что представление контента теперь занимает весь экран. findViewById(Window.ID_ANDROID_CONTENT).getTop(); должен вернуть 0 здесь, что это делает infact. Это не так. Но должно быть зарезервировано место для строки состояния снова. Система присвоила этот отступ представлению содержимого здесь.

Как это исправить

Я думаю, что решение довольно простое: чтобы получить точные результаты, вы должны включить заполнение представления контента. Например, изменить

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

в

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

Это печатает правильные результаты для меня.

Это зависит от того, какую версию Android вы используете, я думаю. Я попробовал ваш код на Samsung Galaxy Y (2.3.6) и HTC Desire HD (2.3.5), и это произошло так, как вы описали. Однако с HTC ONE V (4.0.3) результат оказался верным, как и ожидалось. Вершина просмотра без заголовка вернула 0 пикселей. Таким образом, вы не должны полагаться на этот вывод, так как он может работать некорректно на каждом телефоне.

Другие вопросы по тегам