#android #layout #android-framelayout
#Android #макет #android-framelayout
Вопрос:
Я создаю пользовательский класс layout, и представления в макете отображаются должным образом. Проблема, с которой я сталкиваюсь, заключается в том, что если я включу другой макет в этот, размер всех дочерних элементов вложенного представления будет в основном полноэкранным.
XML-код макета — содержимое в FrameLayout не имеет правильного размера.
<CustomLayout>
<ImageView android:layout_height="wrap_content" android:layout_width="wrap_content"/>
<FrameLayout android:layout_height="wrap_content" android:layout_width="wrap_content">
<!-- The views in here are sizing themselves as full screen. -->
<include layout="@layout/panel_emptypicture" android:id="@ id/EmptyPictureRelativeLayout" android:visibility="visible" />
</FrameLayout>
<EditText android:layout_height="wrap_content" android:layout_width="wrap_content" />
<EditText android:layout_height="wrap_content" android:layout_width="wrap_content" />
</CustomLayout>
Java-код для пользовательского макета.
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int numChildren = getChildCount();
Dimension d = new Dimension(0, 0, this.getWidth(), this.getHeight());
final float scaleX = this.getWidth() / virtualWidth;
final float scaleY = this.getHeight() / virtualHeight;
for(int i = 0; i < numChildren; i ) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Dimension lpd = lp.getDimension();
Dimension dest = lpd.scale(scaleX, scaleY, d);
child.layout(dest.x, dest.y, dest.width dest.x, dest.height dest.y);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width, height;
float maxWidth = MeasureSpec.getSize(widthMeasureSpec);
float maxHeight = MeasureSpec.getSize(heightMeasureSpec);
final float widthVirtualMultiplier = virtualWidth / virtualHeight;
final float heightVirtualMultiplier = virtualHeight / virtualWidth;
if(maxWidth * heightVirtualMultiplier < maxHeight) {
width = (int)maxWidth;
height = (int) (width * heightVirtualMultiplier);
} else {
height = (int)maxHeight;
width = (int)(height * widthVirtualMultiplier);
}
setMeasuredDimension(width, height);
// Tried adding this to see if it would do anything. Nope, no effect.
final int numChildren = getChildCount();
for(int i = 0; i < numChildren; i ) {
final View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
Комментарии:
1. можете ли вы опубликовать panel_emptypicture.xml ? Кроме того, какой класс расширяет CustomLayout?
2. panel_emptypicture.xml это просто относительный макет с несколькими кнопками в каждых 4 углах. CustomLayout расширяет ViewGroup. Он был смоделирован после FrameLayout, но имеет совершенно другие параметры Layout.
Ответ №1:
В итоге получился код, который был достаточно стабилен для моих нужд. Ошибка все еще была, но ладно.
Весь проект размещен здесь, если вам нужны другие классы, на которые есть ссылки. http://code.google.com/p/motivatormaker-android
package com.futonredemption.makemotivator.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import com.futonredemption.makemotivator.util.RectUtils;
public class ScaledCompositeLayout extends ViewGroup {
@ViewDebug.ExportedProperty(category = "virtual-measurement")
public float virtualHeight;
@ViewDebug.ExportedProperty(category = "virtual-measurement")
public float virtualWidth;
@ViewDebug.ExportedProperty(category = "clamp-width")
protected boolean clampOnWidth;
public ScaledCompositeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScaledCompositeLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
final TypedArray styleValues = context.obtainStyledAttributes(
attrs,
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout,
defStyle, 0);
virtualWidth = styleValues.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_VirtualWidth,
-1);
virtualHeight = styleValues.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_VirtualHeight,
-1);
clampOnWidth = styleValues.getBoolean(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_ClampOnWidth,
false);
styleValues.recycle();
if(virtualWidth == -1) {
throw new IllegalStateException("virtualWidth is required parameter.");
}
if(virtualHeight == -1) {
throw new IllegalStateException("virtualHeight is required parameter.");
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int numChildren = getChildCount();
Rect parentRect = new Rect(0, 0, this.getWidth(), this.getHeight());
final float scaleX = this.getWidth() / virtualWidth;
final float scaleY = this.getHeight() / virtualHeight;
for(int i = 0; i < numChildren; i ) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final RectF lpr = lp.getRectF();
Rect dest = RectUtils.scale(lpr, scaleX, scaleY, new RectF(parentRect));
child.layout(dest.left, dest.top, dest.right, dest.bottom);
//Dimension out = dest;
//throw new IllegalStateException(String.format("Position: %d x %d -- Size: %d x %d", out.x, out.y, out.width, out.height));
}
}
/**
* Uses the Virtual Width and Height to determine the ratio size of the actual layout.
* The layout_width and layout_height parameters are used to determine the maximum bounds.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width, height;
//int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//int heightMode = MeasureSpec.getMode(heightMeasureSpec);
float maxWidth = MeasureSpec.getSize(widthMeasureSpec);
float maxHeight = MeasureSpec.getSize(heightMeasureSpec);
if (maxHeight == 0.0F) {
clampOnWidth = true;
}
final float widthVirtualMultiplier = virtualWidth / virtualHeight;
final float heightVirtualMultiplier = virtualHeight / virtualWidth;
//final float widthActualMultiplier = maxWidth / maxHeight;
//final float heightActualMultiplier = maxHeight / maxWidth;
/*
if(widthMode == MeasureSpec.EXACTLY) {
width = (int)maxWidth;
height = (int) (maxWidth * heightVirtualMultiplier);
} else if(heightMode == MeasureSpec.EXACTLY) {
width = (int)(maxHeight * widthVirtualMultiplier);
height = (int)maxHeight;
} else { // Use scaling */
if(clampOnWidth || maxWidth * heightVirtualMultiplier < maxHeight) {
width = (int)maxWidth;
height = (int) (width * heightVirtualMultiplier);
} else {
height = (int)maxHeight;
width = (int)(height * widthVirtualMultiplier);
}
//}
setMeasuredDimension(width, height);
final int numChildren = getChildCount();
final float scaleX = width / virtualWidth;
final float scaleY = height / virtualHeight;
final Rect parentRect = new Rect(0, 0, this.getWidth(), this.getHeight());
for(int i = 0; i < numChildren; i ) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final RectF lpr = lp.getRectF();
Rect dest = RectUtils.scale(lpr, scaleX, scaleY, new RectF(parentRect));
this.measureChild(child, MeasureSpec.makeMeasureSpec(dest.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dest.height(), MeasureSpec.EXACTLY));
//Dimension out = dest;
//throw new IllegalStateException(String.format("Position: %d x %d -- Size: %d x %d", out.x, out.y, out.width, out.height));
}
/*
for(int i = 0; i < numChildren; i ) {
final View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
*/
//throw new IllegalStateException(String.format("%d x %d -- Physical: %.0f x %.0f -- Virtual: %.0f x %.0f", width, height, maxWidth, maxHeight, virtualWidth, virtualHeight));
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new ScaledCompositeLayout.LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(
ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
public static class LayoutParams extends
android.view.ViewGroup.LayoutParams {
public RectF virtualRect = new RectF();
public RectF getRectF() {
return virtualRect;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
float virtualWidth;
float virtualHeight;
TypedArray styleValues = c
.obtainStyledAttributes(
attrs,
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout);
virtualRect.top = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualTop,
0);
virtualRect.left = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualLeft,
0);
virtualRect.right = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualRight,
0);
virtualRect.bottom = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualBottom,
0);
virtualWidth = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualWidth,
-1);
if (virtualWidth > 0) {
virtualRect.right = virtualRect.left virtualWidth;
}
virtualHeight = styleValues
.getDimension(
com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualHeight,
-1);
if (virtualHeight > 0) {
virtualRect.bottom = virtualRect.top virtualHeight;
}
styleValues.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}