用权威的方式覆盖 on拳头() ?

正确的方法是什么?我见过各种各样的方法。例如,专业 Android 开发使用量度规格来计算尺寸,然后以调用 set拳头尺寸()结束。例如:

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

另一方面,根据 这篇文章,“正确”的方法是使用拳头测量规范,调用 set拳头测量维度() ,然后是调用 setLayoutParams () ,最后调用 super.on拳头测量()。例如:

@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);
}

那么哪种方法才是正确的呢? 这两种方法对我都没有百分之百奏效。

我想我真正想问的是,是否有人知道一个教程,其中解释了 on拳头() ,布局,子视图的尺寸等。?

44782 次浏览

The documentation is the authority on this matter: http://developer.android.com/guide/topics/ui/how-android-draws.html and http://developer.android.com/guide/topics/ui/custom-components.html

To summarize: at the end of your overridden onMeasure method you should call setMeasuredDimension.

You should not call super.onMeasure after calling setMeasuredDimension, that will just erase whatever you set. In some situations you might want to call the super.onMeasure first and then modify the results by calling setMeasuredDimension.

Don't call setLayoutParams in onMeasure. Layout happens in a second pass after measuring.

I guess, setLayoutParams and recalculating the measurements is a workaround to resize the child views correctly, as this is usually done in the derived class's onMeasure.

However, this rarely works correct (for whatever reason...), better invoke measureChildren (when deriving a ViewGroup) or try something similar when necessary.

The other solutions are not comprehensive. They may work in some cases, and are a good place to start, but they may are not guaranteed to work.

When onMeasure gets called you may or may not have the rights to change the size. The values that are passed to your onMeasure (widthMeasureSpec, heightMeasureSpec) contain information about what your child view is allowed to do. Currently there are three values:

  1. MeasureSpec.UNSPECIFIED - You can be as big as you'd like
  2. MeasureSpec.AT_MOST - As big as you want (up to the spec size), This is parentWidth in your example.
  3. MeasureSpec.EXACTLY - No choice. Parent has chosen.

This is done so that Android can make multiple passes to find the right size for each item, see here for more details.

If you do not follow these rules, your approach is not guaranteed to work.

For example if you want to check if you're allowed to change the size at all you can do the following:

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

Using this information you will know whether you can modify the values as in your code. Or if you are required to do something different. A quick and easy way to resolve your desired size is to use one of the following methods:

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

int resolveSize (int size, int measureSpec)

While the first is only available on Honeycomb, the second is available on all versions.

Note: You may find that resizeWidth or resizeHeight are always false. I found this to be the case if I was requesting MATCH_PARENT. I was able to fix this by requesting WRAP_CONTENT on my parent layout and then during the UNSPECIFIED phase requesting a size of Integer.MAX_VALUE. Doing so gives you the max size your parent allows on the next pass through onMeasure.

you can take this piece of code as an example of 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);
}
}

If changing the views size inside of onMeasure all you need is the setMeasuredDimension call. If you are changing the size outside of onMeasure you need to call setLayoutParams. For instance changing the size of a text view when the text is changed.

Depends on the control you are using. The instructions in the documentation work for some controls (TextView, Button, ...), but not for others (LinearLayout, ...). The way that worked very well for me was to call the super once I am done. Based on the article in the below link.

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

here is how I solved the problem:

@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);

}

Moreover it was necessary for the ViewPager component

I think it depends on the parent which you are overriding.

For example, if you are extending a ViewGroup (like FrameLayout), when you have measured the size, you should call like below

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

because you may want to ViewGroup to do rest work (do some stuffs on child view)

If you are extending a View (like ImageView), you can just call this.setMeasuredDimension(width, height);, because the parent class will just do something like you have done usually.

In a word, if you want some features your parent class offers free you should call super.onMeasure() (pass MeasureSpec.EXACTLY mode measure spec usually), otherwise call this.setMeasuredDimension(width, height); is enough.