覆盖onMeasure()的权威方式?

时间:2011-09-14 21:16:14

标签: android android-layout

覆盖onMeasure()的正确方法是什么?我见过各种方法。例如, Professional Android Development 使用MeasureSpec计算维度,然后调用setMeasuredDimension()结束。例如:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}

另一方面,根据this post,“正确”的方法是使用MeasureSpec,调用setMeasuredDimensions(),,然后调用setLayoutParams(),并以调用super.onMeasure()。例如:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

那么哪种方式正确?这两种方法都没有对我有效。

我想我真正想要的是有没有人知道一个解释onMeasure(),布局,子视图尺寸等的教程?

8 个答案:

答案 0 :(得分:67)

其他解决方案并不全面。它们可能在某些情况下起作用,并且是一个很好的起点,但它们可能无法保证工作。

当onMeasure被调用时,您可能有权或无权更改大小。传递给onMeasure(widthMeasureSpecheightMeasureSpec)的值包含有关您的子视图允许执行的操作的信息。目前有三个值:

  1. MeasureSpec.UNSPECIFIED - 你可以像你想的那样大
  2. MeasureSpec.AT_MOST - 尽可能大(最大规格尺寸),在您的示例中为parentWidth
  3. MeasureSpec.EXACTLY - 别无选择。家长选择了。
  4. 这样做是为了让Android可以进行多次传递以找到每个项目的正确尺寸,有关详细信息,请参阅here

    如果您不遵守这些规则,则无法保证您的方法有效。

    例如,如果您想检查是否允许更改大小,可以执行以下操作:

    final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
    boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
    

    使用此信息,您将知道是否可以像修改代码一样修改值。或者,如果您需要做一些不同的事情。解决所需大小的快速简便方法是使用以下方法之一:

    int resolveSizeAndState (int size, int measureSpec, int childMeasuredState)

    int resolveSize (int size, int measureSpec)

    虽然第一个仅适用于Honeycomb,但第二个适用于所有版本。

    注意:您可能会发现resizeWidthresizeHeight始终为false。如果我要求MATCH_PARENT,我发现情况就是这样。我能够通过在我的父布局上请求WRAP_CONTENT然后在非常阶段请求大小为Integer.MAX_VALUE来解决此问题。这样做可以为您提供父母在下次浏览onMeasure时允许的最大大小。

答案 1 :(得分:39)

文档是此事的权威:http://developer.android.com/guide/topics/ui/how-android-draws.htmlhttp://developer.android.com/guide/topics/ui/custom-components.html

总结一下:在被覆盖的onMeasure方法结束时,您应该致电setMeasuredDimension

在致电super.onMeasure后,您不应该致电setMeasuredDimension,这只会删除您设置的内容。在某些情况下,您可能需要先调用super.onMeasure,然后通过调用setMeasuredDimension修改结果。

请勿在{{1​​}}中致电setLayoutParams。布局在测量后第二次发生。

答案 2 :(得分:1)

我认为这取决于父母你最重要的是什么。

例如,如果您正在扩展ViewGroup(如FrameLayout),那么当您测量尺寸时,您应该调用如下

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

因为您可能希望ViewGroup进行休息工作(在子视图上做一些事情)

如果你正在扩展一个View(比如ImageView),你可以只调用this.setMeasuredDimension(width, height);,因为父类只会做你喜欢的事情。

总之,如果你想要一些你的父类免费提供的功能,你应该调用super.onMeasure()(通常通过MeasureSpec.EXACTLY模式测量规范),否则调用this.setMeasuredDimension(width, height);就足够了。

答案 3 :(得分:0)

如果更改onMeasure内的视图大小,则只需setMeasuredDimension来电。如果您要更改onMeasure以外的尺寸,则需要致电setLayoutParams。例如,在文本更改时更改文本视图的大小。

答案 4 :(得分:0)

取决于您正在使用的控件。文档中的说明适用于某些控件(TextView,Button,...),但不适用于其他控件(LinearLayout,...)。对我来说非常有效的方法是在完成后给超级电话打电话。基于以下链接中的文章。

http://humptydevelopers.blogspot.in/2013/05/android-view-overriding-onmeasure.html

答案 5 :(得分:0)

这是我如何解决问题:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        ....

        setMeasuredDimension( measuredWidth, measuredHeight );

        widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY );
        heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

此外,ViewPager组件必须

答案 6 :(得分:-1)

我想,setLayoutParams并重新计算测量值是一种解决方法,可以正确调整子视图的大小,因为这通常在派生类的onMeasure中完成。

然而,这很少正常(无论出于何种原因......),更好地调用measureChildren(在派生ViewGroup时)或在必要时尝试类似的东西。

答案 7 :(得分:-4)

你可以把这段代码作为onMeasure()::

的一个例子
public class MyLayerLayout extends RelativeLayout {

    public MyLayerLayout(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int currentChildCount = getChildCount();
        for (int i = 0; i < currentChildCount; i++) {
            View currentChild = getChildAt(i);

            //code to find information

            int widthPercent = currentChildInfo.getWidth();
            int heightPercent = currentChildInfo.getHeight();

//considering we will pass height & width as percentage

            int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0));
            int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0));

//Considering we need to set horizontal & vertical position of the view in parent

            AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP;
            AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT;
            int topPadding = 0;
            int leftPadding = 0;

            if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                topPadding = (parentHeight - myHeight) / 2;
            } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) {
                topPadding = parentHeight - myHeight;
            }

            if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                leftPadding = (parentWidth - myWidth) / 2;
            } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) {
                leftPadding = parentWidth - myWidth;
            }
            LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight);
            currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0);
            currentChild.setLayoutParams(myLayoutParams);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}