Как получить размеры вида? - PullRequest
207 голосов
/ 10 ноября 2010

У меня есть представление, составленное из 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

Ответы [ 16 ]

414 голосов
/ 10 декабря 2010

Я полагаю, что ОП уже давно нет, но в случае, если этот ответ поможет будущим поисковикам, я решил опубликовать найденное решение. Я добавил этот код в мой 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 (здесь, похоже, нет метода суперкласса для вызова здесь ...) и добавляя мой код, который требует знания измерения зрения в этот слушатель. У меня все работает как положено, поэтому я надеюсь, что это поможет.

82 голосов
/ 03 июля 2011

Я просто добавлю альтернативное решение, переопределю метод onWindowFocusChanged вашей активности, и вы сможете получать значения getHeight (), getWidth () оттуда.

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}
19 голосов
/ 11 ноября 2010

Вы пытаетесь получить ширину и высоту элементов, которые еще не были нарисованы.

Если вы используете отладку и остановитесь в какой-то момент, вы увидите, что экран вашего устройства все еще пустэто потому, что ваши элементы еще не прорисованы, поэтому вы не можете получить ширину и высоту чего-то, что еще не существует.

И я могу ошибаться, но setWidth() не всегдауважаемый, Layout выкладывает свои дочерние элементы и решает, как их измерить (вызывая child.measure()), поэтому если вы установите setWidth(), вы не гарантированно получите эту ширину после отрисовки элемента.Что вам нужно, это использовать getMeasuredWidth() (самый последний показатель вашего вида) где-то после того, как вид был нарисован.

Изучите жизненный цикл Activity, чтобы найти лучший момент.

http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

Я считаю, что хорошей практикой является использование 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(), и он будет вызван, когдавиды будут выложены.

15 голосов
/ 06 декабря 2011

Используйте метод представления сообщения, как этот

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });
7 голосов
/ 11 июля 2012

Я пытался использовать onGlobalLayout(), чтобы выполнить произвольное форматирование TextView, но, как заметил @George Bailey, onGlobalLayout() действительно вызывается дважды: один раз на исходном пути макета и второй раз после изменения текста. .

View.onSizeChanged() работает лучше для меня, потому что, если я изменяю текст там, метод вызывается только один раз (во время прохода макета). Это требует подкласса TextView, но на уровне API 11+ View. addOnLayoutChangeListener() можно использовать, чтобы избежать подкласса.

Еще одна вещь, чтобы получить правильную ширину вида в View.onSizeChanged(), layout_width должен быть установлен на match_parent, а не wrap_content.

2 голосов
/ 02 января 2014

Как уже упоминалось в 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
    }
});

Вы можете удалить прослушиватель в обратном вызове, если вы хотите только первоначальный макет.*

2 голосов
/ 27 февраля 2011

Я думаю, это то, на что вам нужно обратить внимание: используйте onSizeChanged() вашего взгляда. Вот расширенный фрагмент кода о том, как использовать onSizeChanged() для динамического получения высоты и ширины макета или представления http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html

2 голосов
/ 10 ноября 2010

Вы пытаетесь получить размеры в конструкторе или любом другом методе, который запускается ДО того, как вы получите реальную картинку?

Вы не получите никаких измерений до фактического измерения всех компонентов (поскольку ваш xml не знает о вашем размере дисплея, родительских позициях и т. Д.)

Попробуйте получить значения после onSizeChanged () (хотя его можно вызвать с нуля) или просто подождите, когда вы получите реальное изображение.

1 голос
/ 12 июля 2018

Даже несмотря на то, что предлагаемое решение работает, оно может быть не лучшим решением для каждого случая, поскольку основано на документации для 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();
    }

});
1 голос
/ 21 августа 2012

ViewTreeObserver и onWindowFocusChanged() вовсе не нужны.

Если вы надуете TextView как макет и / или поместите в него какой-то контент и установите LayoutParams, тогда вы можете использовать getMeasuredHeight() и getMeasuredWidth().

НО Вы должны быть осторожнее с LinearLayouts (возможно, также с другими ViewGroups).Проблема в том, что вы можете получить ширину и высоту после onWindowFocusChanged(), но если вы попытаетесь добавить в нее некоторые виды, то вы не сможете получить эту информацию, пока все не будет нарисовано.Я пытался добавить несколько TextViews к LinearLayouts, чтобы имитировать FlowLayout (стиль упаковки), и поэтому не мог использовать Listeners.Как только процесс запущен, он должен продолжаться синхронно.Поэтому в таком случае вы можете захотеть сохранить ширину в переменной, чтобы использовать ее позже, поскольку при добавлении видов в макет она может понадобиться.

...