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 пикселей. Таким образом, вы не должны полагаться на этот вывод, так как он может работать некорректно на каждом телефоне.