Вид 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)
}
}
})
}