Move to android studio

This commit is contained in:
PanayotCankov
2015-11-03 09:47:24 +02:00
parent c10d53cd22
commit 9fcea45971
34 changed files with 167 additions and 100 deletions

View File

@@ -0,0 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.nativescript.layouts"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="17"
android:targetSdkVersion="21" />
<application
android:allowBackup="true">
</application>
</manifest>

View File

@@ -0,0 +1,82 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.view.View;
/**
* @author hhristov
*
*/
public class AbsoluteLayout extends LayoutBase {
public AbsoluteLayout(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth = 0;
int measureHeight = 0;
int childMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, childMeasureSpec, childMeasureSpec);
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
measureWidth = Math.max(measureWidth, childLayoutParams.left + childMeasuredWidth);
measureHeight = Math.max(measureHeight, childLayoutParams.top + childMeasuredHeight);
}
// Add in our padding
measureWidth += this.getPaddingLeft() + this.getPaddingRight();
measureHeight += this.getPaddingTop() + this.getPaddingBottom();
// Check against our minimum height
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int leftPadding = this.getPaddingLeft();
int topPadding = this.getPaddingTop();
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int childLeft = leftPadding + childLayoutParams.left;
int childTop = topPadding + childLayoutParams.top;
int childRight = childLeft + childWidth + childLayoutParams.leftMargin + childLayoutParams.rightMargin;
int childBottom = childTop + childHeight + childLayoutParams.topMargin + childLayoutParams.bottomMargin;
CommonLayoutParams.layoutChild(child, childLeft, childTop, childRight, childBottom);
}
}
}

View File

@@ -0,0 +1,331 @@
/**
*
*/
package org.nativescript.widgets;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
/**
* @author hhristov
*
*/
public class CommonLayoutParams extends FrameLayout.LayoutParams {
static final String TAG = "NSLayout";
static int debuggable = -1;
private static final StringBuilder sb = new StringBuilder();
public CommonLayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public float widthPercent = 0;
public float heightPercent = 0;
public float topMarginPercent = 0;
public float leftMarginPercent = 0;
public float bottomMarginPercent = 0;
public float rightMarginPercent = 0;
public int left = 0;
public int top = 0;
public int row = 0;
public int column = 0;
public int rowSpan = 1;
public int columnSpan = 1;
public Dock dock = Dock.left;
public static int getDesiredWidth(View view) {
CommonLayoutParams lp = (CommonLayoutParams)view.getLayoutParams();
return view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
public static int getDesiredHeight(View view) {
CommonLayoutParams lp = (CommonLayoutParams)view.getLayoutParams();
return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
// We use our own layout method because the one in FrameLayout is broken when margins are set and gravity is CENTER_VERTICAL or CENTER_HORIZONTAL.
@SuppressLint("RtlHardcoded")
public static void layoutChild(View child, int left, int top, int right, int bottom) {
if (child.getVisibility() == View.GONE) {
return;
}
int childTop = 0;
int childLeft = 0;
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
CommonLayoutParams lp = (CommonLayoutParams)child.getLayoutParams();
int gravity = lp.gravity;
if (gravity == -1) {
gravity = Gravity.FILL;
}
int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
// If we have explicit height and gravity is FILL we need to be centered otherwise our explicit height won't be taken into account.
if (lp.height >= 0 && verticalGravity == Gravity.FILL_VERTICAL) {
verticalGravity = Gravity.CENTER_VERTICAL;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = top + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = top + (bottom - top - childHeight + lp.topMargin - lp.bottomMargin) / 2;
break;
case Gravity.BOTTOM:
childTop = bottom - childHeight - lp.bottomMargin;
break;
case Gravity.FILL_VERTICAL:
default:
childTop = top + lp.topMargin;
childHeight = bottom - top - (lp.topMargin + lp.bottomMargin);
break;
}
int horizontalGravity = Gravity.getAbsoluteGravity(gravity, child.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
// If we have explicit width and gravity is FILL we need to be centered otherwise our explicit width won't be taken into account.
if (lp.width >= 0 && horizontalGravity == Gravity.FILL_HORIZONTAL) {
horizontalGravity = Gravity.CENTER_HORIZONTAL;
}
switch (horizontalGravity) {
case Gravity.LEFT:
childLeft = left + lp.leftMargin;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = left + (right - left - childWidth + lp.leftMargin - lp.rightMargin) / 2;
break;
case Gravity.RIGHT:
childLeft = right - childWidth - lp.rightMargin;
break;
case Gravity.FILL_HORIZONTAL:
default:
childLeft = left + lp.leftMargin;
childWidth = right - left - (lp.leftMargin + lp.rightMargin);
break;
}
int childRight = Math.round(childLeft + childWidth);
int childBottom = Math.round(childTop + childHeight);
childLeft = Math.round(childLeft);
childTop = Math.round(childTop);
// Re-measure TextView because it is not centered if layout width is larger than measure width.
if (child instanceof android.widget.TextView) {
boolean canChangeWidth = lp.width < 0;
boolean canChangeHeight = lp.height < 0;
int measuredWidth = child.getMeasuredWidth();
int measuredHeight = child.getMeasuredHeight();
int width = childRight - childLeft;
int height = childBottom - childTop;
if ((Math.abs(measuredWidth - width) > 1 && canChangeWidth) || (Math.abs(measuredHeight - height) > 1 && canChangeHeight)) {
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(canChangeWidth ? width : lp.width, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(canChangeHeight ? height : lp.height, MeasureSpec.EXACTLY);
if (debuggable > 0) {
sb.setLength(0);
sb.append("remeasure ");
sb.append(child);
sb.append(" with ");
sb.append(MeasureSpec.toString(widthMeasureSpec));
sb.append(", ");
sb.append(MeasureSpec.toString(heightMeasureSpec));
log(TAG, sb.toString());
}
child.measure(widthMeasureSpec, heightMeasureSpec);
}
}
if (debuggable > 0) {
sb.setLength(0);
sb.append(child.getParent().toString());
sb.append(" :layoutChild: ");
sb.append(child.toString());
sb.append(" ");
sb.append(childLeft);
sb.append(", ");
sb.append(childTop);
sb.append(", ");
sb.append(childRight);
sb.append(", ");
sb.append(childBottom);
log(TAG, sb.toString());
}
child.layout(childLeft, childTop, childRight, childBottom);
}
public static void measureChild(View child, int widthMeasureSpec, int heightMeasureSpec) {
if (child.getVisibility() == View.GONE) {
return;
}
// Negative means not initialized.
if(debuggable < 0) {
try {
Context context = child.getContext();
ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), android.content.pm.PackageManager.GET_META_DATA);
android.os.Bundle bundle = ai.metaData;
Boolean debugLayouts = bundle != null ? bundle.getBoolean("debugLayouts", false) : false;
debuggable = debugLayouts ? 1 : 0;
} catch (NameNotFoundException e) {
debuggable = 0;
Log.e(TAG, "Failed to load meta-data, NameNotFound: " + e.getMessage());
} catch (NullPointerException e) {
debuggable = 0;
Log.e(TAG, "Failed to load meta-data, NullPointer: " + e.getMessage());
}
}
int childWidthMeasureSpec = getMeasureSpec(child, widthMeasureSpec, true);
int childHeightMeasureSpec = getMeasureSpec(child, heightMeasureSpec, false);
if (debuggable > 0) {
sb.setLength(0);
sb.append(child.getParent().toString());
sb.append(" :measureChild: ");
sb.append(child.toString());
sb.append(" ");
sb.append(MeasureSpec.toString(childWidthMeasureSpec));
sb.append(", ");
sb.append(MeasureSpec.toString(childHeightMeasureSpec));
log(TAG, sb.toString());
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public static void updateChildLayoutParams(View child, int widthMeasureSpec, int heightMeasureSpec) {
int availableWidth = MeasureSpec.getSize(widthMeasureSpec);
int widthSpec = MeasureSpec.getMode(widthMeasureSpec);
int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
int heightSpec = MeasureSpec.getMode(heightMeasureSpec);
CommonLayoutParams lp = (CommonLayoutParams)child.getLayoutParams();
if (widthSpec != MeasureSpec.UNSPECIFIED) {
if (lp.widthPercent > 0) {
lp.width = (int)(availableWidth * lp.widthPercent);
}
if (lp.leftMarginPercent > 0) {
lp.leftMargin = (int)(availableWidth * lp.leftMarginPercent);
}
if (lp.rightMarginPercent > 0) {
lp.rightMargin = (int)(availableWidth * lp.rightMarginPercent);
}
}
if (heightSpec != MeasureSpec.UNSPECIFIED) {
if (lp.heightPercent > 0) {
lp.height = (int)(availableHeight * lp.heightPercent);
}
if (lp.topMarginPercent > 0) {
lp.topMargin = (int)(availableHeight * lp.topMarginPercent);
}
if (lp.bottomMarginPercent > 0) {
lp.bottomMargin = (int)(availableHeight * lp.bottomMarginPercent);
}
}
}
static void log(String tag, String message) {
Log.d(tag, message);
}
static StringBuilder getStringBuilder() {
sb.setLength(0);
return sb;
}
private static int getMeasureSpec(View view, int parentMeasureSpec, boolean horizontal) {
int parentLength = MeasureSpec.getSize(parentMeasureSpec);
int parentSpecMode = MeasureSpec.getMode(parentMeasureSpec);
CommonLayoutParams lp = (CommonLayoutParams)view.getLayoutParams();
final int margins = horizontal ? lp.leftMargin + lp.rightMargin : lp.topMargin + lp.bottomMargin;
int resultSize = 0;
int resultMode = 0;
int measureLength = Math.max(0, parentLength - margins);
int childLength = horizontal ? lp.width : lp.height;
// We want a specific size... let be it.
if (childLength >= 0) {
if (parentSpecMode != MeasureSpec.UNSPECIFIED) {
resultSize = Math.min(parentLength, childLength);
}
else {
resultSize = childLength;
}
resultMode = MeasureSpec.EXACTLY;
}
else {
switch (parentSpecMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
resultSize = measureLength;
int gravity = LayoutBase.getGravity(view);
boolean stretched;
if (horizontal) {
final int horizontalGravity = Gravity.getAbsoluteGravity(gravity, view.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
stretched = horizontalGravity == Gravity.FILL_HORIZONTAL;
}
else {
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
stretched = verticalGravity == Gravity.FILL_VERTICAL;
}
// if stretched - view wants to be our size. So be it.
// else - view wants to determine its own size. It can't be bigger than us.
resultMode = stretched ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
resultSize = measureLength;
resultMode = MeasureSpec.AT_MOST;
break;
case MeasureSpec.UNSPECIFIED:
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
break;
}
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
}

View File

@@ -0,0 +1,79 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.view.View;
/**
* @author hhristov
*
*/
public class ContentLayout extends LayoutBase {
public ContentLayout(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth = 0;
int measureHeight = 0;
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, widthMeasureSpec, heightMeasureSpec);
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
measureWidth = Math.max(measureWidth, childMeasuredWidth);
measureHeight = Math.max(measureHeight, childMeasuredHeight);
}
// Add in our padding
measureWidth += this.getPaddingLeft() + this.getPaddingRight();
measureHeight += this.getPaddingTop() + this.getPaddingBottom();
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int paddingLeft = this.getPaddingLeft();
int paddingRight = this.getPaddingRight();
int paddingTop = this.getPaddingTop();
int paddingBottom = this.getPaddingBottom();
int childLeft = paddingLeft;
int childTop = paddingTop;
int childRight = right - left - (paddingLeft + paddingRight);
int childBottom = bottom - top - (paddingRight + paddingBottom);
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams.layoutChild(child, childLeft, childTop, childRight, childBottom);
}
}
}

View File

@@ -0,0 +1,15 @@
/**
*
*/
package org.nativescript.widgets;
/**
* @author hhristov
*
*/
public enum Dock {
left,
top,
right,
bottom
}

View File

@@ -0,0 +1,177 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.view.View;
/**
* @author hhristov
*
*/
public class DockLayout extends LayoutBase {
private boolean _stretchLastChild = true;
public DockLayout(Context context) {
super(context);
}
public boolean getStretchLastChild() {
return this._stretchLastChild;
}
public void setStretchLastChild(boolean value) {
this._stretchLastChild = value;
this.requestLayout();
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth = 0;
int measureHeight = 0;
int width = View.MeasureSpec.getSize(widthMeasureSpec);
int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
int verticalPadding = this.getPaddingTop() + this.getPaddingBottom();
int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight();
int remainingWidth = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : width - horizontalPadding;
int remainingHeight = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : height - verticalPadding;
int tempHeight = 0;
int tempWidth = 0;
int childWidthMeasureSpec = 0;
int childHeightMeasureSpec = 0;
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
if (this._stretchLastChild && (i == (count - 1))) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(remainingWidth, widthMode);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(remainingHeight, heightMode);
}
else {
// Measure children with AT_MOST even if our mode is EXACT
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(remainingWidth, widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthMode);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(remainingHeight, heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightMode);
}
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec);
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
Dock dock = childLayoutParams.dock;
switch (dock) {
case top:
case bottom:
remainingHeight = Math.max(0, remainingHeight - childMeasuredHeight);
tempHeight += childMeasuredHeight;
measureWidth = Math.max(measureWidth, tempWidth + childMeasuredWidth);
measureHeight = Math.max(measureHeight, tempHeight);
break;
case left:
case right:
default:
remainingWidth = Math.max(0, remainingWidth - childMeasuredWidth);
tempWidth += childMeasuredWidth;
measureWidth = Math.max(measureWidth, tempWidth);
measureHeight = Math.max(measureHeight, tempHeight + childMeasuredHeight);
break;
}
}
// Add in our padding
measureWidth += horizontalPadding;
measureHeight += verticalPadding;
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = this.getPaddingLeft();
int childTop = this.getPaddingTop();
int x = childLeft;
int y = childTop;
int remainingWidth = Math.max(0, right - left - (this.getPaddingLeft() + this.getPaddingRight()));
int remainingHeight = Math.max(0, bottom - top - (this.getPaddingTop() + this.getPaddingBottom()));
int count = this.getChildCount();
View childToStretch = null;
if (count > 0 && this._stretchLastChild) {
count--;
childToStretch = this.getChildAt(count);
}
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
int childWidth = CommonLayoutParams.getDesiredWidth(child);
int childHeight = CommonLayoutParams.getDesiredHeight(child);
switch (childLayoutParams.dock) {
case top:
childLeft = x;
childTop = y;
childWidth = remainingWidth;
y += childHeight;
remainingHeight = Math.max(0, remainingHeight - childHeight);
break;
case bottom:
childLeft = x;
childTop = y + remainingHeight - childHeight;
childWidth = remainingWidth;
remainingHeight = Math.max(0, remainingHeight - childHeight);
break;
case right:
childLeft = x + remainingWidth - childWidth;
childTop = y;
childHeight = remainingHeight;
remainingWidth = Math.max(0, remainingWidth - childWidth);
break;
case left:
default:
childLeft = x;
childTop = y;
childHeight = remainingHeight;
x += childWidth;
remainingWidth = Math.max(0, remainingWidth - childWidth);
break;
}
CommonLayoutParams.layoutChild(child, childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
if (childToStretch != null) {
CommonLayoutParams.layoutChild(childToStretch, x, y, x + remainingWidth, y + remainingHeight);
}
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
/**
*
*/
package org.nativescript.widgets;
/**
* @author hhristov
*
*/
public enum GridUnitType {
auto,
pixel,
star
}

View File

@@ -0,0 +1,249 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
/**
* @author hhristov
*
*/
public class HorizontalScrollView extends android.widget.HorizontalScrollView {
private final Rect mTempRect = new Rect();
private int contentMeasuredWidth = 0;
private int contentMeasuredHeight = 0;
private int scrollableLength = 0;
private SavedState mSavedState;
private boolean isFirstLayout = true;
/**
* True when the layout has changed but the traversal has not come through yet.
* Ideally the view hierarchy would keep track of this for us.
*/
private boolean mIsLayoutDirty = true;
/**
* The child to give focus to in the event that a child has requested focus while the
* layout is dirty. This prevents the scroll from being wrong if the child has not been
* laid out before requesting focus.
*/
private View mChildToScrollTo = null;
public HorizontalScrollView(Context context) {
super(context);
}
public int getScrollableLength() {
return this.scrollableLength;
}
@Override
public void requestLayout() {
this.mIsLayoutDirty = true;
super.requestLayout();
}
@Override
public void requestChildFocus(View child, View focused) {
if (!mIsLayoutDirty) {
this.scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Don't call measure because it will measure content twice.
// ScrollView is expected to have single child so we measure only the first child.
View child = this.getChildCount() > 0 ? this.getChildAt(0) : null;
if (child == null) {
this.scrollableLength = 0;
this.contentMeasuredWidth = 0;
this.contentMeasuredHeight = 0;
}
else {
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec);
this.contentMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
this.contentMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
// Android ScrollView does not account to child margins so we set them as paddings. Otherwise you can never scroll to bottom.
CommonLayoutParams lp = (CommonLayoutParams)child.getLayoutParams();
this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
}
// Don't add in our paddings because they are already added as child margins. (we will include them twice if we add them).
// Check the previous line - this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
// this.contentMeasuredWidth += this.getPaddingLeft() + this.getPaddingRight();
// this.contentMeasuredHeight += this.getPaddingTop() + this.getPaddingBottom();
// Check against our minimum height
this.contentMeasuredWidth = Math.max(this.contentMeasuredWidth, this.getSuggestedMinimumWidth());
this.contentMeasuredHeight = Math.max(this.contentMeasuredHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(this.contentMeasuredWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(this.contentMeasuredHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childWidth = 0;
if (this.getChildCount() > 0) {
View child = this.getChildAt(0);
childWidth = child.getMeasuredWidth();
int width = right - left;
int height = bottom - top;
this.scrollableLength = this.contentMeasuredWidth - width;
CommonLayoutParams.layoutChild(child, 0, 0, Math.max(this.contentMeasuredWidth, width), height);
this.scrollableLength = Math.max(0, this.scrollableLength);
}
this.mIsLayoutDirty = false;
// Give a child focus if it needs it
if (this.mChildToScrollTo != null && isViewDescendantOf(this.mChildToScrollTo, this)) {
this.scrollToChild(this.mChildToScrollTo);
}
this.mChildToScrollTo = null;
int scrollX = this.getScrollX();
int scrollY = this.getScrollY();
if (this.isFirstLayout) {
this.isFirstLayout = false;
final int scrollRange = Math.max(0, childWidth - (right - left - this.getPaddingLeft() - this.getPaddingRight()));
if (this.mSavedState != null) {
scrollX = (this.isLayoutRtl() == mSavedState.isLayoutRtl) ? mSavedState.scrollPosition : (scrollRange - this.mSavedState.scrollPosition);
mSavedState = null;
} else {
if (this.isLayoutRtl()) {
scrollX = scrollRange - scrollX;
} // mScrollX default value is "0" for LTR
}
// Don't forget to clamp
if (scrollX > scrollRange) {
scrollX = scrollRange;
} else if (scrollX < 0) {
scrollX = 0;
}
}
// Calling this with the present values causes it to re-claim them
this.scrollTo(scrollX, scrollY);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
this.isFirstLayout = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.isFirstLayout = true;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
this.mSavedState = ss;
this.requestLayout();
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.scrollPosition = this.getScrollX();
ss.isLayoutRtl = this.isLayoutRtl();
return ss;
}
private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
/* Offset from child's local coordinates to ScrollView coordinates */
offsetDescendantRectToMyCoords(child, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
if (scrollDelta != 0) {
this.scrollBy(scrollDelta, 0);
}
}
private boolean isLayoutRtl() {
return (this.getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
/**
* Return true if child is a descendant of parent, (or equal to the parent).
*/
static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
final ViewParent theParent = child.getParent();
return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
}
static class SavedState extends BaseSavedState {
public int scrollPosition;
public boolean isLayoutRtl;
SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
scrollPosition = source.readInt();
isLayoutRtl = (source.readInt() == 0) ? true : false;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(scrollPosition);
dest.writeInt(isLayoutRtl ? 1 : 0);
}
@Override
public String toString() {
return "HorizontalScrollView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " scrollPosition=" + scrollPosition
+ " isLayoutRtl=" + isLayoutRtl + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

View File

@@ -0,0 +1,168 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.Drawable;
/**
* @author hhristov
*
*/
public class ImageView extends android.widget.ImageView {
private float cornerRadius = 0;
private float borderWidth = 0;
private Path path = new Path();
private RectF rect = new RectF();
private double scaleW = 1;
private double scaleH = 1;
public ImageView(Context context) {
super(context);
this.setScaleType(ScaleType.FIT_CENTER);
}
public float getCornerRadius() {
return this.cornerRadius;
}
public void setCornerRadius(float radius) {
if (radius != this.cornerRadius) {
this.cornerRadius = radius;
this.invalidate();
}
}
public float getBorderWidth() {
return this.borderWidth;
}
public void setBorderWidth(float radius) {
if (radius != this.borderWidth) {
this.borderWidth = radius;
this.invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Drawable drawable = this.getDrawable();
int measureWidth;
int measureHeight;
if (drawable != null) {
measureWidth = drawable.getIntrinsicWidth();
measureHeight = drawable.getIntrinsicHeight();
} else {
measureWidth = 0;
measureHeight = 0;
}
boolean finiteWidth = widthMode != MeasureSpec.UNSPECIFIED;
boolean finiteHeight = heightMode != MeasureSpec.UNSPECIFIED;
if (measureWidth != 0 && measureHeight != 0 && (finiteWidth || finiteHeight)) {
this.computeScaleFactor(width, height, finiteWidth, finiteHeight, measureWidth, measureHeight);
int resultW = (int) Math.floor(measureWidth * this.scaleW);
int resultH = (int) Math.floor(measureHeight * this.scaleH);
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
}
measureWidth += this.getPaddingLeft() + this.getPaddingRight();
measureHeight += this.getPaddingTop() + this.getPaddingBottom();
measureWidth = Math.max(measureWidth, getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, getSuggestedMinimumHeight());
if (CommonLayoutParams.debuggable > 0) {
StringBuilder sb = CommonLayoutParams.getStringBuilder();
sb.append("ImageView onMeasure: ");
sb.append(MeasureSpec.toString(widthMeasureSpec));
sb.append(", ");
sb.append(MeasureSpec.toString(heightMeasureSpec));
sb.append(", stretch: ");
sb.append(this.getScaleType());
sb.append(", measureWidth: ");
sb.append(measureWidth);
sb.append(", measureHeight: ");
sb.append(measureHeight);
CommonLayoutParams.log(CommonLayoutParams.TAG, sb.toString());
}
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
private void computeScaleFactor(int measureWidth, int measureHeight, boolean widthIsFinite, boolean heightIsFinite, double nativeWidth, double nativeHeight) {
this.scaleW = 1;
this.scaleH = 1;
ScaleType scale = this.getScaleType();
if ((scale == ScaleType.CENTER_CROP || scale == ScaleType.FIT_CENTER || scale == ScaleType.FIT_XY) &&
(widthIsFinite || heightIsFinite)) {
this.scaleW = (nativeWidth > 0) ? measureWidth / nativeWidth : 0d;
this.scaleH = (nativeHeight > 0) ? measureHeight / nativeHeight : 0d;
if (!widthIsFinite) {
this.scaleW = scaleH;
} else if (!heightIsFinite) {
this.scaleH = scaleW;
} else {
// No infinite dimensions.
switch (scale) {
case FIT_CENTER:
this.scaleH = this.scaleW < this.scaleH ? this.scaleW : this.scaleH;
this.scaleW = this.scaleH;
break;
case CENTER_CROP:
this.scaleH = this.scaleW > this.scaleH ? this.scaleW : this.scaleH;
this.scaleW = this.scaleH;
break;
default:
break;
}
}
}
}
@Override
protected void onDraw(Canvas canvas) {
// floor the border width to avoid gaps between the border and the image
float roundedBorderWidth = (float) Math.floor(this.borderWidth);
float innerRadius = Math.max(0, this.cornerRadius - roundedBorderWidth);
// The border width is included in the padding so there is no need for
// clip if there is no inner border radius.
if (innerRadius != 0) {
this.rect.set(
roundedBorderWidth,
roundedBorderWidth,
this.getWidth() - roundedBorderWidth,
this.getHeight() - roundedBorderWidth);
this.path.reset();
this.path.addRoundRect(rect, innerRadius, innerRadius, android.graphics.Path.Direction.CW);
canvas.clipPath(this.path);
}
super.onDraw(canvas);
}
}

View File

@@ -0,0 +1,60 @@
/**
*
*/
package org.nativescript.widgets;
/**
* @author hhristov
*
*/
public class ItemSpec {
private int _value;
private GridUnitType _unitType;
public ItemSpec() {
this(1, GridUnitType.star);
}
public ItemSpec(int value, GridUnitType unitType) {
this._value = value;
this._unitType = unitType;
}
GridLayout owner;
int _actualLength = 0;
@Override
public boolean equals(Object o) {
if (!(o instanceof ItemSpec)) {
return false;
}
ItemSpec other = (ItemSpec)o;
return (this._unitType == other._unitType) && (this._value == other._value) && (this.owner == other.owner);
}
public GridUnitType getGridUnitType() {
return this._unitType;
}
public boolean getIsAbsolute() {
return this._unitType == GridUnitType.pixel;
}
public boolean getIsAuto() {
return this._unitType == GridUnitType.auto;
}
public boolean getIsStar() {
return this._unitType == GridUnitType.star;
}
public int getValue() {
return this._value;
}
public int getActualLength() {
return this._actualLength;
}
}

View File

@@ -0,0 +1,67 @@
/**
*
*/
package org.nativescript.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
/**
* @author hhristov
*
*/
public abstract class LayoutBase extends ViewGroup {
public LayoutBase(Context context) {
super(context);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof CommonLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new CommonLayoutParams();
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
protected static int getGravity(View view) {
int gravity = -1;
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof FrameLayout.LayoutParams) {
gravity = ((FrameLayout.LayoutParams)params).gravity;
}
if (gravity == -1) {
gravity = Gravity.FILL;
}
return gravity;
}
}

View File

@@ -0,0 +1,13 @@
/**
*
*/
package org.nativescript.widgets;
/**
* @author hhristov
*
*/
public enum Orientation {
horzontal,
vertical
}

View File

@@ -0,0 +1,218 @@
/**
*
*/
package org.nativescript.widgets;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
/**
* @author hhristov
*
*/
public class StackLayout extends LayoutBase {
private int _totalLength = 0;
private Orientation _orientation = Orientation.vertical;
public StackLayout(Context context) {
super(context);
}
public Orientation getOrientation() {
return this._orientation;
}
public void setOrientation(Orientation value) {
this._orientation = value;
this.requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childState = 0;
int measureWidth = 0;
int measureHeight = 0;
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean isVertical = this._orientation == Orientation.vertical;
int verticalPadding = this.getPaddingTop() + this.getPaddingBottom();
int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight();
int count = this.getChildCount();
int measureSpecMode;
int remainingLength;
int mode = isVertical ? heightMode : widthMode;
if (mode == MeasureSpec.UNSPECIFIED) {
measureSpecMode = MeasureSpec.UNSPECIFIED;
remainingLength = 0;
}
else {
measureSpecMode = MeasureSpec.AT_MOST;
remainingLength = isVertical ? height - verticalPadding : width - horizontalPadding;
}
int childMeasureSpec;
if (isVertical) {
int childWidth = (widthMode == MeasureSpec.UNSPECIFIED) ? 0 : width - horizontalPadding;
childWidth = Math.max(0, childWidth);
childMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, widthMode);
}
else {
int childHeight = (heightMode == MeasureSpec.UNSPECIFIED) ? 0 : height - verticalPadding;
childHeight = Math.max(0, childHeight);
childMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, heightMode);
}
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
if (isVertical) {
CommonLayoutParams.measureChild(child, childMeasureSpec, MeasureSpec.makeMeasureSpec(remainingLength, measureSpecMode));
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
measureWidth = Math.max(measureWidth, childMeasuredWidth);
measureHeight += childMeasuredHeight;
remainingLength = Math.max(0, remainingLength - childMeasuredHeight);
}
else {
CommonLayoutParams.measureChild(child, MeasureSpec.makeMeasureSpec(remainingLength, measureSpecMode), childMeasureSpec);
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
measureHeight = Math.max(measureHeight, childMeasuredHeight);
measureWidth += childMeasuredWidth;
remainingLength = Math.max(0, remainingLength - childMeasuredWidth);
}
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
// Add in our padding
measureWidth += horizontalPadding;
measureHeight += verticalPadding;
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
this._totalLength = isVertical ? measureHeight : measureWidth;
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, isVertical ? childState : 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, isVertical ? 0 : childState);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (this._orientation == Orientation.vertical) {
this.layoutVertical(l, t, r, b);
}
else {
this.layoutHorizontal(l, t, r, b);
}
}
private void layoutVertical(int left, int top, int right, int bottom) {
int paddingLeft = this.getPaddingLeft();
int paddingRight = this.getPaddingRight();
int paddingTop = this.getPaddingTop();
int paddingBottom = this.getPaddingBottom();
int childTop = 0;
int childLeft = paddingLeft;
int childRight = right - left - paddingRight;
int gravity = getGravity(this);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (verticalGravity) {
case Gravity.CENTER_VERTICAL:
childTop = (bottom - top - this._totalLength) / 2 + paddingTop - paddingBottom;
break;
case Gravity.BOTTOM:
childTop = bottom - top - this._totalLength + paddingTop - paddingBottom;
break;
case Gravity.TOP:
case Gravity.FILL_VERTICAL:
default:
childTop = paddingTop;
break;
}
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
int childHeight = child.getMeasuredHeight() + childLayoutParams.topMargin + childLayoutParams.bottomMargin;
CommonLayoutParams.layoutChild(child, childLeft, childTop, childRight, childTop + childHeight);
childTop += childHeight;
}
}
@SuppressLint("RtlHardcoded")
private void layoutHorizontal(int left, int top, int right, int bottom) {
int paddingLeft = this.getPaddingLeft();
int paddingRight = this.getPaddingRight();
int paddingTop = this.getPaddingTop();
int paddingBottom = this.getPaddingBottom();
int childTop = paddingTop;
int childLeft = 0;
int childBottom = bottom - top - paddingBottom;
int gravity = getGravity(this);
final int horizontalGravity = Gravity.getAbsoluteGravity(gravity, this.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
switch (horizontalGravity) {
case Gravity.CENTER_HORIZONTAL:
childLeft = (right - left - this._totalLength) / 2 + paddingLeft - paddingRight;
break;
case Gravity.RIGHT:
childLeft = right - left - this._totalLength + paddingLeft - paddingRight;
break;
case Gravity.LEFT:
case Gravity.FILL_HORIZONTAL:
default:
childLeft = paddingLeft;
break;
}
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams childLayoutParams = (CommonLayoutParams)child.getLayoutParams();
int childWidth = child.getMeasuredWidth() + childLayoutParams.leftMargin + childLayoutParams.rightMargin;
CommonLayoutParams.layoutChild(child, childLeft, childTop, childLeft + childWidth, childBottom);
childLeft += childWidth;
}
}
}

View File

@@ -0,0 +1,9 @@
package org.nativescript.widgets;
import android.graphics.drawable.Drawable;
public class TabItemSpec {
public String title;
public int iconId;
public Drawable iconDrawable;
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nativescript.widgets;
import android.content.Context;
import android.graphics.Typeface;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* To be used with ViewPager to provide a tab indicator component which give
* constant feedback as to the user's scroll progress.
* <p>
* To use the component, simply add it to your view hierarchy. Then in your
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
* {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is
* being used for.
* <p>
* The colors can be customized in two ways. The first and simplest is to
* provide an array of colors via {@link #setSelectedIndicatorColors(int...)}.
* The alternative is via the {@link TabColorizer} interface which provides you
* complete control over which color is used for any individual position.
* <p>
*/
public class TabLayout extends HorizontalScrollView {
/**
* Allows complete control over the colors drawn in the tab layout. Set with
* {@link #setCustomTabColorizer(TabColorizer)}.
*/
public interface TabColorizer {
/**
* @return return the color of the indicator used when {@code position}
* is selected.
*/
int getIndicatorColor(int position);
}
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
private static final int TEXT_MAX_WIDHT = 180;
private static final int SMALL_MIN_HEIGHT = 48;
private static final int LARGE_MIN_HEIGHT = 72;
private int mTitleOffset;
private boolean mDistributeEvenly = true;
private TabItemSpec[] mTabItems;
private ViewPager mViewPager;
private SparseArray<String> mContentDescriptions = new SparseArray<String>();
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
private final TabStrip mTabStrip;
public TabLayout(Context context) {
this(context, null);
}
public TabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Disable the Scroll Bar
setHorizontalScrollBarEnabled(false);
// Make sure that the Tab Strips fills this View
setFillViewport(true);
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
mTabStrip = new TabStrip(context);
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
/**
* Set the custom {@link TabColorizer} to be used.
*
* If you only require simple customisation then you can use
* {@link #setSelectedIndicatorColors(int...)} to achieve similar effects.
*/
public void setCustomTabColorizer(TabColorizer tabColorizer) {
mTabStrip.setCustomTabColorizer(tabColorizer);
}
public void setDistributeEvenly(boolean distributeEvenly) {
mDistributeEvenly = distributeEvenly;
}
/**
* Sets the colors to be used for indicating the selected tab. These colors
* are treated as a circular array. Providing one color will mean that all
* tabs are indicated with the same color.
*/
public void setSelectedIndicatorColors(int... colors) {
mTabStrip.setSelectedIndicatorColors(colors);
}
/**
* Set the {@link ViewPager.OnPageChangeListener}. When using
* {@link TabLayout} you are required to set any
* {@link ViewPager.OnPageChangeListener} through this method. This is so
* that the layout can update it's scroll position correctly.
*
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
*/
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
mViewPagerPageChangeListener = listener;
}
/**
* Sets the associated view pager. Note that the assumption here is that the
* pager content (number of tabs and tab titles) does not change after this
* call has been made.
*/
public void setViewPager(ViewPager viewPager) {
this.setItems(null, viewPager);
}
public void setItems(TabItemSpec[] items, ViewPager viewPager) {
mTabStrip.removeAllViews();
mViewPager = viewPager;
mTabItems = items;
if (viewPager != null) {
viewPager.addOnPageChangeListener(new InternalViewPagerListener());
populateTabStrip();
}
}
/**
* Updates the UI of an item at specified index
*/
public void updateItemAt(int position, TabItemSpec tabItem) {
LinearLayout ll = (LinearLayout)mTabStrip.getChildAt(position);
ImageView imgView = (ImageView)ll.getChildAt(0);
TextView textView = (TextView)ll.getChildAt(1);
this.setupItem(ll, textView, imgView, tabItem);
}
/**
* Gets the TextView for tab item at index
*/
public TextView getTextViewForItemAt(int index){
LinearLayout ll = this.getViewForItemAt(index);
return (ll != null) ? (TextView)ll.getChildAt(1) : null;
}
/**
* Gets the LinearLayout container for tab item at index
*/
public LinearLayout getViewForItemAt(int index){
LinearLayout result = null;
if(this.mTabStrip.getChildCount() > index){
result = (LinearLayout)this.mTabStrip.getChildAt(index);
}
return result;
}
/**
* Create a default view to be used for tabs.
*/
protected View createDefaultTabView(Context context, TabItemSpec tabItem) {
float density = getResources().getDisplayMetrics().density;
int padding = (int) (TAB_VIEW_PADDING_DIPS * density);
LinearLayout ll = new LinearLayout(context);
ll.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
ll.setGravity(Gravity.CENTER);
ll.setOrientation(LinearLayout.VERTICAL);
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
ll.setBackgroundResource(outValue.resourceId);
ImageView imgView = new ImageView(context);
imgView.setScaleType(ScaleType.FIT_CENTER);
LinearLayout.LayoutParams imgLP = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
imgLP.gravity = Gravity.CENTER;
imgView.setLayoutParams(imgLP);
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setMaxWidth((int) (TEXT_MAX_WIDHT * density));
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setAllCaps(true);
textView.setMaxLines(2);
textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setPadding(padding, 0, padding, 0);
this.setupItem(ll, textView, imgView, tabItem);
ll.addView(imgView);
ll.addView(textView);
return ll;
}
private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, TabItemSpec tabItem){
float density = getResources().getDisplayMetrics().density;
if (tabItem.iconId != 0) {
imgView.setImageResource(tabItem.iconId);
imgView.setVisibility(VISIBLE);
} else if (tabItem.iconDrawable != null) {
imgView.setImageDrawable(tabItem.iconDrawable);
imgView.setVisibility(VISIBLE);
} else {
imgView.setVisibility(GONE);
}
if (tabItem.title != null && !tabItem.title.isEmpty()) {
textView.setText(tabItem.title);
textView.setVisibility(VISIBLE);
} else {
textView.setVisibility(GONE);
}
if (imgView.getVisibility() == VISIBLE && textView.getVisibility() == VISIBLE) {
ll.setMinimumHeight((int) (LARGE_MIN_HEIGHT * density));
} else {
ll.setMinimumHeight((int) (SMALL_MIN_HEIGHT * density));
}
if (mDistributeEvenly) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
lp.width = 0;
lp.weight = 1;
}
}
private void populateTabStrip() {
final PagerAdapter adapter = mViewPager.getAdapter();
final View.OnClickListener tabClickListener = new TabClickListener();
for (int i = 0; i < adapter.getCount(); i++) {
View tabView = null;
TabItemSpec tabItem;
if (this.mTabItems != null && this.mTabItems.length > i) {
tabItem = this.mTabItems[i];
} else {
tabItem = new TabItemSpec();
tabItem.title = adapter.getPageTitle(i).toString();
}
tabView = createDefaultTabView(getContext(), tabItem);
tabView.setOnClickListener(tabClickListener);
String desc = mContentDescriptions.get(i, null);
if (desc != null) {
tabView.setContentDescription(desc);
}
mTabStrip.addView(tabView);
if (i == mViewPager.getCurrentItem()) {
tabView.setSelected(true);
}
}
}
public void setContentDescription(int i, String desc) {
mContentDescriptions.put(i, desc);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mViewPager != null) {
scrollToTab(mViewPager.getCurrentItem(), 0);
}
}
private void scrollToTab(int tabIndex, int positionOffset) {
final int tabStripChildCount = mTabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
View selectedChild = mTabStrip.getChildAt(tabIndex);
if (selectedChild != null) {
int targetScrollX = selectedChild.getLeft() + positionOffset;
if (tabIndex > 0 || positionOffset > 0) {
// If we're not at the first child and are mid-scroll, make sure
// we obey the offset
targetScrollX -= mTitleOffset;
}
scrollTo(targetScrollX, 0);
}
}
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
private int mScrollState;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int tabStripChildCount = mTabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
return;
}
mTabStrip.onViewPagerPageChanged(position, positionOffset);
View selectedTitle = mTabStrip.getChildAt(position);
int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0;
scrollToTab(position, extraOffset);
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageSelected(int position) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
mTabStrip.onViewPagerPageChanged(position, 0f);
scrollToTab(position, 0);
}
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
mTabStrip.getChildAt(i).setSelected(position == i);
}
if (mViewPagerPageChangeListener != null) {
mViewPagerPageChangeListener.onPageSelected(position);
}
}
}
private class TabClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
if (v == mTabStrip.getChildAt(i)) {
mViewPager.setCurrentItem(i);
return;
}
}
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nativescript.widgets;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
class TabStrip extends LinearLayout {
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
private final int mBottomBorderThickness;
private final Paint mBottomBorderPaint;
private final int mSelectedIndicatorThickness;
private final Paint mSelectedIndicatorPaint;
private final int mDefaultBottomBorderColor;
private int mSelectedPosition;
private float mSelectionOffset;
private TabLayout.TabColorizer mCustomTabColorizer;
private final SimpleTabColorizer mDefaultTabColorizer;
TabStrip(Context context) {
this(context, null);
}
TabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
final float density = getResources().getDisplayMetrics().density;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
final int themeForegroundColor = outValue.data;
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
mDefaultTabColorizer = new SimpleTabColorizer();
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
mBottomBorderPaint = new Paint();
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
mSelectedIndicatorPaint = new Paint();
}
void setCustomTabColorizer(TabLayout.TabColorizer customTabColorizer) {
mCustomTabColorizer = customTabColorizer;
invalidate();
}
void setSelectedIndicatorColors(int... colors) {
// Make sure that the custom colorizer is removed
mCustomTabColorizer = null;
mDefaultTabColorizer.setIndicatorColors(colors);
invalidate();
}
void onViewPagerPageChanged(int position, float positionOffset) {
mSelectedPosition = position;
mSelectionOffset = positionOffset;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
final int height = getHeight();
final int childCount = getChildCount();
final TabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
? mCustomTabColorizer
: mDefaultTabColorizer;
// Thick colored underline below the current selection
if (childCount > 0) {
View selectedTitle = getChildAt(mSelectedPosition);
int left = selectedTitle.getLeft();
int right = selectedTitle.getRight();
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
if (color != nextColor) {
color = blendColors(nextColor, color, mSelectionOffset);
}
// Draw the selection partway between the tabs
View nextTitle = getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
}
mSelectedIndicatorPaint.setColor(color);
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
height, mSelectedIndicatorPaint);
}
// Thin underline along the entire bottom edge
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
}
/**
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
*/
private static int setColorAlpha(int color, byte alpha) {
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
}
/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
* 0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
private static class SimpleTabColorizer implements TabLayout.TabColorizer {
private int[] mIndicatorColors;
@Override
public final int getIndicatorColor(int position) {
return mIndicatorColors[position % mIndicatorColors.length];
}
void setIndicatorColors(int... colors) {
mIndicatorColors = colors;
}
}
}

View File

@@ -0,0 +1,188 @@
/**
*
*/
package org.nativescript.widgets;
import org.nativescript.widgets.HorizontalScrollView.SavedState;
import android.content.Context;
import android.graphics.Rect;
import android.os.Parcelable;
import android.view.View;
import android.widget.ScrollView;
/**
* @author hhristov
*
*/
public class VerticalScrollView extends ScrollView {
private final Rect mTempRect = new Rect();
private int contentMeasuredWidth = 0;
private int contentMeasuredHeight = 0;
private int scrollableLength = 0;
private SavedState mSavedState;
private boolean isFirstLayout = true;
/**
* True when the layout has changed but the traversal has not come through yet.
* Ideally the view hierarchy would keep track of this for us.
*/
private boolean mIsLayoutDirty = true;
/**
* The child to give focus to in the event that a child has requested focus while the
* layout is dirty. This prevents the scroll from being wrong if the child has not been
* laid out before requesting focus.
*/
private View mChildToScrollTo = null;
public VerticalScrollView(Context context) {
super(context);
}
public int getScrollableLength() {
return this.scrollableLength;
}
@Override
public void requestLayout() {
this.mIsLayoutDirty = true;
super.requestLayout();
}
@Override
public void requestChildFocus(View child, View focused) {
if (!this.mIsLayoutDirty) {
this.scrollToChild(focused);
}
else {
// The child may not be laid out yet, we can't compute the scroll yet
this.mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Don't call measure because it will measure content twice.
// ScrollView is expected to have single child so we measure only the first child.
View child = this.getChildCount() > 0 ? this.getChildAt(0) : null;
if (child == null) {
this.scrollableLength = 0;
this.contentMeasuredWidth = 0;
this.contentMeasuredHeight = 0;
this.setPadding(0, 0, 0, 0);
}
else {
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
this.contentMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
this.contentMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
// Android ScrollView does not account to child margins so we set them as paddings. Otherwise you can never scroll to bottom.
CommonLayoutParams lp = (CommonLayoutParams)child.getLayoutParams();
this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
}
// Don't add in our paddings because they are already added as child margins. (we will include them twice if we add them).
// check the previous line - this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
// this.contentMeasuredWidth += this.getPaddingLeft() + this.getPaddingRight();
// this.contentMeasuredHeight += this.getPaddingTop() + this.getPaddingBottom();
// Check against our minimum height
this.contentMeasuredWidth = Math.max(this.contentMeasuredWidth, this.getSuggestedMinimumWidth());
this.contentMeasuredHeight = Math.max(this.contentMeasuredHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(this.contentMeasuredWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(this.contentMeasuredHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childHeight = 0;
if (this.getChildCount() > 0) {
View child = this.getChildAt(0);
childHeight = child.getMeasuredHeight();
int width = right - left;
int height = bottom - top;
this.scrollableLength = this.contentMeasuredHeight - height;
CommonLayoutParams.layoutChild(child, 0, 0, width, Math.max(this.contentMeasuredHeight, height));
this.scrollableLength = Math.max(0, this.scrollableLength);
}
this.mIsLayoutDirty = false;
// Give a child focus if it needs it
if (this.mChildToScrollTo != null && HorizontalScrollView.isViewDescendantOf(this.mChildToScrollTo, this)) {
this.scrollToChild(this.mChildToScrollTo);
}
this.mChildToScrollTo = null;
int scrollX = this.getScrollX();
int scrollY = this.getScrollY();
if (this.isFirstLayout) {
this.isFirstLayout = false;
final int scrollRange = Math.max(0, childHeight - (bottom - top - this.getPaddingTop() - this.getPaddingBottom()));
if (this.mSavedState != null) {
scrollY = mSavedState.scrollPosition;
mSavedState = null;
}
// Don't forget to clamp
if (scrollY > scrollRange) {
scrollY = scrollRange;
} else if (scrollY < 0) {
scrollY = 0;
}
}
// Calling this with the present values causes it to re-claim them
this.scrollTo(scrollX, scrollY);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
this.isFirstLayout = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.isFirstLayout = true;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
this.mSavedState = ss;
this.requestLayout();
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.scrollPosition = this.getScrollY();
return ss;
}
private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
/* Offset from child's local coordinates to ScrollView coordinates */
offsetDescendantRectToMyCoords(child, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
if (scrollDelta != 0) {
this.scrollBy(scrollDelta, 0);
}
}
}

View File

@@ -0,0 +1,237 @@
/**
*
*/
package org.nativescript.widgets;
import java.util.ArrayList;
import android.content.Context;
import android.view.View;
/**
* @author hhristov
*
*/
public class WrapLayout extends LayoutBase {
private int _itemWidth = -1;
private int _itemHeight = -1;
private Orientation _orientation = Orientation.horzontal;
private ArrayList<Integer> _lengths = new ArrayList<Integer>();
public WrapLayout(Context context) {
super(context);
}
public Orientation getOrientation() {
return this._orientation;
}
public void setOrientation(Orientation value) {
this._orientation = value;
this.requestLayout();
}
public int getItemWidth() {
return this._itemWidth;
}
public void setItemWidth(int value) {
this._itemWidth = value;
this.requestLayout();
}
public int getItemHeight() {
return this._itemHeight;
}
public void setItemHeight(int value) {
this._itemHeight = value;
this.requestLayout();
}
private static int getViewMeasureSpec(int parentMode, int parentLength, int itemLength) {
if (itemLength > 0) {
return MeasureSpec.makeMeasureSpec(itemLength, MeasureSpec.EXACTLY);
}
else if (parentMode == MeasureSpec.UNSPECIFIED) {
return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
else {
return MeasureSpec.makeMeasureSpec(parentLength, MeasureSpec.AT_MOST);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth = 0;
int measureHeight = 0;
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean isVertical = this._orientation == Orientation.vertical;
int verticalPadding = this.getPaddingTop() + this.getPaddingBottom();
int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight();
int childWidthMeasureSpec = getViewMeasureSpec(widthMode, width, this._itemWidth);
int childHeightMeasureSpec = getViewMeasureSpec(heightMode, height, this._itemHeight);
int remainingWidth = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : width - horizontalPadding;
int remainingHeight = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : height - verticalPadding;
int count = this.getChildCount();
this._lengths.clear();
int rowOrColumn = 0;
int maxLength = 0;
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
CommonLayoutParams.updateChildLayoutParams(child, widthMeasureSpec, heightMeasureSpec);
CommonLayoutParams.measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec);
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
if (isVertical) {
if (childMeasuredHeight > remainingHeight) {
rowOrColumn++;
maxLength = Math.max(maxLength, measureHeight);
measureHeight = childMeasuredHeight;
remainingWidth = height - childMeasuredHeight;
this._lengths.add(rowOrColumn, childMeasuredWidth);
}
else {
remainingHeight -= childMeasuredHeight;
measureHeight += childMeasuredHeight;
}
}
else {
if (childMeasuredWidth > remainingWidth) {
rowOrColumn++;
maxLength = Math.max(maxLength, measureWidth);
measureWidth = childMeasuredWidth;
remainingWidth = width - childMeasuredWidth;
this._lengths.add(rowOrColumn, childMeasuredHeight);
}
else {
remainingWidth -= childMeasuredWidth;
measureWidth += childMeasuredWidth;
}
}
if(this._lengths.size() <= rowOrColumn) {
this._lengths.add(rowOrColumn, isVertical ? childMeasuredWidth : childMeasuredHeight);
}
else {
this._lengths.set(rowOrColumn, Math.max(this._lengths.get(rowOrColumn), isVertical ? childMeasuredWidth : childMeasuredHeight));
}
}
count = this._lengths.size();
if (isVertical) {
measureHeight = Math.max(maxLength, measureHeight);
for (int i = 0; i < count; i++) {
measureWidth += this._lengths.get(i);
}
}
else {
measureWidth = Math.max(maxLength, measureWidth);
for (int i = 0; i < count; i++) {
measureHeight += this._lengths.get(i);
}
}
// Add in our padding
measureWidth += horizontalPadding;
measureHeight += verticalPadding;
// Check against our minimum sizes
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
boolean isVertical = this._orientation == Orientation.vertical;
int paddingLeft = this.getPaddingLeft();
int paddingRight = this.getPaddingRight();
int paddingTop = this.getPaddingTop();
int paddingBottom = this.getPaddingBottom();
int childLeft = paddingLeft;
int childTop = paddingTop;
int childrenLength = isVertical ? bottom - top - paddingBottom : right - left - paddingRight;
int rowOrColumn = 0;
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
// Add margins because layoutChild will subtract them.
int childWidth = CommonLayoutParams.getDesiredWidth(child);
int childHeight = CommonLayoutParams.getDesiredHeight(child);
int length = this._lengths.get(rowOrColumn);
if (isVertical) {
childWidth = length;
childHeight = this._itemHeight > 0 ? this._itemHeight : childHeight;
if (childTop + childHeight > childrenLength) {
// Move to top.
childTop = paddingTop;
// Move to right with current column width.
childLeft += length;
// Move to next column.
rowOrColumn++;
// Take current column width.
childWidth = length = this._lengths.get(rowOrColumn);
}
}
else {
childWidth = this._itemWidth > 0 ? this._itemWidth : childWidth;
childHeight = length;
if (childLeft + childWidth > childrenLength) {
// Move to left.
childLeft = paddingLeft;
// Move to bottom with current row height.
childTop += length;
// Move to next column.
rowOrColumn++;
// Take current row height.
childHeight = length = this._lengths.get(rowOrColumn);
}
}
CommonLayoutParams.layoutChild(child, childLeft, childTop, childLeft + childWidth, childTop + childHeight);
if (isVertical) {
// Move next child Top position to bottom.
childTop += childHeight;
}
else {
// Move next child Left position to right.
childLeft += childWidth;
}
}
}
}

View File