Tôi có lớp vùng chứa này được sử dụng để tạo một trong các thứ nguyên, chiều rộng hoặc chiều cao, tỷ lệ của thứ nguyên khác. Ví dụ, tôi muốn một thùng chứa bố trí 16: 9 nơi chiều rộng là "match_parent". Tuy nhiên, khi sử dụng chiều cao là "match_parent", Android dường như không chuyển tiếp chính xác. Khi tôi thiết lập chiều cao là một tỷ lệ của chiều rộng tất cả mọi thứ là tốt! Ngược lại và nó không hoạt động. Chuyện gì vậy?RelativeLayout không cập nhật đúng chiều rộng của chế độ xem tùy chỉnh
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, attrs, 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());
}
}
Để nhìn thấy nó trong hành động, sử dụng một cách bố trí như thế này:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.RatioLayout
android:id="@+id/image"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginBottom="100dp"
android:layout_marginTop="100dp"
android:background="#FFFF00FF" />
</RelativeLayout>
Và đây sẽ là Hoạt động của tôi:
đầu ra Logging:
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 <--- NO reason this should not be 655
Tại sao Android không tôn trọng đo lường mới nhất của tôiWidth nhưng sử dụng một phiên bản cũ hơn nhưng chiều cao mới nhất?
EDIT: Đã cập nhật. Đặt phụ huynh của RatioLayout NOT một RelativeLayout nhưng LinearLayout hoặc FrameLayout cho tôi hành vi đúng. Đối với một số lý do RelativeLayout là "bộ nhớ đệm" đo lườngWidth và không sử dụng mới nhất.
EDIT 2: bình luận trong RelativeLayout.onLayout Điều này dường như xác nhận của tôi "đó là bộ nhớ đệm" mà tôi tin là một lỗi
@Override
protected void onLayout(boolean changed, int l, int t, int r, 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) {
CUỐI CÙNG EDIT Ok. Tôi từ bỏ. Đây là lỗi hợp pháp trong RelativeLayout. Loại mã này sửa chữa nó, nhưng nó tạo ra các vấn đề với các thuộc tính toRightOf. Công việc xung quanh mà tôi tìm thấy là làm tổ RatioLayout này vào một ViewGroup khác như LinerLayout. Mã cho những người tò mò
@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);
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", "error", e);
} catch (IllegalAccessException e) {
Log.e("Ration", "error", e);
}
}
int mw = getMeasuredWidth();
int mh = getMeasuredHeight();
lastWidth = mw;
Log.i("Ratio", "mw: " + mw + ", mh: " + mh);
}