覆盖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(),布局,子视图尺寸等的教程?
答案 0 :(得分:67)
其他解决方案并不全面。它们可能在某些情况下起作用,并且是一个很好的起点,但它们可能无法保证工作。
当onMeasure被调用时,您可能有权或无权更改大小。传递给onMeasure(widthMeasureSpec
,heightMeasureSpec
)的值包含有关您的子视图允许执行的操作的信息。目前有三个值:
MeasureSpec.UNSPECIFIED
- 你可以像你想的那样大MeasureSpec.AT_MOST
- 尽可能大(最大规格尺寸),在您的示例中为parentWidth
。MeasureSpec.EXACTLY
- 别无选择。家长选择了。这样做是为了让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,但第二个适用于所有版本。
注意:您可能会发现resizeWidth
或resizeHeight
始终为false。如果我要求MATCH_PARENT
,我发现情况就是这样。我能够通过在我的父布局上请求WRAP_CONTENT
然后在非常阶段请求大小为Integer.MAX_VALUE
来解决此问题。这样做可以为您提供父母在下次浏览onMeasure时允许的最大大小。
答案 1 :(得分:39)
文档是此事的权威:http://developer.android.com/guide/topics/ui/how-android-draws.html和http://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);
}
}