Android API10及以下版本中的顶部边距从高度中减去
在我正在处理的项目中,我将视图定位在Android API10及以下版本中的顶部边距从高度中减去,android,android-layout,android-view,Android,Android Layout,Android View,在我正在处理的项目中,我将视图定位在FrameLayout中的WRAP\u CONTENT: <FrameLayout 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
FrameLayout
中的WRAP\u CONTENT
:
<FrameLayout
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">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF0400">
<TextView
android:background="#04FF00"
android:text="Test"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:layout_gravity="top|left"
/>
</FrameLayout>
</FrameLayout>
自Android 3.0(API11)以来,呈现的布局与预期一致:
但在Android 2.3.3(API10)及以下版本中,绿色TextView
的MarginBottom
似乎是从红色FrameLayout
中提取的:
为什么布局没有按照API10及以下版本中的预期呈现?这是安卓系统中的一个bug吗?如果是,是否有快速的解决方法
我知道我可以将底部填充应用于框架布局
,而不是文本视图
的底部边距。但在我的例子中,我没有简单的方法来实现这一点,因为布局是以编程方式构建的
编辑(22-07)
在我寻找解决这个问题的方法的过程中,我查看了。但是我在FrameLayout
类中找不到任何机会。这很奇怪,因为API11中修复了以下bug(我认为可能与此相关):
编辑(23-07)
利润底部似乎不是问题;移除它没有任何区别。似乎是顶部边距导致API10及以下版本的高度错误。尝试从
文本视图中删除android:layout\u gravity=“top | left”
向父布局添加填充
<FrameLayout 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" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF0400"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingTop="10dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#04FF00"
android:text="Test" />
</FrameLayout>
框架布局文档说明如下:
FrameLayout设计用于在屏幕上标出要显示的区域
单一项目。通常,FrameLayout应用于保存单个
子视图,因为在一个视图中组织子视图可能很困难
这种方式可以扩展到不同的屏幕大小,而不需要孩子
相互重叠
这就是我放弃FrameLayout
的原因
然后,我开始搜索一个不同的视图组
,它支持通过x/y定位元素,并能够提供重力
AbsoluteLayout
仅支持x/y定位(也不推荐使用)
RelativeLayout
进行额外测量,这(在我的情况下)意味着性能不佳
因此,我最终创建了自己的视图组
,它基本上是框架布局
的重力和绝对布局
的x/y的合并:
package com.example.common.widget;
import java.util.ArrayList;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
public class FixedLayout extends ViewGroup
{
private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP;
private final ArrayList<View> mMatchParentChildren = new ArrayList<View>();
public FixedLayout(Context context)
{
super(context);
}
public FixedLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public FixedLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY
|| MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// Find rightmost and bottom-most child
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams)child.getLayoutParams();
maxWidth =
Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin
+ lp.rightMargin);
maxHeight =
Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin
+ lp.bottomMargin);
childState = childState | getChildMeasuredState(child);
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT
|| lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
// Check against minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(
getResolvedSizeAndState(maxWidth, widthMeasureSpec, childState),
getResolvedSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
for (int i = 0; i < mMatchParentChildren.size(); i++) {
final View child = mMatchParentChildren.get(i);
final LayoutParams lp = (LayoutParams)child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec =
MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft()
- getPaddingRight() - lp.leftMargin - lp.rightMargin - lp.x,
MeasureSpec.EXACTLY);
}
else {
childWidthMeasureSpec =
getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight()
+ lp.leftMargin + lp.rightMargin + lp.x, lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec =
MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop()
- getPaddingBottom() - lp.topMargin - lp.bottomMargin - lp.y,
MeasureSpec.EXACTLY);
}
else {
childHeightMeasureSpec =
getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom()
+ lp.topMargin + lp.bottomMargin + lp.y, lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
/**
* Returns a set of layout parameters with a width of
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, a height of
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and with the coordinates (0, 0).
*/
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams()
{
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);
}
/**
* Ask one of the children of this view to measure itself, taking into account both the
* MeasureSpec requirements for this view and its padding and margins. The child must have
* MarginLayoutParams The heavy lifting is done in getChildMeasureSpec.
*
* @param child
* The child to measure
* @param parentWidthMeasureSpec
* The width requirements for this view
* @param widthUsed
* Extra space that has been used up by the parent horizontally (possibly by other
* children of the parent)
* @param parentHeightMeasureSpec
* The height requirements for this view
* @param heightUsed
* Extra space that has been used up by the parent vertically (possibly by other
* children of the parent)
*/
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed)
{
final LayoutParams lp = (LayoutParams)child.getLayoutParams();
final int childWidthMeasureSpec =
getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight()
+ lp.leftMargin + lp.rightMargin + lp.x + widthUsed, lp.width);
final int childHeightMeasureSpec =
getChildMeasureSpec(parentHeightMeasureSpec, getPaddingTop() + getPaddingBottom()
+ lp.topMargin + lp.bottomMargin + lp.y + heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
int count = getChildCount();
final int parentLeft = getPaddingLeft();
final int parentRight = right - left - getPaddingRight();
final int parentTop = getPaddingTop();
final int parentBottom = bottom - top - getPaddingBottom();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams)child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (horizontalGravity) {
case Gravity.LEFT:
childLeft = parentLeft + lp.x;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.x;
break;
case Gravity.RIGHT:
childLeft = parentRight - width - lp.x;
break;
default:
childLeft = parentLeft + lp.x;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.y;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.y;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.y;
break;
default:
childTop = parentTop + lp.y;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new FixedLayout.LayoutParams(getContext(), attrs);
}
// Override to allow type-checking of LayoutParams.
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p)
{
return p instanceof FixedLayout.LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p)
{
return new LayoutParams(p);
}
@Override
public boolean shouldDelayChildPressedState()
{
return false;
}
/**
* Return only the state bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredHeightAndState()} of a child view, combined into one integer. The width
* component is in the regular bits {@link #MEASURED_STATE_MASK} and the height component is at
* the shifted bits {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
*/
public final int getChildMeasuredState(View child)
{
return (child.getMeasuredWidth() & MEASURED_STATE_MASK)
| ((child.getMeasuredHeight() >> MEASURED_HEIGHT_STATE_SHIFT) & (MEASURED_STATE_MASK >> MEASURED_HEIGHT_STATE_SHIFT));
}
/**
* Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpec.
* Will take the desired size, unless a different size is imposed by the constraints. The
* returned value is a compound integer, with the resolved size in the
* {@link #MEASURED_SIZE_MASK} bits and optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set
* if the resulting size is smaller than the size the view wants to be.
*
* @param size
* How big the view wants to be
* @param measureSpec
* Constraints imposed by the parent
* @return Size information bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
public static int getResolvedSizeAndState(int size, int measureSpec, int childMeasuredState)
{
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
}
else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
/**
* Per-child layout information associated with AbsoluteLayout. See
* {@link android.R.styleable#AbsoluteLayout_Layout Absolute Layout Attributes} for a list of
* all child view attributes that this class supports.
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams
{
/**
* The horizontal, or X, location of the child within the view group.
*/
public int x;
/**
* The vertical, or Y, location of the child within the view group.
*/
public int y;
/**
* The gravity to apply with the View to which these layout parameters are associated.
*
* @see android.view.Gravity
*/
public int gravity = -1;
/**
* Creates a new set of layout parameters with the specified width, height and location.
*
* @param width
* the width, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size
* in pixels
* @param height
* the height, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed
* size in pixels
*/
public LayoutParams(int width, int height)
{
super(width, height);
}
/**
* Creates a new set of layout parameters with the specified width, height and location.
*
* @param width
* the width, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size
* in pixels
* @param height
* the height, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed
* size in pixels
* @param x
* the X location of the child
* @param y
* the Y location of the child
*/
public LayoutParams(int width, int height, int x, int y)
{
super(width, height);
this.x = x;
this.y = y;
}
/**
* Creates a new set of layout parameters. The values are extracted from the supplied
* attributes set and context. The XML attributes mapped to this set of layout parameters
* are:
*
* <ul>
* <li><code>layout_x</code>: the X location of the child</li>
* <li><code>layout_y</code>: the Y location of the child</li>
* <li>All the XML attributes from {@link android.view.ViewGroup.LayoutParams}</li>
* </ul>
*
* @param c
* the application environment
* @param attrs
* the set of attributes from which to extract the layout parameters values
*/
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
TypedArray a =
c.obtainStyledAttributes(attrs,
com.example.common.R.styleable.FixedLayout_Layout);
gravity =
a.getInt(com.example.common.R.styleable.FixedLayout_Layout_layout_gravity,
-1);
x =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_x, 0);
y =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_y, 0);
topMargin =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_marginTop,
0);
leftMargin =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_marginLeft,
0);
bottomMargin =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_marginBottom,
0);
rightMargin =
a.getDimensionPixelOffset(
com.example.common.R.styleable.FixedLayout_Layout_layout_marginRight,
0);
a.recycle();
}
/**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
}
}
尝试在属性中设置文本大小(dp)。还要做垫子zero@Diljeet这与@Aprian中的错误没有任何区别。当定位带有边距的视图(左上)时,可能会出现此错误。解决此问题的一个方法是在视图中添加重力顶部;正如您所看到的,我已经在我的示例中这样做了。@sroes为什么会这样?当以编程方式构建时,您应该可以访问父对象和子对象,对吗?如果您可以访问家长,那么困难是什么?这是您的解决方案。删除这意味着忽略所有的边距。至于框架布局上的填充:布局是以编程方式构建的,我没有简单的方法将视图的底部边距应用于父视图的底部填充。