Android: получить высоту вида до его отрисовки - PullRequest
46 голосов
/ 06 марта 2012

Для анимации мне нужно знать высоту из вида.Проблема в том, что метод getHeight () всегда возвращает 0, если только представление не отрисовано.Так есть ли способ получить высоту, не рисуя ее?

В этом случае View является LinearLayout.

РЕДАКТИРОВАТЬ: я пытаюсь адаптировать анимацию расширения с https://github.com/Udinic/SmallExamples/blob/master/ExpandAnimationExample/src/com/udinic/expand_animation_example/ExpandAnimation.java

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

Ответы [ 7 ]

122 голосов
/ 13 апреля 2012

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

Установите видимость в представлении на видимое или невидимое для начала (только для создания высоты).Не волнуйтесь, мы изменим его на невидимый / пропавший в коде, как показано ниже:

private int mHeight = 0;
private View mView;

class...

// onCreate or onResume or onStart ...
mView = findViewByID(R.id.someID);
mView.getViewTreeObserver().addOnGlobalLayoutListener( 
    new OnGlobalLayoutListener(){

        @Override
        public void onGlobalLayout() {
            // gets called after layout has been done but before display
            // so we can get the height then hide the view


            mHeight = mView.getHeight();  // Ahaha!  Gotcha

            mView.getViewTreeObserver().removeGlobalOnLayoutListener( this );
            mView.setVisibility( View.GONE );
        }

});
21 голосов
/ 23 марта 2016

У меня была похожая проблема с похожей ситуацией.

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

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

Я обдумал этот ответ: https://stackoverflow.com/a/6157820/854396 Но это не сработало для меня, потому что я не могу получить доступ к встроенному TextView напрямую отсюда. (Хотя я мог бы повторить, хотя дерево дочерних представлений.)

Посмотрите, как работает мой код:

view - это вид, который должен быть анимирован. Это LinearLayout (Окончательное решение будет немного более типичным, сохраняйте здесь) this - это пользовательское представление, которое содержит view и также наследуется от LinearLayout.

   private void expandView( final View view ) {

      view.setVisibility(View.VISIBLE);

      LayoutParams parms = (LayoutParams) view.getLayoutParams();
      final int width = this.getWidth() - parms.leftMargin - parms.rightMargin;

      view.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
              MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

      final int targetHeight = view.getMeasuredHeight();

      view.getLayoutParams().height = 0;

      Animation anim = new Animation() {
         @Override
         protected void applyTransformation( float interpolatedTime, Transformation trans ) {
            view.getLayoutParams().height =  (int) (targetHeight * interpolatedTime);
            view.requestLayout();
         }

         @Override
         public boolean willChangeBounds() {
            return true;
         }
      };

      anim.setDuration( ANIMATION_DURATION );
      view.startAnimation( anim );
   }

Это было почти так, но я допустил еще одну ошибку. В иерархии представлений участвуют два пользовательских представления, которые являются подклассами LinearLayout, и их xml объединяется с тегом merge в качестве корневого тега xml. В одном из этих тегов merge я пропустил установку атрибута android:orientation на vertical. Это не слишком беспокоило саму компоновку, потому что в этом объединенном ViewGroup был только один дочерний элемент ViewGroup, и поэтому я фактически не мог видеть неправильную ориентацию на экране. Но он был там и немного нарушил эту логику измерения макета.

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

12 голосов
/ 06 марта 2012

Технически, вы можете вызвать measure метод в представлении, а затем получить его высоту через getMeasureHeight.См. Это для получения дополнительной информации: https://developer.android.com/guide/topics/ui/how-android-draws.html. Вам нужно будет указать ему MeasureSpec.

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

Кроме того, вы можете попробовать использовать RELATIVE_TO_SELF значения в вашей анимации.

4 голосов
/ 06 февраля 2018
static void getMeasurments(View v) {

    v.measure(View.MeasureSpec.makeMeasureSpec(PARENT_VIEW.getWidth(), 
      View.MeasureSpec.EXACTLY),
         View.MeasureSpec.makeMeasureSpec(0, 
      View.MeasureSpec.UNSPECIFIED));

    final int targetHeight = v.getMeasuredHeight();
    final int targetWidth = v.getMesauredWidth();
}
0 голосов
/ 27 марта 2019

просто измерьте это сами, если родитель не определит его размер:

Вот расширение kotlin, чтобы сделать это для вас всегда:

fun View.getMeasurments(): Pair<Int, Int> {
    measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
    val width = measuredWidth
    val height = measuredHeight
    return width to height
}
0 голосов
/ 06 марта 2012

Хорошо, что касается обновленного поста, вот что вам поможет: Анимация # инициализировать метод. Вы должны просто поместить инициализацию вместо конструктора, и у вас будут все необходимые размеры.

0 голосов
/ 06 марта 2012

Вы можете попробовать начать с видимого представления, а затем добавить что-то вроде этого:

view.post(new Runnable() {      
    @Override
    public void run() {
        // hide view here so that its height has been already computed
        view.setVisibility(View.GONE);
    }
});

Теперь, когда вы вызовете view.getHeight (), он должен вернуть ожидаемую высоту.

Я не уверен, действительно ли это хороший способ сделать это, но по крайней мере это должно сработать.

...