Android RelativeLayout未正确更新自定义视图的宽度
我有一个容器类,用来制作一个维度,宽度或高度,与另一个维度的比率。例如,我想要一个宽度为“match_parent”的16:9布局容器。然而,当使用高度作为“匹配父对象”时,Android似乎没有正确地重新显示自己。当我将高度设置为宽度的比率时,一切都很好!反之亦然,则不起作用。发生什么事了Android RelativeLayout未正确更新自定义视图的宽度,android,android-layout,Android,Android Layout,我有一个容器类,用来制作一个维度,宽度或高度,与另一个维度的比率。例如,我想要一个宽度为“match_parent”的16:9布局容器。然而,当使用高度作为“匹配父对象”时,Android似乎没有正确地重新显示自己。当我将高度设置为宽度的比率时,一切都很好!反之亦然,则不起作用。发生什么事了 public class RatioLayout extends ViewGroup { private float ratio = 1.6222f; // 4 x 3 default. 1.78 is
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());
}
}
要查看它的运行情况,请使用如下布局:
<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>
最终编辑确定。我放弃了。这是相对论中的一个合法错误。这段代码在某种程度上修复了它,但它在属性的定义方面产生了问题。我发现的解决方法是将此RatioLayout嵌套到另一个视图组(如LinerLayout)中。那些好奇的人的密码
@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);
}
@覆盖
测量时的保护空隙(内部宽度测量等级、内部高度测量等级){
对数i(“比率”,即“测量值”);
int width=MeasureSpec.getSize(widthmasurespec);
int height=MeasureSpec.getSize(heightMeasureSpec);
int widthMode=MeasureSpec.getMode(widthmasurespec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
//int-justice=MeasureSpec.justice;//1073741824
//int atMost=MeasureSpec.AT_MOST;//-2147483648
//int unspec=MeasureSpec.UNSPECIFIED;//0
宽度=数学圆(高度*比率);
widthMeasureSpec=MeasureSpec.MakeMasureSpec(宽度,精确测量);
heightMeasureSpec=MeasureSpec.MakeMasureSpec(高度,精确测量);
设置测量尺寸(宽度测量等级、高度测量等级);
if(getParent()instanceof RelativeLayout){
RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)getLayoutParams();
clazz类=RelativeLayout.LayoutParams.Class;
试一试{
字段左=clazz.getDeclaredField(“mLeft”);
字段右侧=clazz.getDeclaredField(“mRight”);
left.setAccessible(true);
对。setAccessible(true);
int l=left.getInt(参数);
如果(l==-1)l=params.leftMargin;//如果该值未初始化,则将其设置为0;
如果(l==-1)l=0;//如果该值未初始化,则将其设置为0;
//设置此选项似乎会破坏布局\u marginLeft属性。
right.setInt(params,l+getMeasuredWidth());
}捕获(无此字段例外){
Log.e(“定量”、“错误”,e);
}捕获(IllegalArgumentException e){
Log.e(“定量”、“错误”,e);
}捕获(非法访问例外e){
Log.e(“定量”、“错误”,e);
}
}
int mw=getMeasuredWidth();
int mh=getMeasuredHeight();
最后宽度=兆瓦;
对数i(“比率”,“mw:+mw+”,mh:+mh);
}
实际上,我已经试着做了您之前想做的事情,也就是说,使视图尽量大,同时保留一些纵横比(即正方形或4:3或类似的东西)
我的问题是,当我的视图位于滚动视图中时,大小计算不正确。我无法理解这一点,但我会在下面发布我的代码,以防万一。它与您的代码非常相似,但我继承了FrameLayout
,最后调用了super.onMeasure()
我仔细检查了我在中使用它的项目,实际上它是RelativeLayout
的直接子项目
爪哇:
attrs.xml
:
<resources>
<declare-styleable name="RatioFrameLayout">
<attr name="ratio" format="string|reference" />
</declare-styleable>
</resources>
@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);
}
/**
* A FrameLayout which tries to be as big as possible while maintaining a given ratio between its width and height.
* Formula: Height = Width / ratio;
* Usage:
* 1) Set layout_width and layout_height to "match_parent"
* 2) For 4:3 for example, set ratio to 4/3 = 1.333333 etc. Or don't specify, and it will be square by default.
*/
public class RatioFrameLayout extends FrameLayout
{
private final static float DEFAULT_RATIO = 1f;
private float ratio;
private boolean measured = false;
public RatioFrameLayout(Context context)
{
super(context);
}
public RatioFrameLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
readAttributes(context, attrs);
}
public RatioFrameLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
readAttributes(context, attrs);
}
private void readAttributes(Context context, AttributeSet attrs)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout);
String value = a.getString(R.styleable.RatioFrameLayout_ratio);
try
{
ratio = Float.parseFloat(value);
}
catch (Exception e)
{
ratio = DEFAULT_RATIO;
}
a.recycle();
}
public float getRatio()
{
return ratio;
}
public void setRatio(float ratio)
{
this.ratio = ratio;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int targetWidth = getMeasuredWidth();
int targetHeight = Math.round((float)getMeasuredWidth() / ratio);
if (targetHeight > getMeasuredHeight() && getMeasuredHeight() != 0)
{
targetWidth = Math.round(getMeasuredHeight() * ratio);
targetHeight = getMeasuredHeight();
}
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(targetHeight, MeasureSpec.EXACTLY));
}
private void printMeasureSpec(String description, int value)
{
int mode = MeasureSpec.getMode(value);
String modeName = mode == MeasureSpec.AT_MOST ? "AT_MOST"
: mode == MeasureSpec.EXACTLY ? "EXACTLY" : "UNSPECIFIED";
DLog.d(String.format("Measure spec for %s, mode = %s, size = %d", description, modeName, MeasureSpec.getSize(value)));
}
}
<resources>
<declare-styleable name="RatioFrameLayout">
<attr name="ratio" format="string|reference" />
</declare-styleable>
</resources>