Android: получить высоту представления, прежде чем он будет нарисован

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

В этом случае представление представляет собой LinearLayout.

EDIT: Я пытаюсь адаптировать расширение Animation из https://github.com/Udinic/SmallExamples/blob/master/ExpandAnimationExample/src/com/udinic/expand_animation_example/ExpandAnimation.java

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

Ответ 1

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

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

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 );
        }

});

Ответ 2

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

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

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

Я рассмотрел этот ответ: fooobar.com/questions/18402/... Но для меня это не сработало, потому что я не могу получить доступ к встроенному 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 и подклассами в иерархии представлений. Заменяйте их по полям, даже если вам нужно обернуть еще один макет, чтобы он выглядел одинаково.

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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(), он должен вернуть ожидаемую высоту.

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