我有這個容器類,用于制作其他尺寸的一個尺寸,寬度或高度,其他尺寸的比例.例如,我想要一個16:9布局容器,其寬度為“match_parent”.但是,當将高度用作“match_parent”時,Android似乎沒有正确的重新發送.當我将高度設定為一個比例的寬度一切都很好!反之亦然,它不起作用.這是怎麼回事?
public class RatioLayout extends ViewGroup {
private float ratio = 1.6222f; // 4 x 3 default. 1.78 is 16 x 9
public float getRatio() { return ratio; }
public void setRatio(float ratio) {
this.ratio = ratio;
requestLayout();
}
public RatioLayout(Context context) {
this(context,null);
}
public RatioLayout(Context context,AttributeSet attrs) {
this(context,attrs,0);
}
public RatioLayout(Context context,AttributeSet attrs,int defStyle) {
super(context,defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
Log.i("Ratio","/onMeasure");
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// int exactly = MeasureSpec.EXACTLY; // 1073741824
// int atMost = MeasureSpec.AT_MOST; // -2147483648
// int unspec = MeasureSpec.UNSPECIFIED; // 0
width = Math.round(height * ratio);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width,MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY);
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
int mw = getMeasuredWidth();
int mh = getMeasuredHeight();
Log.i("Ratio","mw: " + mw + ",mh: " + mh);
}
@Override
protected void onLayout(boolean changed,int l,int t,int r,int b) {
Log.i("Ratio","/onLayout");
Log.i("Ratio","l: " + l + ",t: " + t + ",r:" + r + ",b:" + b);
Log.i("Ratio","mw: " + getMeasuredWidth() + ",mh:" + getMeasuredHeight());
Log.i("Ratio","w: " + getWidth() + ",mw:" + getHeight());
}
}
要檢視它的行動,使用這樣的布局:
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
android:id="@+id/image"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginBottom="100dp"
android:layout_marginTop="100dp"
android:background="#FFFF00FF" />
這将是我的活動:
記錄輸出:
I/Ratio (9445): /onMeasure
I/Ratio (9445): mw: 1087,mh: 670
I/Ratio (9445): /onMeasure
I/Ratio (9445): mw: 655,mh: 404
I/Ratio (9445): /onLayout
I/Ratio (9445): l: 0,t: 133,r:1087,b:537
I/Ratio (9445): mw: 655,mh:404
I/Ratio (9445): w: 1087,mw:404
為什麼Android不符合我最新的measuredWidth,但使用較舊的版本,但最新的高度?
編輯:更新.使RatioLayout的父級不是RelativeLayout,但是LinearLayout或FrameLayout給出了正确的行為.由于某些原因RelativeLayout是“緩存”measuredWidth并且不使用最新的.
編輯2:RelativeLayout.onLayout中的這個評論似乎證明了我的“緩存”,我認為這是一個bug
@Override
protected void onLayout(boolean changed,int b) {
// The layout has actually already been performed and the positions
// cached. Apply the cached values to the children.
int count = getChildCount();
// TODO: we need to find another way to implement RelativeLayout
// This implementation cannot handle every case
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
最後編輯好的我放棄.這是RelativeLayout中的一個合法的錯誤.這種代碼修複它,但它會導緻toRightOf屬性的問題.我發現的工作是将此RatioLayout嵌入另一個ViewGroup,如LinerLayout.那些好奇的代碼
@Override
protected void onMeasure(int widthMeasureSpec,heightMeasureSpec);
if (getParent() instanceof RelativeLayout) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)getLayoutParams();
Class> clazz = RelativeLayout.LayoutParams.class;
try {
Field left = clazz.getDeclaredField("mLeft");
Field right = clazz.getDeclaredField("mRight");
left.setAccessible(true);
right.setAccessible(true);
int l = left.getInt(params);
if (l == -1) l = params.leftMargin; // if the value is uninitialized,set it to 0;
if (l == -1) l = 0; // if the value is uninitialized,set it to 0;
// setting this seems to break the layout_marginLeft properties.
right.setInt(params,l + getMeasuredWidth());
} catch (NoSuchFieldException e) {
Log.e("Ration","error",e);
} catch (IllegalArgumentException e) {
Log.e("Ration",e);
} catch (IllegalAccessException e) {
Log.e("Ration",e);
}
}
int mw = getMeasuredWidth();
int mh = getMeasuredHeight();
lastWidth = mw;
Log.i("Ratio",mh: " + mh);
}