Как получить размеры вида?
У меня есть представление, состоящее из TableLayout, TableRow and TextView
, Я хочу, чтобы это выглядело как сетка. Мне нужно получить высоту и ширину этой сетки. Методы getHeight()
а также getWidth()
всегда возвращайте 0. Это происходит, когда я динамически форматирую сетку, а также когда я использую версию XML.
Как получить размеры для вида?
Вот моя тестовая программа, которую я использовал в Debug для проверки результатов:
import android.app.Activity;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TextView;
public class appwig extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
int vh = 0;
int vw = 0;
//Test-1 used the xml layout (which is displayed on the screen):
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight(); //<- getHeight returned 0, Why?
vw = tl.getWidth(); //<- getWidth returned 0, Why?
//Test-2 used a simple dynamically generated view:
TextView tv = new TextView(this);
tv.setHeight(20);
tv.setWidth(20);
vh = tv.getHeight(); //<- getHeight returned 0, Why?
vw = tv.getWidth(); //<- getWidth returned 0, Why?
} //eof method
} //eof class
15 ответов
Я считаю, что ОП уже давно нет, но в случае, если этот ответ поможет будущим поисковикам, я решил опубликовать найденное мной решение. Я добавил этот код в свой onCreate()
метод:
ИЗДАН: 07/05/11, чтобы включить код из комментариев:
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
LayerDrawable ld = (LayerDrawable)tv.getBackground();
ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
ViewTreeObserver obs = tv.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
}
});
Сначала я получаю окончательную ссылку на мой TextView
(для доступа в onGlobalLayout()
метод). Далее я получаю ViewTreeObserver
от моего TextView
и добавить OnGlobalLayoutListener
переопределяя onGLobalLayout
(похоже, здесь нет метода суперкласса для вызова...) и добавления моего кода, который требует знания измерений представления в этом слушателе. У меня все работает как положено, поэтому я надеюсь, что это поможет.
Я просто добавлю альтернативное решение, переопределю метод onWindowFocusChanged вашей деятельности, и вы сможете получать значения getHeight(), getWidth() оттуда.
@Override
public void onWindowFocusChanged (boolean hasFocus) {
// the height will be set at this point
int height = myEverySoTallView.getMeasuredHeight();
}
Вы пытаетесь получить ширину и высоту элементов, которые еще не были нарисованы.
Если вы используете отладку и остановитесь в какой-то момент, вы увидите, что экран вашего устройства по-прежнему пуст, это потому, что ваши элементы еще не прорисованы, поэтому вы не можете получить ширину и высоту чего-либо, что еще нет существовать.
И я могу ошибаться, но setWidth()
не всегда соблюдается, Layout
выкладывает детей и решает, как их измерить child.measure()
), так что если вы установите setWidth()
Вы не гарантированно получите эту ширину после отрисовки элемента.
Что вам нужно, это использовать getMeasuredWidth()
(самая последняя мера вашего вида) где-то после того, как вид был нарисован.
Заглянуть в Activity
Жизненный цикл для поиска лучшего момента.
http://developer.android.com/reference/android/app/Activity.html
Я считаю, что хорошей практикой является использование OnGlobalLayoutListener
как это:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!mMeasured) {
// Here your view is already layed out and measured for the first time
mMeasured = true; // Some optional flag to mark, that we already got the sizes
}
}
});
Вы можете разместить этот код прямо в onCreate()
и он будет вызван, когда будут выложены представления.
Используйте метод сообщения View, как этот
post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
}
});
Я пытался использовать onGlobalLayout()
сделать некоторое пользовательское форматирование TextView
, но, как заметил @ Джордж Бейли, onGlobalLayout()
действительно вызывается дважды: один раз на начальном пути макета и второй раз после изменения текста.
View.onSizeChanged()
работает лучше для меня, потому что, если я изменю текст там, метод вызывается только один раз (во время прохождения макета). Это требует подклассификации TextView
, но на уровне API 11+ View. addOnLayoutChangeListener()
может использоваться, чтобы избежать подклассов.
Еще одна вещь, чтобы получить правильную ширину вида в View.onSizeChanged()
, layout_width
должен быть установлен в match_parent
не wrap_content
,
Вы пытаетесь получить размеры в конструкторе или любом другом методе, который запускается ДО того, как вы получите реальную картинку?
Вы не получите никаких измерений, пока все компоненты не будут измерены (так как ваш xml не знает о размере вашего дисплея, родительских позициях и т. Д.)
Попробуйте получить значения после onSizeChanged() (хотя он может быть вызван с нуля) или просто подождите, когда вы получите реальное изображение.
Я думаю, это то, что вам нужно посмотреть: использовать onSizeChanged()
с вашей точки зрения. Вот расширенный фрагмент кода о том, как использовать onSizeChanged()
для динамического определения высоты и ширины макета или представления http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html
Как уже упоминалось FX, вы можете использовать OnLayoutChangeListener
к мнению, что вы хотите отслеживать себя
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Make changes
}
});
Вы можете удалить слушателя в обратном вызове, если вы хотите только первоначальный макет.
ViewTreeObserver
а также onWindowFocusChanged()
совсем не нужны.
Если вы надуть TextView
как макет и / или положить некоторый контент в нем и установить LayoutParams
тогда вы можете использовать getMeasuredHeight()
а также getMeasuredWidth()
,
НО вы должны быть осторожны сLinearLayouts
(может быть и другие ViewGroups
). Проблема в том, что вы можете получить ширину и высоту после onWindowFocusChanged()
но если вы попытаетесь добавить в него некоторые представления, вы не сможете получить эту информацию, пока все не будет нарисовано. Я пытался добавить несколько TextViews
в LinearLayouts
подражать FlowLayout
(стиль упаковки) и поэтому не мог использовать Listeners
, Как только процесс запущен, он должен продолжаться синхронно. Поэтому в таком случае вы можете захотеть сохранить ширину в переменной, чтобы использовать ее позже, поскольку при добавлении видов в макет она может понадобиться.
Несмотря на то, что предлагаемое решение работает, оно может быть не лучшим решением для каждого случая, потому что на основе документации для ViewTreeObserver.OnGlobalLayoutListener
Определение интерфейса для обратного вызова, вызываемого при изменении состояния глобального макета или видимости представлений в дереве представлений.
это означает, что он вызывается много раз, и не всегда вид измеряется (для него определены высота и ширина)
Альтернативой является использование ViewTreeObserver.OnPreDrawListener
который вызывается только тогда, когда вид готов к рисованию и имеет все свои измерения.
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public void onPreDraw() {
tv.getViewTreeObserver().removeOnPreDrawListener(this);
// Your view will have valid height and width at this point
tv.getHeight();
tv.getWidth();
}
});
Высота и ширина равны нулю, потому что представление не было создано к тому времени, когда вы запрашиваете его высоту и ширину. Одним из самых простых решений является
view.post(new Runnable() {
@Override
public void run() {
view.getHeight(); //height is ready
view.getWidth(); //width is ready
}
});
Этот метод хорош по сравнению с другими методами, поскольку он короткий и четкий.
Вам лучше взглянуть на View жизненный цикл: http://developer.android.com/reference/android/view/View.html Как правило, вы не должны знать ширину и высоту наверняка, пока ваша активность не перейдет в состояние onResume.
Вы можете использовать трансляцию, которая вызывается в OnResume ()
Например:
int vh = 0;
int vw = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight();
vw = tl.getWidth();
}
}, new IntentFilter("Test"));
}
protected void onResume() {
super.onResume();
Intent it = new Intent("Test");
sendBroadcast(it);
}
Вы не можете получить высоту представления в OnCreate (), onStart () или даже в onResume () по причине того, что kcoppock ответил
Простой ответ: Это сработало для меня без проблем. Кажется, ключ в том, чтобы убедиться, что представление имеет фокус, прежде чем вы получите getHeight и т. Д. Сделайте это с помощью метода hasFocus(), а затем с помощью метода getHeight() в этом порядке. Требуется всего 3 строки кода.
ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1);
myImageButton1.hasFocus();
int myButtonHeight = myImageButton1.getHeight();
Log.d("Button Height: ", ""+myButtonHeight );//Not required
Надеюсь, поможет.
Используйте getMeasuredWidth() и getMeasuredHeight() для вашего представления.
ИСПРАВЛЕНИЕ: Я обнаружил, что вышеуказанное решение ужасно. Особенно, когда ваш телефон работает медленно. И здесь я нашел другое решение: вычислить значение px элемента, включая поля и отступы: от dp до px: /questions/3091946/preobrazovanie-pikselej-v-dp/3091956#3091956
или измерения. xml в px: /questions/34461604/zagruzit-znachenie-izmereniya-iz-resvalues-dimensionxml-iz-ishodnogo-koda/34461639#34461639
sp to px: /questions/29653171/konvertirovat-pikseli-v-sp/29653178#29653178 (отменить решение)
или размеры в px: /questions/34461604/zagruzit-znachenie-izmereniya-iz-resvalues-dimensionxml-iz-ishodnogo-koda/34461639#34461639
и это все.