Определение размера представления Android во время выполнения

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

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

ImageView myView = (ImageView)getWindow().findViewById(R.id.MyViewID);

Это работает нормально, но при вызове getWidth(), getHeight(), getMeasuredWidth(), getLayoutParams().width и т.д. все они возвращают 0. Я также пробовал вручную вызывать measure() в представлении, сопровождаемом вызовом до getMeasuredWidth(), но это не влияет.

Я попытался вызвать эти методы и проверить объект в отладчике в своей активности onCreate() и в onPostCreate(). Как я могу определить точные размеры этого представления во время выполнения?

Ответ 1

Основываясь на совете @mbaird, я нашел работоспособное решение путем подкласса класса ImageView и переопределения onLayout(). Затем я создал интерфейс наблюдателя, который реализовал мой актив и передал ссылку на него классу, что позволило ему рассказать о действии, когда оно было фактически закончено.

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

Ответ 2

Используйте ViewTreeObserver в представлении, чтобы дождаться первого макета. Только после первого макета будет работать getWidth()/getHeight()/getMeasuredWidth()/getMeasuredHeight().

ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
  viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
      viewWidth = view.getWidth();
      viewHeight = view.getHeight();
    }
  });
}

Ответ 3

На самом деле есть несколько решений, в зависимости от сценария:

  1. Безопасный метод будет работать непосредственно перед отрисовкой вида после завершения фазы макета:
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
    final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            runnable.run();
            return true;
        }
    };
    view.getViewTreeObserver().addOnPreDrawListener(preDrawListener); }

Пример использования:

    ViewUtil.runJustBeforeBeingDrawn(yourView, new Runnable() {
        @Override
        public void run() {
            //Here you can safely get the view size (use "getWidth" and "getHeight"), and do whatever you wish with it
        }
    });
  1. В некоторых случаях этого достаточно, чтобы измерить размер представления вручную:
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth(); 
int height=view.getMeasuredHeight();

Если вы знаете размер контейнера:

    val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxWidth, View.MeasureSpec.AT_MOST)
    val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
    view.measure(widthMeasureSpec, heightMeasureSpec)
    val width=view.measuredWidth
    val height=view.measuredHeight
  1. если у вас есть пользовательское представление, которое вы расширили, вы можете получить его размер с помощью метода "onMeasure", но я думаю, что он работает хорошо только в некоторых случаях:
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    final int newHeight= MeasureSpec.getSize(heightMeasureSpec);
    final int newWidth= MeasureSpec.getSize(widthMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
  1. Если вы пишете на Kotlin, вы можете использовать следующую функцию, которая за кадром работает точно так же, как runJustBeforeBeingDrawn, которую я написал:

    view.doOnPreDraw { actionToBeTriggered() }
    

    Обратите внимание, что вам нужно добавить это в Gradle (можно найти через здесь):

    implementation 'androidx.core:core-ktx:#.#'
    

Ответ 4

Вы вызываете getWidth() до того, как представление действительно выложено на экране?

Общей ошибкой, совершенной новыми разработчиками Android, является использование ширины и высота представления внутри его конструктора. Когда взгляды конструктор называется, Android еще не знает, насколько большой будет просмотр быть, поэтому размеры установлены равными нулю. Реальные размеры вычисляются во время этап компоновки, который возникает после строительства, но прежде всего нарисован. Вы можете использовать метод onSizeChanged() для уведомления значения, когда они известны, или вы можете использовать getWidth() и getHeight() позже, например, в методе onDraw().

Ответ 5

Вот код для получения макета путем переопределения вида, если API < 11 (API 11 включает функцию View.OnLayoutChangedListener):

public class CustomListView extends ListView
{
    private OnLayoutChangedListener layoutChangedListener;

    public CustomListView(Context context)
    {
        super(context);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        if (layoutChangedListener != null)
        {
            layoutChangedListener.onLayout(changed, l, t, r, b);
        }
        super.onLayout(changed, l, t, r, b);
    }

    public void setLayoutChangedListener(
        OnLayoutChangedListener layoutChangedListener)
    {
        this.layoutChangedListener = layoutChangedListener;
    }
}
public interface OnLayoutChangedListener
{
    void onLayout(boolean changed, int l, int t, int r, int b);
}

Ответ 6

Вы можете проверить этот вопрос. Вы можете использовать метод View post().

Ответ 7

Это работает для меня в моем onClickListener:

yourView.postDelayed(new Runnable() {               
    @Override
    public void run() {         
        yourView.invalidate();
        System.out.println("Height yourView: " + yourView.getHeight());
        System.out.println("Width yourView: " + yourView.getWidth());               
    }
}, 1);

Ответ 8

Используйте приведенный ниже код, он дает размер представления.

@Override
public void onWindowFocusChanged(boolean hasFocus) {
       super.onWindowFocusChanged(hasFocus);
       Log.e("WIDTH",""+view.getWidth());
       Log.e("HEIGHT",""+view.getHeight());
}

Ответ 9

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

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

Ответ 10

работает для меня:

 protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        CTEditor ctEdit = Element as CTEditor;
        if (ctEdit == null) return;           
        if (e.PropertyName == "Text")
        {
            double xHeight = Element.Height;
            double aHaight = Control.Height;
            double height;                
            Control.Measure(LayoutParams.MatchParent,LayoutParams.WrapContent);
            height = Control.MeasuredHeight;
            height = xHeight / aHaight * height;
            if (Element.HeightRequest != height)
                Element.HeightRequest = height;
        }
    }