【问题标题】:Android: get height of a view before it´s drawnAndroid:在绘制之前获取视图的高度
【发布时间】: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 达到相同的效果。 目前动画只有在绘制之前知道布局大小时才有效。

【问题讨论】:

    标签: android layout view height


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

      除了上述之外,可能还需要删除与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() 时,它应该返回您期望的高度。

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

                  【讨论】:

                    猜你喜欢
                    • 2011-12-07
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多