28

In a Fragment, I am inflating a Layout with multiple child View. I need to get the dimensions (width and height) of one of them which is a custom view.

Inside the custom view class I can do it easily. But if I try to do it from the fragment I always get 0 as dimensions.

 public void onViewCreated(View view, Bundle savedInstanceState) {

    super.onViewCreated(view, savedInstanceState);
    View culoide = view.findViewWithTag(DRAW_AREA_TAG);
    Log.d("event", "culoide is: "+culoide.getWidth()); // always 0
}

I figure that onViewCreated should be the right place to get it, but well this happens. I tried before super.onViewCreated, in debug it looks like 'findViewWithTag' finds the right view, tried with api 7 v4 support only.

Any help?

quinestor
  • 1,432
  • 4
  • 19
  • 40

7 Answers7

47

You must wait until after the first measure and layout in order to get nonzero values for getWidth() and getHeight(). You can do this with a ViewTreeObserver.OnGlobalLayouListener

public void onViewCreated(final View view, Bundle saved) {
    super.onViewCreated(view, saved);
    view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
              view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
              view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }

            // get width and height of the view
        }
    });
}
Karakuri
  • 38,365
  • 12
  • 84
  • 104
  • +1 thanks :) ; removeGlobalOnLayoutListener is today already deprecated ; but that's already a different issue – quinestor Jun 05 '13 at 12:38
  • The deprecated method calls through to the new one (and in fact I don't know if it was worth deprecating just because of the name), but I've edited my code anyway. – Karakuri Jun 05 '13 at 14:39
  • 1
    Or just override `onSizeChanged()`. – D-Dᴙum Oct 16 '13 at 07:02
  • In my case I want to use the getWidth() to adjust some padding/margin. I cannot override onSizeChanged() since it is called during layout and changes to padding/margin would not be persisted. The OnGlobalLayoutListener fires when layout is finished, no problems there. – mmvie Nov 08 '13 at 14:32
  • Remember to set mView=null in onDestroyView(). – user2203031 Nov 26 '14 at 18:28
  • You have to call `view.getViewTreeObserver()` instead of saving `observer` as variable – sagus_helgy Sep 18 '16 at 14:41
6

My preferred method is to add an OnLayoutChangeListener to the view that you want to track itself

CustomView customView = ...

customView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // Make changes
    }
});

You can remove the listener in the callback if you only want the initial layout.

Zeophlite
  • 1,607
  • 3
  • 19
  • 36
3

Using ViewTreeObserver.OnGlobalLayoutListener, View.post(Runnable action) or onWindowFocusChanged() isn't the best solution. This article (note: I am the author of this article) explains why and provides a working solution using doOnLayout kotlin extension, which is based on View.OnLayoutChangeListener. If you want it in Java, in the article there's a link to doOnLayout source code, it's very simple and you can do something similar in Java too.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Diego Oliveira
  • 459
  • 11
  • 7
2

You have to wait until the onSizeChanged() method is called before you can reliably determine the View size.

This is called during layout when the size of this view has changed. If you were just added to the view hierarchy, you're called with the old values of 0.

D-Dᴙum
  • 7,689
  • 8
  • 58
  • 97
1

Try calling

culoide.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

first, then try getWidth() and getHeight()

Ken Wolf
  • 23,133
  • 6
  • 63
  • 84
0

Try checking in onWindowFocusChanged and it should have valid values:

public void onWindowFocusChanged (boolean hasFocus) { }

I had a similar issue where I needed to get width and height of a widget and this was the function in which I could guarantee the widget reported it's correct size.

spartygw
  • 3,289
  • 2
  • 27
  • 51
0

this is a real pain especially because you expected with a name like onViewCreated in fragments lifecycle that the view is ready. for me get the fragment view itself like this:

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)   
     getView()?.let{
                  it.doOnLayout{// do your UI work here }
                   }
    }

this ensures the fragments getView has actually had one layout pass already.

j2emanue
  • 60,549
  • 65
  • 286
  • 456