Android getMeasuredHeight returns wrong values !

后端 未结 7 717
野的像风
野的像风 2020-12-02 20:13

I\'m trying to determine the real dimension in pixels of some UI elements !

Those elements are inflated from a .xml file and are initialized with dip width and height

7条回答
  •  广开言路
    2020-12-02 20:51

    Unfortunately, in Activity lifecycle methods such as Activity#onCreate(Bundle), a layout pass has not yet been performed, so you can't yet retrieve the size of views in your view hierarchy. However, you can explicitly ask Android to measure a view using View#measure(int, int).

    As @adamp's answer points out, you have to provide View#measure(int, int) with MeasureSpec values, but it can be a bit daunting figuring out the correct MeasureSpec.

    The following method tries to determine the correct MeasureSpec values and measures the passed in view:

    public class ViewUtil {
    
        public static void measure(@NonNull final View view) {
            final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
    
            final int horizontalMode;
            final int horizontalSize;
            switch (layoutParams.width) {
                case ViewGroup.LayoutParams.MATCH_PARENT:
                    horizontalMode = View.MeasureSpec.EXACTLY;
                    if (view.getParent() instanceof LinearLayout
                            && ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.VERTICAL) {
                        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
                        horizontalSize = ((View) view.getParent()).getMeasuredWidth() - lp.leftMargin - lp.rightMargin;
                    } else {
                        horizontalSize = ((View) view.getParent()).getMeasuredWidth();
                    }
                    break;
                case ViewGroup.LayoutParams.WRAP_CONTENT:
                    horizontalMode = View.MeasureSpec.UNSPECIFIED;
                    horizontalSize = 0;
                    break;
                default:
                    horizontalMode = View.MeasureSpec.EXACTLY;
                    horizontalSize = layoutParams.width;
                    break;
            }
            final int horizontalMeasureSpec = View.MeasureSpec
                    .makeMeasureSpec(horizontalSize, horizontalMode);
    
            final int verticalMode;
            final int verticalSize;
            switch (layoutParams.height) {
                case ViewGroup.LayoutParams.MATCH_PARENT:
                    verticalMode = View.MeasureSpec.EXACTLY;
                    if (view.getParent() instanceof LinearLayout
                            && ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.HORIZONTAL) {
                        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
                        verticalSize = ((View) view.getParent()).getMeasuredHeight() - lp.topMargin - lp.bottomMargin;
                    } else {
                        verticalSize = ((View) view.getParent()).getMeasuredHeight();
                    }
                    break;
                case ViewGroup.LayoutParams.WRAP_CONTENT:
                    verticalMode = View.MeasureSpec.UNSPECIFIED;
                    verticalSize = 0;
                    break;
                default:
                    verticalMode = View.MeasureSpec.EXACTLY;
                    verticalSize = layoutParams.height;
                    break;
            }
            final int verticalMeasureSpec = View.MeasureSpec.makeMeasureSpec(verticalSize, verticalMode);
    
            view.measure(horizontalMeasureSpec, verticalMeasureSpec);
        }
    
    }
    

    Then you can simply call:

    ViewUtil.measure(view);
    int height = view.getMeasuredHeight();
    int width = view.getMeasuredWidth();
    

    Alternatively, as @Amit Yadav suggested, you can use OnGlobalLayoutListener to have a listener called after the layout pass has been performed. The following is a method that handles unregistering the listener and method naming changes across versions:

    public class ViewUtil {
    
        public static void captureGlobalLayout(@NonNull final View view,
                                               @NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) {
            view.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            final ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                viewTreeObserver.removeOnGlobalLayoutListener(this);
                            } else {
                                //noinspection deprecation
                                viewTreeObserver.removeGlobalOnLayoutListener(this);
                            }
                            listener.onGlobalLayout();
                        }
                    });
        }
    
    }
    

    Then you can:

    ViewUtil.captureGlobalLayout(rootView, new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int width = view.getMeasureWidth();
            int height = view.getMeasuredHeight();
        }
    });
    

    Where rootView can be the root view of your view hierarchy and view can be any view within your hierarchy that you want to know the dimensions of.

提交回复
热议问题