Методы getWidth() и getHeight() у View возвращают 0
У меня такая проблема: я создаю все элементы динамически в своем проекте для Android. Я пытаюсь получить ширину и высоту кнопки, чтобы затем повернуть эту кнопку. Я только начинаю осваивать язык программирования для 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 {
// Вызывается при создании активности
@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);
}
}
Помогите, пожалуйста, разобраться, как правильно получать размеры кнопки для вращения.
5 ответ(ов)
Чтобы получить размер окна в методе onWindowFocusChanged
, вы можете использовать следующий код:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// Здесь вы можете получить размер!
}
В данном методе, когда ваше окно получает или теряет фокус, можно добавить логику для получения размеров окна. Например, вы можете использовать getWindow().getDecorView().getWidth()
и getWindow().getDecorView().getHeight()
, чтобы получить ширину и высоту окна соответственно. Однако, стоит учитывать, что этот метод вызывается каждый раз при изменении фокуса, так что убедитесь, что вы проверяете состояние hasFocus
перед выполнением операции, если это необходимо.
Я использовал это решение, которое, на мой взгляд, лучше, чем onWindowFocusChanged(). Если открывается DialogFragment, то onWindowFocusChanged будет вызван только тогда, когда пользователь закроет диалог.
Вот код:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Убедитесь, что вызываете это только один раз:
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Здесь вы можете получить размер :)
}
});
Важно: так как метод removeGlobalOnLayoutListener устарел, теперь следует использовать следующий подход:
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
// Убедитесь, что вызываете это только один раз:
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// Здесь вы можете получить размер :)
}
Таким образом, код будет работать корректно на более новых версиях Android, и вы избежите использования устаревших методов.
Если вам нужно получить ширину какого-либо виджета до его отображения на экране, вы можете использовать методы getMeasuredWidth()
и getMeasuredHeight()
.
Для этого необходимо вызвать метод measure()
у вашего виджета, указав параметры WRAP_CONTENT
для ширины и высоты. Вот пример кода:
myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = myImage.getMeasuredWidth();
int height = myImage.getMeasuredHeight();
Таким образом, после вызова measure()
вы сможете получить размеры вашего виджета, даже если он еще не отображен на экране.
AndroidX предоставляет несколько функций-расширений, которые могут помочь вам с подобной задачей в androidx.core.view
.
Вам необходимо использовать Kotlin для этого.
Функция, которая наилучшим образом подходит для вашего случая, — это doOnLayout
:
Выполняет заданное действие, когда этот вид размечен. Если вид уже был размечен и не запросил новую разметку, действие будет выполнено немедленно. В противном случае действие будет выполнено после следующей разметки вида.
Действие будет вызвано только один раз на следующей разметке, а затем удалено.
В вашем примере это будет выглядеть так:
bt.doOnLayout {
val ra = RotateAnimation(0, 360, it.width / 2, it.height / 2)
// дополнительный код
}
Не забудьте добавить зависимость: androidx.core:core-ktx:1.0.0
.
Вы можете создать расширение для Kotlin, которое будет отслеживать изменения глобального макета и выполнять заданную задачу, когда высота вью будет готова динамически. Это полезно, если вам нужно получить высоту вью после того, как макет был построен.
Использование:
view.height { Log.i("Info", "Вот ваша высота: $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)
}
}
Объяснение:
- Функция расширения
height
принимает лямбда-выражение, которое будет вызвано с высотой вью. - Если высота вью равна 0, это означает, что макет еще не был построен, и мы добавляем слушатель
OnGlobalLayoutListener
. - В методе
onGlobalLayout
, как только макет будет готов, слушатель удаляется и вызывается переданная функция с текущей высотой. - Если высота уже известна, функция вызывается немедленно.
Этот подход позволяет вам легко инициализировать действия, зависящие от высоты вью, без необходимости ручной обработки макета.
Как исправить 'android.os.NetworkOnMainThreadException'?
Ошибка «Необходимо переопределить метод суперкласса» после импорта проекта в Eclipse
Использование контекста в фрагменте
Как заставить Android-устройство вибрировать с разной частотой?
Room - Директория экспорта схемы не указана аннотационному процессору, не удается экспортировать схему