mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
NativeScript layouts implemented in Java
This commit is contained in:
9
.classpath
Normal file
9
.classpath
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/.metadata/
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
local.properties
|
||||
|
||||
gen/
|
||||
armeabi-v7a/
|
||||
x86/
|
||||
obj/
|
||||
bin/
|
||||
.svn/
|
||||
.settings/
|
||||
33
.project
Normal file
33
.project
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>widgets</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
14
AndroidManifest.xml
Normal file
14
AndroidManifest.xml
Normal 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>
|
||||
3
lint.xml
Normal file
3
lint.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
</lint>
|
||||
20
proguard-project.txt
Normal file
20
proguard-project.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
15
project.properties
Normal file
15
project.properties
Normal file
@@ -0,0 +1,15 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-17
|
||||
android.library=true
|
||||
81
src/org/nativescript/widgets/AbsoluteLayout.java
Normal file
81
src/org/nativescript/widgets/AbsoluteLayout.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
251
src/org/nativescript/widgets/CommonLayoutParams.java
Normal file
251
src/org/nativescript/widgets/CommonLayoutParams.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 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);
|
||||
|
||||
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 we are not initialized.
|
||||
if(debuggable < 0) {
|
||||
try {
|
||||
Context context = child.getContext();
|
||||
int flags = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.flags;
|
||||
debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ? 1 : 0;
|
||||
}
|
||||
catch (NameNotFoundException e) {
|
||||
debuggable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (debuggable > 0) {
|
||||
sb.setLength(0);
|
||||
sb.append(child.getParent().toString());
|
||||
sb.append(" :measureChild: ");
|
||||
sb.append(child.toString());
|
||||
sb.append(" ");
|
||||
sb.append(MeasureSpec.toString(widthMeasureSpec));
|
||||
sb.append(", ");
|
||||
sb.append(MeasureSpec.toString(heightMeasureSpec));
|
||||
log(tag, sb.toString());
|
||||
}
|
||||
|
||||
int childWidthMeasureSpec = getMeasureSpec(child, widthMeasureSpec, true);
|
||||
int childHeightMeasureSpec = getMeasureSpec(child, heightMeasureSpec, false);
|
||||
|
||||
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
78
src/org/nativescript/widgets/ContentLayout.java
Normal file
78
src/org/nativescript/widgets/ContentLayout.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/org/nativescript/widgets/Dock.java
Normal file
15
src/org/nativescript/widgets/Dock.java
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.nativescript.widgets;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*
|
||||
*/
|
||||
public enum Dock {
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom
|
||||
}
|
||||
176
src/org/nativescript/widgets/DockLayout.java
Normal file
176
src/org/nativescript/widgets/DockLayout.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
1158
src/org/nativescript/widgets/GridLayout.java
Normal file
1158
src/org/nativescript/widgets/GridLayout.java
Normal file
File diff suppressed because it is too large
Load Diff
14
src/org/nativescript/widgets/GridUnitType.java
Normal file
14
src/org/nativescript/widgets/GridUnitType.java
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.nativescript.widgets;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*
|
||||
*/
|
||||
public enum GridUnitType {
|
||||
auto,
|
||||
pixel,
|
||||
star
|
||||
}
|
||||
247
src/org/nativescript/widgets/HorizontalScrollView.java
Normal file
247
src/org/nativescript/widgets/HorizontalScrollView.java
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.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).
|
||||
// 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
125
src/org/nativescript/widgets/ImageView.java
Normal file
125
src/org/nativescript/widgets/ImageView.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.nativescript.widgets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*
|
||||
*/
|
||||
public class ImageView extends android.widget.ImageView {
|
||||
|
||||
private double scaleW = 1;
|
||||
private double scaleH = 1;
|
||||
|
||||
public ImageView(Context context) {
|
||||
super(context);
|
||||
this.setScaleType(ScaleType.FIT_CENTER);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/org/nativescript/widgets/ItemSpec.java
Normal file
60
src/org/nativescript/widgets/ItemSpec.java
Normal 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;
|
||||
}
|
||||
}
|
||||
68
src/org/nativescript/widgets/LayoutBase.java
Normal file
68
src/org/nativescript/widgets/LayoutBase.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
13
src/org/nativescript/widgets/Orientation.java
Normal file
13
src/org/nativescript/widgets/Orientation.java
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.nativescript.widgets;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*
|
||||
*/
|
||||
public enum Orientation {
|
||||
horzontal,
|
||||
vertical
|
||||
}
|
||||
217
src/org/nativescript/widgets/StackLayout.java
Normal file
217
src/org/nativescript/widgets/StackLayout.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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), heightMeasureSpec);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
186
src/org/nativescript/widgets/VerticalScrollView.java
Normal file
186
src/org/nativescript/widgets/VerticalScrollView.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.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).
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
236
src/org/nativescript/widgets/WrapLayout.java
Normal file
236
src/org/nativescript/widgets/WrapLayout.java
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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> _lenghts = 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._lenghts.clear();
|
||||
int rowOrColumn = 0;
|
||||
int maxLenght = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
View child = this.getChildAt(i);
|
||||
if (child.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CommonLayoutParams.measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec);
|
||||
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
|
||||
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
|
||||
|
||||
if (isVertical) {
|
||||
if (childMeasuredHeight > remainingHeight) {
|
||||
rowOrColumn++;
|
||||
maxLenght = Math.max(maxLenght, measureHeight);
|
||||
measureHeight = childMeasuredHeight;
|
||||
remainingWidth = height - childMeasuredHeight;
|
||||
this._lenghts.add(rowOrColumn, childMeasuredWidth);
|
||||
}
|
||||
else {
|
||||
remainingHeight -= childMeasuredHeight;
|
||||
measureHeight += childMeasuredHeight;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (childMeasuredWidth > remainingWidth) {
|
||||
rowOrColumn++;
|
||||
maxLenght = Math.max(maxLenght, measureWidth);
|
||||
measureWidth = childMeasuredWidth;
|
||||
remainingWidth = width - childMeasuredWidth;
|
||||
this._lenghts.add(rowOrColumn, childMeasuredHeight);
|
||||
}
|
||||
else {
|
||||
remainingWidth -= childMeasuredWidth;
|
||||
measureWidth += childMeasuredWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if(this._lenghts.size() <= rowOrColumn) {
|
||||
this._lenghts.add(rowOrColumn, isVertical ? childMeasuredWidth : childMeasuredHeight);
|
||||
}
|
||||
else {
|
||||
this._lenghts.set(rowOrColumn, Math.max(this._lenghts.get(rowOrColumn), isVertical ? childMeasuredWidth : childMeasuredHeight));
|
||||
}
|
||||
}
|
||||
|
||||
count = this._lenghts.size();
|
||||
if (isVertical) {
|
||||
measureHeight = Math.max(maxLenght, measureHeight);
|
||||
for (int i = 0; i < count; i++) {
|
||||
measureWidth += this._lenghts.get(i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
measureWidth = Math.max(maxLenght, measureWidth);
|
||||
for (int i = 0; i < count; i++) {
|
||||
measureHeight += this._lenghts.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 - (paddingRight + paddingBottom) : right - left - (paddingLeft + 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._lenghts.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._lenghts.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._lenghts.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user