package com.actionbarsherlock.internal.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.internal.nineoldandroids.view.NineViewGroup;
private static final int[] LinearLayout = new int[] {
android.R.attr.baselineAlignedChildIndex,
android.R.attr.weightSum,
android.R.attr.divider,
android.R.attr.measureWithLargestChild,
android.R.attr.showDividers,
android.R.attr.dividerPadding,
};
private static final int LinearLayout_baselineAlignedChildIndex = 0;
private static final int LinearLayout_weightSum = 1;
private static final int LinearLayout_divider = 2;
private static final int LinearLayout_measureWithLargestChild = 3;
private static final int LinearLayout_showDividers = 4;
private static final int LinearLayout_dividerPadding = 5;
public static final int SHOW_DIVIDER_NONE = 0;
public static final int SHOW_DIVIDER_BEGINNING = 1;
public static final int SHOW_DIVIDER_MIDDLE = 2;
public static final int SHOW_DIVIDER_END = 4;
private boolean mBaselineAligned = true;
private int mBaselineAlignedChildIndex = -1;
private int mBaselineChildTop = 0;
private int mGravity = Gravity.START | Gravity.TOP;
private int mTotalLength;
private float mWeightSum;
private boolean mUseLargestChild;
private int[] mMaxAscent;
private int[] mMaxDescent;
private static final int VERTICAL_GRAVITY_COUNT = 4;
private static final int INDEX_CENTER_VERTICAL = 0;
private static final int INDEX_TOP = 1;
private static final int INDEX_BOTTOM = 2;
private static final int INDEX_FILL = 3;
private Drawable mDivider;
private int mDividerWidth;
private int mShowDividers;
private int mDividerPadding;
super(context);
}
this(context, attrs, 0);
}
public IcsLinearLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, LinearLayout, defStyle, 0);
mWeightSum = a.getFloat(LinearLayout_weightSum, -1.0f);
mBaselineAlignedChildIndex = a.getInt(LinearLayout_baselineAlignedChildIndex, -1);
mUseLargestChild = a.getBoolean(LinearLayout_measureWithLargestChild, false);
setDividerDrawable(a.getDrawable(LinearLayout_divider));
mShowDividers = a.getInt(LinearLayout_showDividers, SHOW_DIVIDER_NONE);
mDividerPadding = a.getDimensionPixelSize(LinearLayout_dividerPadding, 0);
a.recycle();
}
@Override
return false;
}
if (divider == mDivider) {
return;
}
mDivider = divider;
if (divider != null) {
mDividerWidth = divider.getIntrinsicWidth();
} else {
mDividerWidth = 0;
}
setWillNotDraw(divider == null);
requestLayout();
}
return mDividerWidth;
}
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
return;
}
final int count = getVirtualChildCount();
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int left = child.getLeft() - lp.leftMargin;
drawVerticalDivider(canvas, left);
}
}
}
if (hasDividerBeforeChildAt(count)) {
final View child = getVirtualChildAt(count - 1);
int right = 0;
if (child == null) {
right = getWidth() - getPaddingRight() - mDividerWidth;
} else {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
right = child.getRight() + lp.rightMargin;
}
drawVerticalDivider(canvas, right);
}
}
mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
mDivider.draw(canvas);
}
mBaselineAligned = baselineAligned;
}
@Override
if (mBaselineAlignedChildIndex < 0) {
return super.getBaseline();
}
if (getChildCount() <= mBaselineAlignedChildIndex) {
throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
+ "set to an index that is out of bounds.");
}
final View child = getChildAt(mBaselineAlignedChildIndex);
final int childBaseline = child.getBaseline();
if (childBaseline == -1) {
if (mBaselineAlignedChildIndex == 0) {
return -1;
}
throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
+ "points to a View that doesn't know how to get its baseline.");
}
int childTop = mBaselineChildTop;
IcsLinearLayout.LayoutParams lp = (IcsLinearLayout.LayoutParams) child.getLayoutParams();
return childTop + lp.topMargin + childBaseline;
}
return getChildAt(index);
}
return getChildCount();
}
if (childIndex == 0) {
return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
} else if (childIndex == getChildCount()) {
return (mShowDividers & SHOW_DIVIDER_END) != 0;
} else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
boolean hasVisibleViewBefore = false;
for (int i = childIndex - 1; i >= 0; i--) {
if (getChildAt(i).getVisibility() != GONE) {
hasVisibleViewBefore = true;
break;
}
}
return hasVisibleViewBefore;
}
return false;
}
@Override
protected void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec) {
mTotalLength = 0;
int maxHeight = 0;
int childState = 0;
int alternativeMaxHeight = 0;
int weightedMaxHeight = 0;
boolean allFillParent = true;
float totalWeight = 0;
final int count = getVirtualChildCount();
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean matchHeight = false;
if (mMaxAscent == null || mMaxDescent == null) {
mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
}
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
final boolean baselineAligned = mBaselineAligned;
final boolean useLargestChild = mUseLargestChild;
final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
int largestChildWidth = Integer.MIN_VALUE;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerWidth;
}
final IcsLinearLayout.LayoutParams lp = (IcsLinearLayout.LayoutParams)
child.getLayoutParams();
totalWeight += lp.weight;
if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
if (isExactly) {
mTotalLength += lp.leftMargin + lp.rightMargin;
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength +
lp.leftMargin + lp.rightMargin);
}
if (baselineAligned) {
final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(freeSpec, freeSpec);
}
} else {
int oldWidth = Integer.MIN_VALUE;
if (lp.width == 0 && lp.weight > 0) {
oldWidth = 0;
lp.width = LayoutParams.WRAP_CONTENT;
}
measureChildBeforeLayout(child, i, widthMeasureSpec,
totalWeight == 0 ? mTotalLength : 0,
heightMeasureSpec, 0);
if (oldWidth != Integer.MIN_VALUE) {
lp.width = oldWidth;
}
final int childWidth = child.getMeasuredWidth();
if (isExactly) {
mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
lp.rightMargin + getNextLocationOffset(child));
}
if (useLargestChild) {
largestChildWidth = Math.max(childWidth, largestChildWidth);
}
}
boolean matchHeightLocally = false;
if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
matchHeight = true;
matchHeightLocally = true;
}
final int margin = lp.topMargin + lp.bottomMargin;
final int childHeight = child.getMeasuredHeight() + margin;
childState = combineMeasuredStatesInt(childState, IcsView.getMeasuredStateInt(child));
if (baselineAligned) {
final int childBaseline = child.getBaseline();
if (childBaseline != -1) {
final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
& Gravity.VERTICAL_GRAVITY_MASK;
final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
& ~Gravity.AXIS_SPECIFIED) >> 1;
maxAscent[index] = Math.max(maxAscent[index], childBaseline);
maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
}
}
maxHeight = Math.max(maxHeight, childHeight);
allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
weightedMaxHeight = Math.max(weightedMaxHeight,
matchHeightLocally ? margin : childHeight);
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight,
matchHeightLocally ? margin : childHeight);
}
i += getChildrenSkipCount(child, i);
}
if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerWidth;
}
if (maxAscent[INDEX_TOP] != -1 ||
maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
maxAscent[INDEX_BOTTOM] != -1 ||
maxAscent[INDEX_FILL] != -1) {
final int ascent = Math.max(maxAscent[INDEX_FILL],
Math.max(maxAscent[INDEX_CENTER_VERTICAL],
Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
final int descent = Math.max(maxDescent[INDEX_FILL],
Math.max(maxDescent[INDEX_CENTER_VERTICAL],
Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
maxHeight = Math.max(maxHeight, ascent + descent);
}
if (useLargestChild &&
(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
final IcsLinearLayout.LayoutParams lp = (IcsLinearLayout.LayoutParams)
child.getLayoutParams();
if (isExactly) {
mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
}
mTotalLength += getPaddingLeft() + getPaddingRight();
int widthSize = mTotalLength;
widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
int widthSizeAndState = IcsView.resolveSizeAndState(widthSize, widthMeasureSpec, 0);
widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
int delta = widthSize - mTotalLength;
if (delta != 0 && totalWeight > 0.0f) {
float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
maxHeight = -1;
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null || child.getVisibility() == View.GONE) {
continue;
}
final IcsLinearLayout.LayoutParams lp =
(IcsLinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;
final int childHeightMeasureSpec = getChildMeasureSpec(
heightMeasureSpec,
getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin,
lp.height);
if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
int childWidth = child.getMeasuredWidth() + share;
if (childWidth < 0) {
childWidth = 0;
}
child.measure(
MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
childHeightMeasureSpec);
} else {
child.measure(MeasureSpec.makeMeasureSpec(
share > 0 ? share : 0, MeasureSpec.EXACTLY),
childHeightMeasureSpec);
}
childState = combineMeasuredStatesInt(childState,
IcsView.getMeasuredStateInt(child) & MEASURED_STATE_MASK);
}
if (isExactly) {
mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
lp.height == LayoutParams.MATCH_PARENT;
final int margin = lp.topMargin + lp .bottomMargin;
int childHeight = child.getMeasuredHeight() + margin;
maxHeight = Math.max(maxHeight, childHeight);
alternativeMaxHeight = Math.max(alternativeMaxHeight,
matchHeightLocally ? margin : childHeight);
allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
if (baselineAligned) {
final int childBaseline = child.getBaseline();
if (childBaseline != -1) {
final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
& Gravity.VERTICAL_GRAVITY_MASK;
final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
& ~Gravity.AXIS_SPECIFIED) >> 1;
maxAscent[index] = Math.max(maxAscent[index], childBaseline);
maxDescent[index] = Math.max(maxDescent[index],
childHeight - childBaseline);
}
}
}
mTotalLength += getPaddingLeft() + getPaddingRight();
if (maxAscent[INDEX_TOP] != -1 ||
maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
maxAscent[INDEX_BOTTOM] != -1 ||
maxAscent[INDEX_FILL] != -1) {
final int ascent = Math.max(maxAscent[INDEX_FILL],
Math.max(maxAscent[INDEX_CENTER_VERTICAL],
Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
final int descent = Math.max(maxDescent[INDEX_FILL],
Math.max(maxDescent[INDEX_CENTER_VERTICAL],
Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
maxHeight = Math.max(maxHeight, ascent + descent);
}
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null || child.getVisibility() == View.GONE) {
continue;
}
final IcsLinearLayout.LayoutParams lp =
(IcsLinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
MeasureSpec.EXACTLY));
}
}
}
}
if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
maxHeight = alternativeMaxHeight;
}
maxHeight += getPaddingTop() + getPaddingBottom();
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
IcsView.resolveSizeAndState(maxHeight, heightMeasureSpec,
(childState<<MEASURED_HEIGHT_STATE_SHIFT)));
if (matchHeight) {
forceUniformHeight(count, widthMeasureSpec);
}
}
return curState | newState;
}
int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
MeasureSpec.EXACTLY);
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child.getVisibility() != GONE) {
IcsLinearLayout.LayoutParams lp = (IcsLinearLayout.LayoutParams) child.getLayoutParams();
if (lp.height == LayoutParams.MATCH_PARENT) {
int oldWidth = lp.width;
lp.width = child.getMeasuredWidth();
measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
lp.width = oldWidth;
}
}
}
}
return 0;
}
return 0;
}
int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
int totalHeight) {
measureChildWithMargins(child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight);
}
return 0;
}
return 0;
}
@Override
protected void onLayout(
boolean changed,
int l,
int t,
int r,
int b) {
final boolean isLayoutRtl = false;
final int paddingTop = getPaddingTop();
int childTop;
int childLeft;
final int height = getBottom() - getTop();
int childBottom = height - getPaddingBottom();
int childSpace = height - paddingTop - getPaddingBottom();
final int count = getVirtualChildCount();
final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final boolean baselineAligned = mBaselineAligned;
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
childLeft = getPaddingLeft();
int start = 0;
int dir = 1;
if (isLayoutRtl) {
start = count - 1;
dir = -1;
}
for (int i = 0; i < count; i++) {
int childIndex = start + dir * i;
final View child = getVirtualChildAt(childIndex);
if (child == null) {
childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int childBaseline = -1;
final IcsLinearLayout.LayoutParams lp =
(IcsLinearLayout.LayoutParams) child.getLayoutParams();
if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
childBaseline = child.getBaseline();
}
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = paddingTop + lp.topMargin;
if (childBaseline != -1) {
childTop += maxAscent[INDEX_TOP] - childBaseline;
}
break;
case Gravity.CENTER_VERTICAL:
childTop = paddingTop + ((childSpace - childHeight) / 2)
+ lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = childBottom - childHeight - lp.bottomMargin;
if (childBaseline != -1) {
int descent = child.getMeasuredHeight() - childBaseline;
childTop -= (maxDescent[INDEX_BOTTOM] - descent);
}
break;
default:
childTop = paddingTop;
break;
}
if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
childLeft += lp.leftMargin;
setChildFrame(child, childLeft + getLocationOffset(child), childTop,
childWidth, childHeight);
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
i += getChildrenSkipCount(child, childIndex);
}
}
}
private void setChildFrame(View child,
int left,
int top,
int width,
int height) {
child.layout(left, top, left + width, top + height);
}
@Override
return new IcsLinearLayout.LayoutParams(getContext(), attrs);
}
@Override
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
return new LayoutParams(p);
}
@Override
return p instanceof IcsLinearLayout.LayoutParams;
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public float weight;
public int gravity = -1;
super(c, attrs);
weight = 0;
gravity = -1;
}
super(width, height);
weight = 0;
}
super(width, height);
this.weight = weight;
}
super(p);
}
super(source);
}
}
}