139

创建活动后,我正在尝试将动画应用于我的 Android 应用程序中的视图。为此,我需要确定视图的当前大小,然后设置动画以从当前大小缩放到新大小。这部分必须在运行时完成,因为视图会根据用户的输入缩放到不同的大小。我的布局是用 XML 定义的。

这似乎是一项简单的任务,并且有很多关于此的 SO 问题,但显然没有解决我的问题。所以也许我错过了一些明显的东西。我通过以下方式处理我的观点:

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

这工作正常,但是当调用getWidth()getHeight()getMeasuredWidth()getLayoutParams().width等时,它们都返回 0。我也尝试手动调用measure()视图,然后调用getMeasuredWidth(),但这没有效果。

我已经尝试调用这些方法并在我的活动onCreate()onPostCreate(). 如何在运行时确定此视图的确切尺寸?

4

13 回答 13

190

使用 View 上的 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();
    }
  });
}
于 2011-07-04T08:54:48.787 回答
78

实际上有多种解决方案,具体取决于场景:

  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(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth(); 
int height=view.getMeasuredHeight();

如果您知道容器的大小:

    val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxWidth, View.MeasureSpec.AT_MOST)
    val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
    view.measure(widthMeasureSpec, heightMeasureSpec)
    val width=view.measuredWidth
    val height=view.measuredHeight
  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 编写,你可以使用 next 函数,它在幕后的工作方式与runJustBeforeBeingDrawn我写的完全一样:

     view.doOnPreDraw { actionToBeTriggered() }
    

请注意,您需要将其添加到 gradle(通过此处找到):

android {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

implementation 'androidx.core:core-ktx:#.#'
于 2015-01-25T11:48:05.100 回答
22

getWidth()是在视图实际显示在屏幕上之前调用吗?

新 Android 开发人员常犯的一个错误是在其构造函数中使用视图的宽度和高度。当调用视图的构造函数时,Android 还不知道视图的大小,因此将大小设置为零。实际尺寸是在布局阶段计算的,这发生在构建之后但在绘制任何东西之前。您可以使用该onSizeChanged()方法在已知值时通知它们,也可以稍后使用getWidth()and getHeight()方法,例如在该onDraw()方法中。

于 2010-09-23T14:29:58.880 回答
17

根据@mbaird 的建议,我找到了一个可行的解决方案,方法是继承ImageView类并覆盖onLayout(). 然后,我创建了一个观察者接口,我的活动实现了该接口,并将对自身的引用传递给该类,这允许它在实际完成调整大小时告诉活动。

我不是 100% 相信这是最好的解决方案(因此我还没有将此答案标记为正确),但它确实有效,并且根据文档,这是第一次可以找到视图的实际大小。

于 2010-09-24T10:00:13.153 回答
10

如果 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);
}
于 2010-10-22T19:55:07.413 回答
7

你可以检查这个问题。您可以使用View's post()方法。

于 2011-07-09T15:30:39.277 回答
5

这适用于我的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);
于 2013-04-04T09:57:51.643 回答
5

使用下面的代码,它给出了视图的大小。

@Override
public void onWindowFocusChanged(boolean hasFocus) {
       super.onWindowFocusChanged(hasFocus);
       Log.e("WIDTH",""+view.getWidth());
       Log.e("HEIGHT",""+view.getHeight());
}
于 2017-02-10T17:42:57.873 回答
4

我也迷路了getMeasuredWidth()getMeasuredHeight() getHeight()getWidth()一段时间......后来我发现获取视图的宽度和高度onSizeChanged()是最好的方法......你可以动态通过覆盖onSizeChanged() 方法获取视图的当前宽度和当前高度。

可能想看看这个有一个精心设计的代码片段。新博客文章:如何在 Android 中获取 customView(扩展视图)的宽度和高度尺寸http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html

于 2011-02-27T20:48:54.640 回答
0

您可以在屏幕上获取视图的位置和尺寸

 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]
            }

        }
    });
}
于 2020-04-09T07:49:15.110 回答
0

如果您需要在绘制后立即知道 View 的尺寸,您可以简单地在给定的 View 上调用 post() 并发送一个 Runnable 来执行您需要的任何内容。它是比 ViewTreeObserver 和 globalLayout 更好的解决方案,因为它不仅被重复调用一次。此 Runnsble 将只执行一次,您将知道视图大小。

于 2021-04-25T18:13:48.930 回答
0

在 Kotlin 文件中,进行相应的更改

 Handler().postDelayed({

           Your Code

        }, 1)
于 2020-11-05T09:23:16.910 回答
-2

适合我:

 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;
        }
    }
于 2017-09-08T07:03:04.883 回答