Android:在绘制之前获取视图的高度

Posted

技术标签:

【中文标题】Android:在绘制之前获取视图的高度【英文标题】:Android: get height of a view before it´s drawn 【发布时间】:2012-03-23 11:01:48 【问题描述】:

对于动画,我需要知道视图的高度。问题是,除非绘制视图,否则 getHeight() 方法总是返回 0。 那么有没有什么办法不用画就可以得到高度呢?

在这种情况下,视图是一个线性布局。

编辑: 我尝试从 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 只需要填充一行。难怪每次当文本恰好足够短到一行时,这都能正常工作,但当文本长于一行时,它就会失败。

我考虑过这个答案:https://***.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,因此我实际上无法在屏幕上看到错误的方向。但它就在那里,有点打破了这种布局测量逻辑。

除了上述之外,可能还需要删除与LinearLayouts 和视图层次结构中的子类相关的所有填充。用边距替换它们,即使您必须围绕它包装另一个布局以使其看起来相同。

【讨论】:

【参考方案3】:

从技术上讲,您可以在视图上调用measure 方法,然后通过getMeasureHeight 获取它的高度。有关更多信息,请参阅:https://developer.android.com/guide/topics/ui/how-android-draws.html。不过你需要给它一个MeasureSpec

但实际上,视图的大小受其父布局的影响,因此您可能会得到与实际绘制时不同的大小。

另外,您可以尝试在动画中使用 RELATIVE_TO_SELF 值。

【讨论】:

【参考方案4】:
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();

【讨论】:

【参考方案5】:

如果父母没有确定它的大小,就自己测量吧:

这里有一个 kotlin 扩展,可以随时为你做这件事:

fun View.getMeasurments(): Pair<Int, Int> 
    measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
    val width = measuredWidth
    val height = measuredHeight
    return width to height

【讨论】:

【参考方案6】:

虽然this answer 一开始似乎有效,但最终我注意到它弄乱了正在测量的视图。 我通过传递父视图来修复它。

/**
 * class to map width and height values.
 */
data class Point(val width: Int, val height: Int)

/**
 * Measures the view and returns the width and height.
 */
fun View.getMeasurements(parent: View): Point 
    measure(View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED))

    return Point(measuredWidth, measuredHeight)

我还创建了一个数据类,因此值所代表的内容变得更具可读性。 您现在可以像这样访问结果值

child.getMeasurements(parent).width

或者这个

child.getMeasurements(parent).height

【讨论】:

【参考方案7】:

好的,关于更新后的帖子,以下内容将对您有所帮助: Animation#initialize 方法。你应该把初始化而不是构造函数放在那里,你就会有你需要的所有大小。

【讨论】:

【参考方案8】:

您可以尝试从 View 可见开始,然后添加如下内容:

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() 时,它应该返回您期望的高度。

我不确定这是否真的是一个好方法,但至少它应该有效。

【讨论】:

以上是关于Android:在绘制之前获取视图的高度的主要内容,如果未能解决你的问题,请参考以下文章

Android 在一切之上叠加一个视图?

如何在android中获取标签内容的高度和宽度?

如何在xml中设置复合可绘制文本视图的高度和宽度? [关闭]

Android:获取View视图在屏幕绝对位置和坐标方法总结

在Android中绘制之前测量所有视图层次结构

自定义视图缩小:之前占用的空间没有被清除