Вид getWidth() и getHeight() возвращает 0

Я создаю все элементы в моем проекте android динамически. Я пытаюсь получить ширину и высоту кнопки, чтобы я мог повернуть эту кнопку вокруг. Я просто пытаюсь научиться работать с языком андроид. Однако, она возвращает 0.

Я сделал некоторые исследования, и я увидел, что это нужно делать где-то кроме onCreate() метод. Если кто-то может дать мне пример, как это сделать, это было бы здорово.

вот мой нынешний код:

package com.animation;

import android.app.Activity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;

public class AnimateScreen extends Activity {


//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LinearLayout ll = new LinearLayout(this);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(30, 20, 30, 0);

    Button bt = new Button(this);
    bt.setText(String.valueOf(bt.getWidth()));

    RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2);
    ra.setDuration(3000L);
    ra.setRepeatMode(Animation.RESTART);
    ra.setRepeatCount(Animation.INFINITE);
    ra.setInterpolator(new LinearInterpolator());

    bt.startAnimation(ra);

    ll.addView(bt,layoutParams);

    setContentView(ll);
}

любая помощь приветствуется.

10 ответов


вы призываете getWidth() слишком рано. Пользовательский интерфейс еще не был определен и выложен на экран.

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

Это, как говорится, вы можете использовать измерение ресурсов чтобы определить размер кнопки, а затем ссылаться на этот ресурс измерения из вашего файла макета и исходного кода, чтобы избежать этой проблемы.


основная проблема заключается в том, что вам нужно дождаться фазы рисования для фактических измерений (особенно с динамическими значениями, такими как wrap_content или match_parent), но обычно эта фаза не была завершена до onResume(). Таким образом, вам нужно обходной путь для ожидания этого этапа. Существуют различные возможные решения для этого:


1. Прослушивание событий Draw / Layout: ViewTreeObserver

ViewTreeObserver увольняется для другого чертежа события. Обычно OnGlobalLayoutListener это то, что вы хотите для получения измерения, поэтому код в прослушивателе будет вызываться после фазы макета, поэтому измерения готовы:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                view.getHeight(); //height is ready
            }
        });

Примечание: слушатель будет немедленно удален, потому что в противном случае он будет срабатывать на каждое событие layout. Если вам нужно поддерживать приложения SDK Lvl использовать это, чтобы отменить регистрацию слушателя:

public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)


2. Добавьте runnable в очередь компоновки: вид.post ()

не очень хорошо известно, и мое любимое решение. В основном просто используйте метод post представления с вашим собственным runnable. Это в основном очереди вашего кода после вид, макет и т. д. как указано Ромен Парень:

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

пример:

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });

преимущество ViewTreeObserver:

  • ваш код выполняется только один раз, и вам не нужно отключать наблюдателя после выполнения, что может быть хлопот
  • меньше информации синтаксис

ссылки:


3. Метод onLayout перезаписи представлений

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

view = new View(this) {
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        view.getHeight(); //height is ready
    }
};

также имейте в виду, что onLayout будет вызываться много раз, поэтому будьте внимательны, что вы делаете в методе, или отключите свой код после первого раза


4. Проверьте, прошел ли этап компоновки

если у вас есть код, который выполняется несколько раз при создании пользовательского интерфейса, вы можете использовать следующий метод поддержки V4 lib:

View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
   viewYouNeedHeightFrom.getHeight();
}

возвращает true, если view прошел по крайней мере один макет, так как это было последний прикреплен к окну или отсоединен от него.


дополнительно: получение статически определенных измерений

если достаточно просто получить статически определенную высоту / ширину, вы можете просто сделать это с помощью:

но имейте в виду, что это может отличаться от фактической ширины / высоты после рисунок. Документация javadoc прекрасно описывает разницу:

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

первая пара известна как измеренная ширина и измеренная высота. Эти размеры определяют, насколько большим должно быть представление внутри родительского элемента (см. Макет для более подробной информации.) Измеренные размеры могут быть получены путем звоню getMeasuredWidth() и getMeasuredHeight ().

вторая пара просто известна как ширина и высота, или иногда ширина чертежа и высота чертежа. Эти измерения определяют фактическое размер вида на экране, во время рисования и после компоновки. Эти значения могут, но не должны отличаться от измеренной ширины и рост. Ширину и высоту можно получить, вызвав getWidth() и getHeight().


можно использовать

@Override
 public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  //Here you can get the size!
 }

я использовал это решение, которое я думаю, лучше, чем onWindowFocusChanged(). Если открыть DialogFragment, а затем повернуть телефон, onWindowFocusChanged будет вызываться только тогда, когда пользователь закроет диалоговое окно):

    yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once :
            yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

            // Here you can get the size :)
        }
    });

Edit: поскольку removeGlobalOnLayoutListener устарел, теперь вы должны сделать:

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {

    // Ensure you call it only once :
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
        yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    else {
        yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    // Here you can get the size :)
}

Как утверждает Ян в этот поток разработчиков Android:

во всяком случае, сделка заключается в том, что макет содержимое окна происходит после все элементы построены и добавлены к их родителю просмотр. Это должно быть так, потому что пока вы не знаете, какие компоненты вид содержит, и что они содержат, и так что, нет никакого разумного способа, которым вы можете выкладывай.

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

имейте в виду, что onSizeChanged() иногда вызывается с параметрами zero -- проверьте для этого случая, и вернитесь немедленно (так что вы не получите деление на ноль при вычислении ваш макет и т. д.). Некоторое время спустя будет вызываться с реальными значениями.


Если вам нужно получить ширину некоторого виджета до его отображения на экране, вы можете использовать getMeasuredWidth() или getMeasuredHeight().

myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = myImage.getMeasuredWidth();
int height = myImage.getMeasuredHeight();

Я бы предпочел использовать OnPreDrawListener() вместо addOnGlobalLayoutListener(), так как это называли раньше.

    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            if (view.getViewTreeObserver().isAlive())
                view.getViewTreeObserver().removeOnPreDrawListener(this);

            // put your code here
            return false;
        }
    });

один лайнер, если вы используете RxJava & RxBindings. Аналогичный подход без шаблона. Это также решает взломать подавить предупреждения, как в ответе на Тим Информация Autin.

RxView.layoutChanges(yourView).take(1)
      .subscribe(aVoid -> {
           // width and height have been calculated here
      });

вот оно. Не нужно отписываться, даже если никогда не звонил.


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

использование:

view.height { Log.i("Info", "Here is your height:" + it) }

реализация:

fun <T : View> T.height(function: (Int) -> Unit) {
    if (height == 0)
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                function(height)
            }
        })
    else function(height)
}

Gone views возвращает 0 в качестве высоты, если приложение в фоновом режиме. Это мой код (1oo% работает)

fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            measure(widthSpec, heightSpec)
            postJob(this@postWithTreeObserver, measuredWidth, measuredHeight)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                @Suppress("DEPRECATION")
                viewTreeObserver.removeGlobalOnLayoutListener(this)
            } else {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}