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

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

Это кажется легкой задачей, и есть много ТАКИХ вопросов по этому поводу, хотя ни один, очевидно, не решил мою проблему. Так что, возможно, я упускаю что-то очевидное. Я получаю ручку к своему мнению:

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

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

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

14 ответов

Решение

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

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

Используйте 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();
    }
  });
}

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

  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(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
int width=view.getMeasuredWidth(); 
int height=view.getMeasuredHeight();
  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:#.#'
    

Ты звонишь getWidth() перед тем, как вид фактически выложен на экран?

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

Вот код для получения макета с помощью переопределения представления, если 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);
}

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

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

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

Это работает для меня в моем 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);

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

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

Вы можете получить как положение, так и размер представления на экране

 val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

if (viewTreeObserver.isAlive) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            //Remove Listener
            videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);

            //View Dimentions
            viewWidth = videoView.width;
            viewHeight = videoView.height;

            //View Location
            val point = IntArray(2)
            videoView.post {
                videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                viewPositionX = point[0]
                viewPositionY = point[1]
            }

        }
    });
}

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

      llayout.measure(0, 0)// after this call, measured Height or Width is not 0 anymore
mychartView.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, llayout.measuredHeight)

Если вам нужно знать размеры View сразу после того, как оно нарисовано, вы можете просто вызвать post() для этого данного View и отправить туда Runnable, который выполнит все, что вам нужно. Это лучшее решение, чем ViewTreeObserver и globalLayout, поскольку оно вызывается неоднократно, а не один раз. Этот Runnsble будет выполняться только один раз, и вы будете знать размер просмотров.

В файле Kotlin измените соответственно

 Handler().postDelayed({

           Your Code

        }, 1)

У меня работает перфект:

 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;
        }
    }
Другие вопросы по тегам