在运行时确定Android视图的大小

Nik*_*man 113 android

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

这似乎是一件容易的事,虽然没有一个能解决我的问题,但很多问题都存在.所以也许我错过了一些明显的东西.我通过以下方式了解了我的观点:

ImageView myView = (ImageView)getWindow().findViewById(R.id.MyViewID);
Run Code Online (Sandbox Code Playgroud)

这工作得很好,但打电话时getWidth(),getHeight(),getMeasuredWidth(),getLayoutParams().width,等,他们都返回0.我也试过手动调用measure()视图上,然后到一个呼叫getMeasuredWidth(),但没有效果.

我已经尝试调用这些方法并在我的activity onCreate()和in中检查调试器中的对象onPostCreate().如何在运行时确定此视图的确切尺寸?

Vik*_*rla 177

使用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();
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意这个答案,因为如果底层视图是视频/表面,它将被连续调用 (21认同)
  • 你能详细说说@Kevin吗?两件事,首先,因为它是一个监听器,我希望有一个回调,接下来,一旦我们得到第一个回调,我们就删除了监听器.我错过了什么? (7认同)
  • @FarticlePilter,改为使用removeOnGlobalLayoutListener().它在文档中提到过. (7认同)
  • 在调用任何ViewTreeObserver方法之前,建议检查isAlive():if(view.getViewTreeObserver().isAlive()){view.getViewTreeObserver().removeGlobalOnLayoutListener(this); } (4认同)
  • `removeGlobalOnLayoutListener(this);`的替代品?因为它已被弃用. (4认同)

and*_*per 55

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

  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); }
Run Code Online (Sandbox Code Playgroud)

样品用法:

    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
        }
    });
Run Code Online (Sandbox Code Playgroud)
  1. 在某些情况下,手动测量视图的大小就足够了:
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth(); 
int height=view.getMeasuredHeight();
Run Code Online (Sandbox Code Playgroud)
  1. 如果你有一个扩展的自定义视图,你可以在"onMeasure"方法上获得它的大小,但我认为它只适用于某些情况:
    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
Run Code Online (Sandbox Code Playgroud)
  1. 如果你在Kotlin写作,你可以使用下一个函数,它在幕后的工作方式与runJustBeforeBeingDrawn我写的完全一样:

    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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

    view.doOnPreDraw { actionToBeTriggered() }
    
    Run Code Online (Sandbox Code Playgroud)

  • @CoolMind 如果您使用 Kotlin,您还可以使用新方法。更新的答案也包括它。 (3认同)

Mar*_*k B 19

您是否getWidth()在视图实际布局在屏幕上之前打电话?

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

  • 那么这是否意味着我需要覆盖ImageView才能捕获`onSizeChange()`事件以了解实际大小?这似乎有点矫枉过正......虽然在这一点上我只是为了让代码工作而绝望,所以值得一试. (2认同)

Nik*_*man 17

基于@ mbaird的建议,我通过继承ImageView类和重写找到了一个可行的解决方案onLayout().然后我创建了一个我的活动实现的观察者接口,并将对它自己的引用传递给了类,这允许它在实际完成大小调整时告诉活动.

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


gre*_*egm 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);
}
Run Code Online (Sandbox Code Playgroud)


Mac*_*rse 6

你可以查看这个问题.你可以使用View' post()方法.


小智 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);
Run Code Online (Sandbox Code Playgroud)

  • 当它在点击监听器中时,您不需要postDelayed.在视图全部自我测量并出现在屏幕上之前,您无法单击任何内容.因此,它们已经在您能够点击它们的时间进行测量. (4认同)

Asi*_*tel 5

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

@Override
public void onWindowFocusChanged(boolean hasFocus) {
       super.onWindowFocusChanged(hasFocus);
       Log.e("WIDTH",""+view.getWidth());
       Log.e("HEIGHT",""+view.getHeight());
}
Run Code Online (Sandbox Code Playgroud)