it-swarm.com.ru

Как получить размеры вида?

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

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

410
kcoppock

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

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}
82
JustDanyul

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

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

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

19
Alex Orlov

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

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });
14
Boris Rusev

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

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

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

7
Y2i

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

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

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

2
Alex Orlov

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

2
Syed Rakib Al Hasan

Как F.X. упомянуто, вы можете использовать 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
Zeophlite

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

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

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

1
Genom

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

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

view.post(new Runnable() {
    @Override
    public void run() {
        view.getHeight(); //height is ready
        view.getWidth(); //width is ready
    }
});

Этот метод хорош по сравнению с другими методами, поскольку он короткий и четкий.

0
Shivam Yadav

Вам лучше взглянуть на View жизненный цикл: http://developer.Android.com/reference/Android/view/View.html Обычно вы не должны знать ширину и высоту наверняка, пока ваша активность не перейдет в состояние onResume.

0
Andrey Novikov

Вы можете использовать трансляцию, которая вызывается в 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 ответил

0
Lucas Ferreira Batista