Як отримати розміри подання?


209

У мене є погляд TableLayout, TableRow and TextView. Я хочу, щоб це виглядало як сітка. Мені потрібно отримати висоту і ширину цієї сітки. Методи getHeight()і getWidth()завжди повертають 0. Це відбувається, коли я динамічно форматую сітку, а також коли використовую версію XML.

Як отримати розміри для перегляду?


Ось моя тестова програма, яку я використовував у налагодженні для перевірки результатів:

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

замість використання getWidth / Height використовувати getMeasuredWidth / Height після того, як макет буде застосований до діяльності.
бухи

9
Хлопці, все, що вам потрібно зробити, - це зателефонувати, getHeight()і getWidth()після того, як життєвий цикл діяльності попросить погляди виміряти себе, іншими словами, зробіть такі речі onResume()і все. Не варто сподіватися, що ще не викладений об’єкт дізнається його розміри, у цьому вся хитрість. Немає необхідності в магії, запропонованій нижче.
укладальник класів

2
Виклик getHeight()/Width()в onResume()не піддавалися> 0 значення для мене.
Дарпан

@ClassStacker Що з фрагментом?
здд

@zdd Будь ласка, задайте повністю нове запитання та опублікуйте посилання тут у коментарі.
укладальник класів

Відповіді:


418

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


23
@George Bailey: Одне, що я нещодавно бачив хтось інший пост (я його ще не перевіряв, тому YMMV) для подолання декількох дзвінків було (в межах onGlobalLayout ()): tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);таким чином слухача слід видалити після першого разу це відбувається.
Кевін Коппок

3
Я теж хочу допомогти. Можливо, вам доведеться зробити щось на зразок: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); якщо ви хочете змінити розмір перегляду лише один раз. Сподіваюся, що це допоможе
Генрі Су

7
Питання дуже старе, але з використанням addOnLayoutChangeListenerтворів також! :)
FX

7
Також зауважте, що з API 16 вам потрібно: якщо (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (це); } else {view.getViewTreeObserver (). RemoveGlobalOnLayoutListener (це); }
Едісон

2
Оскільки устарення RemoveGlobalOnLayoutListener застаріле, воно все ще дає попередження у Eclipse. Це нормально? З часом застарілі попередження про код затоплять усюди ...
Jonny

82

Я просто додам альтернативне рішення, замініть метод onWindowFocusChanged вашої діяльності, і ви зможете отримати значення getHeight (), getWidth () звідти.

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

4
Цитуючи документацію, "Це найкращий показник того, чи бачить ця діяльність користувач".
Камерон Лоуелл Палмер

6
Це не працює в разі використання фрагментів, які вам доведеться використовувати ViewTreeObserver.
Міхір

ОП спеціально запитала, як це отримати для перегляду
JustDanyul

загальна висота scrollview та макс. Scrollview.getScrolly () будуть однаковими? тобто в моєму випадку я отримав Висота з вищевказаним методом - 702, і я отримав максимальне значення від getScrolly () - 523,
Hitesh Dhamshaniya

Цей метод також не працює, коли ваш перегляд додається з іншого файлу xml.
Джей Донга

19

Ви намагаєтеся отримати ширину та висоту елементів, які ще не були намальовані.

Якщо ви скористаєтеся налагодженням і зупинитесь в якийсь момент, ви побачите, що екран вашого пристрою все ще порожній, це тому, що ваші елементи ще не були намальовані, тому ви не можете отримати ширину і висоту чогось, що ще не існує існують.

І, можливо, я помиляюся, але 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(), і він буде викликаний, коли буде викладений вид.


На жаль, він також не працює в OnResume. Остаточні розміри встановлюються десь після виклику OnResume, принаймні в емуляторі.
jki

Це жахлива практика !!! Цей слухач буде передзвонюватися знову і знову і вбиватиме ваше виконання. Замість використання прапорів скасуйте реєстрацію слухача.
bluewhile

15

Використовуйте такий спосіб публікації View, як це

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

1
Це працює, але насправді, без обговорення чому? Btw, це працює, тому що виконується не запускається, поки не відбудеться макет.
Норман Н

7

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

View.onSizeChanged()працює для мене краще, тому що якщо я модифікую текст там, метод викликається лише один раз (під час проходу макета). Це вимагало підкласифікації TextView, але на рівні 11+ APIView. addOnLayoutChangeListener() можна використовувати, щоб уникнути підкласифікації.

Ще одне, щоб отримати правильну ширину перегляду в View.onSizeChanged(), layout_widthслід встановити match_parent, а не wrap_content.


2

Ви намагаєтеся отримати розміри в конструкторі чи будь-якому іншому методі, який запускається перед тим, як отримати фактичну картину?

Ви не отримаєте жодних розмірів, перш ніж всі компоненти будуть фактично виміряні (оскільки ваш xml не знає про розмір дисплея, позиції батьків та інше)

Спробуйте отримати значення після onSizeChanged () (хоча це можна назвати нулем) або просто чекати, коли ви отримаєте фактичне зображення.


Я оновив своє первісне запитання та код, щоб бути зрозумілішими. Дякую.

2

Як згадував 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
    }
});

Ви можете видалити слухача з зворотного дзвінка, якщо ви хочете лише початковий макет.



1

ViewTreeObserverі onWindowFocusChanged()зовсім не такі необхідні.

Якщо ви надуєте TextViewяк макет і / або помістите в нього якийсь вміст і встановите, LayoutParamsви можете використовувати getMeasuredHeight()і getMeasuredWidth().

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


1

Незважаючи на те, що запропоноване рішення працює, воно може бути не найкращим рішенням для кожного випадку, оскільки виходячи з документації на 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();
    }

});


0

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

Висота та ширина дорівнюють нулю, оскільки представлення не було створено до моменту, коли ви запитуєте, це висота та ширина. Одне з найпростіших рішень

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

Цей метод хороший порівняно з іншими методами, оскільки він короткий і чіткий.


-1

Проста відповідь: це спрацювало для мене без проблем. Здається, ключовим є переконання, що у "Перегляді" є фокус, перш ніж ви отримуєте висоту тощо. Зробіть це, використовуючи метод hasFocus (), а потім використовуючи метод getHeight () у цьому порядку. Всього потрібно 3 рядки коду.

ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1); myImageButton1.hasFocus();

int myButtonHeight = myImageButton1.getHeight();

Log.d("Button Height: ", ""+myButtonHeight );//Not required

Сподіваюся, це допомагає.


-3

Використовуйте для перегляду getMeasuredWidth () та getMeasuredHeight ().

Посібник для розробників: Перегляд


4
Як зазначено раніше, ці методи повертають лише дійсний результат після того, як розмір вимірюється. У межах діяльності це справедливо, коли метод onWindowFocusChanged os викликав.
слот

-8

КОРЕКЦІЯ: Я з’ясував, що вищезгадане рішення є жахливим. Особливо, коли ваш телефон повільний. І ось, я знайшов інше рішення: обчислити значення px елемента, включаючи поля та paddings: dp to px: https://stackoverflow.com/a/6327095/1982712

або dimens.xml до px: https://stackoverflow.com/a/16276351/1982712

sp до px: https://stackoverflow.com/a/9219417/1982712 (скасувати рішення)

або зменшується до px: https://stackoverflow.com/a/16276351/1982712

і це все.


10
Зазвичай погана ідея сподіватися, що все буде встановлено правильно через деяку довільно вибрану кількість мілісекунд. Пристрій може бути повільним, інші процеси / потоки можуть працювати, не працюватимуть у налагоджувачі, можуть не працювати в наступній версії ОС, ...
Крістофер Джонсон,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.