tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 501HTML 254C 19JavaScript 81412 Definiciones

Load of small fixes, updates API, and added tests

310fb3e6e8df9266056b8997c1625231da1f4719 · por Emil Sjölander · (hace 7 años) · padre 478499a01 · ver el árbol en este commit

Delta semántico · 30

bindings/kotlin/stretch/src/main/rust/src/lib.rs

src/node.rs

tests/measure.rs

tests/node.rs

46 archivos · +523 −382

diff --git a/.circleci/config.yml b/.circleci/config.yml
index cf5487e91..0e5050e4f 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,7 @@
 version: 2
 
 jobs:
-  build:
+  core:
     docker:
       - image: rust:1
 
@@ -23,4 +23,100 @@ jobs:
           key: target-cache
           paths:
             - ~/.cargo
-            - ./target
\ No newline at end of file
+            - ./target
+
+  android:
+    docker:
+      - image: circleci/android:api-28
+    
+    environment:
+      _JAVA_OPTIONS: "-Xmx3200m -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=2 -Djava.util.concurrent.ForkJoinPool.common.parallelism=2"
+
+    steps:
+      - checkout
+      - restore_cache:
+          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "stretch/build.gradle" }}
+      - run: yes | sdkmanager --update || true
+      - run: cd android && ./gradlew androidDependencies --no-daemon --max-workers 2
+      - save_cache:
+          paths:
+            - ~/.gradle
+          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "stretch/build.gradle" }}
+      - run:
+          name: Setup emulator
+          command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a"
+      - run:
+          name: Launch emulator
+          command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel auto -verbose
+          background: true
+      - run:
+          name: Wait emulator
+          command: |
+            # wait for it to have booted
+            circle-android wait-for-boot
+            # unlock the emulator screen
+            sleep 30
+            adb shell input keyevent 82
+      - run:
+          name: Run tests
+          command: ./gradlew connectedAndroidTest
+      - store_artifacts:
+          path: stretch/build/reports
+          destination: reports
+      - store_test_results:
+          path: stretch/build/test-results
+
+workflows:
+  version: 2
+  test_all:
+    jobs:
+      - android
+      - core
+
+
+version: 2
+jobs:
+  build:
+    working_directory: ~/code
+    docker:
+      # Android the primary container
+      - image: circleci/android:api-26-alpha
+    environment:
+      JVM_OPTS: -Xmx3200m
+    steps:
+      - checkout
+      - restore_cache:
+          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
+      - run:
+          name: Download Dependencies
+          command: ./gradlew androidDependencies
+      - save_cache:
+          paths:
+            - ~/.gradle
+          key: jars-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
+      - run:
+          name: Run UnitTest
+          command: ./gradlew testDemoDebugUnitTest
+      - run:
+          name: Setup emulator
+          command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a"
+      - run:
+          name: Launch emulator
+          command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel auto -verbose
+          background: true
+      - run:
+          name: Wait emulator
+          command: |
+            # wait for it to have booted
+            circle-android wait-for-boot
+            # unlock the emulator screen
+            sleep 30
+            adb shell input keyevent 82
+      - run:
+          name: Run EspressoTests
+          command: ./gradlew connectedDemoDebugAndroidTest
+      - store_artifacts:
+          path: app/build/reports
+          destination: reports
+      - store_test_results:
+          path: app/build/test-results
diff --git a/Cargo.toml b/Cargo.toml
index 7041f4984..abdddc80e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "stretch"
-version = "0.2.1"
+version = "0.2.2"
 authors = ["Emil Sjölander <emil@visly.app>"]
 edition = "2018"
 include = ["src/**/*", "Cargo.toml"]
diff --git a/bindings/kotlin/.idea/codeStyles/Project.xml b/bindings/kotlin/.idea/codeStyles/Project.xml
index 34dc27cb6..1bec35e57 100644
--- a/bindings/kotlin/.idea/codeStyles/Project.xml
+++ b/bindings/kotlin/.idea/codeStyles/Project.xml
@@ -3,31 +3,6 @@
     <JetCodeStyleSettings>
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </JetCodeStyleSettings>
-    <Objective-C-extensions>
-      <file>
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
-      </file>
-      <class>
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
-      </class>
-      <extensions>
-        <pair source="cpp" header="h" fileNamingConvention="NONE" />
-        <pair source="c" header="h" fileNamingConvention="NONE" />
-      </extensions>
-    </Objective-C-extensions>
     <codeStyleSettings language="kotlin">
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </codeStyleSettings>
diff --git a/bindings/kotlin/.idea/encodings.xml b/bindings/kotlin/.idea/encodings.xml
new file mode 100644
index 000000000..15a15b218
--- /dev/null
+++ b/bindings/kotlin/.idea/encodings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" addBOMForNewFiles="with NO BOM" />
+</project>
\ No newline at end of file
diff --git a/bindings/kotlin/.idea/gradle.xml b/bindings/kotlin/.idea/gradle.xml
index 5f07c20d7..8ec153ca0 100644
--- a/bindings/kotlin/.idea/gradle.xml
+++ b/bindings/kotlin/.idea/gradle.xml
@@ -8,7 +8,6 @@
         <option name="modules">
           <set>
             <option value="$PROJECT_DIR$" />
-            <option value="$PROJECT_DIR$/sample" />
             <option value="$PROJECT_DIR$/stretch" />
           </set>
         </option>
diff --git a/bindings/kotlin/build.gradle b/bindings/kotlin/build.gradle
index a6b2983d1..af2e4069c 100644
--- a/bindings/kotlin/build.gradle
+++ b/bindings/kotlin/build.gradle
@@ -8,7 +8,7 @@ buildscript {
         
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.3.2'
+        classpath 'com.android.tools.build:gradle:3.4.0'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/bindings/kotlin/gradle/wrapper/gradle-wrapper.properties b/bindings/kotlin/gradle/wrapper/gradle-wrapper.properties
index 253fb2e29..3e48a3deb 100644
--- a/bindings/kotlin/gradle/wrapper/gradle-wrapper.properties
+++ b/bindings/kotlin/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Apr 08 13:50:07 BST 2019
+#Tue Apr 23 11:26:41 BST 2019
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/bindings/kotlin/sample/.gitignore b/bindings/kotlin/sample/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/bindings/kotlin/sample/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/bindings/kotlin/sample/build.gradle b/bindings/kotlin/sample/build.gradle
deleted file mode 100644
index 4881844e0..000000000
--- a/bindings/kotlin/sample/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-apply plugin: 'com.android.application'
-
-apply plugin: 'kotlin-android'
-
-apply plugin: 'kotlin-android-extensions'
-
-android {
-    compileSdkVersion 28
-    defaultConfig {
-        applicationId "app.visly.stretch.sample"
-        minSdkVersion 15
-        targetSdkVersion 28
-        versionCode 1
-        versionName "1.0"
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-        }
-    }
-}
-
-dependencies {
-    implementation fileTree(dir: 'libs', include: ['*.jar'])
-    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    implementation 'com.android.support:appcompat-v7:28.0.0'
-    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
-    implementation project(path: ':stretch')
-}
diff --git a/bindings/kotlin/sample/proguard-rules.pro b/bindings/kotlin/sample/proguard-rules.pro
deleted file mode 100644
index f1b424510..000000000
--- a/bindings/kotlin/sample/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# 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 *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/bindings/kotlin/sample/src/main/AndroidManifest.xml b/bindings/kotlin/sample/src/main/AndroidManifest.xml
deleted file mode 100644
index b9bdbe240..000000000
--- a/bindings/kotlin/sample/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="app.visly.stretch.sample">
-
-    <application
-            android:allowBackup="true"
-            android:icon="@mipmap/ic_launcher"
-            android:label="@string/app_name"
-            android:roundIcon="@mipmap/ic_launcher_round"
-            android:supportsRtl="true"
-            android:theme="@style/AppTheme">
-        <activity android:name=".MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-
-                <category android:name="android.intent.category.LAUNCHER"/>
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
\ No newline at end of file
diff --git a/bindings/kotlin/sample/src/main/java/app/visly/stretch/sample/MainActivity.kt b/bindings/kotlin/sample/src/main/java/app/visly/stretch/sample/MainActivity.kt
deleted file mode 100644
index 9ca654433..000000000
--- a/bindings/kotlin/sample/src/main/java/app/visly/stretch/sample/MainActivity.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package app.visly.stretch.sample
-
-import android.support.v7.app.AppCompatActivity
-import android.os.Bundle
-import android.util.Log
-import app.visly.stretch.*
-
-class MainActivity : AppCompatActivity() {
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_main)
-
-        val child1 = Node(Style(size = Size(Dimension.Points(100f), Dimension.Points(100f))), listOf())
-        val child2 = Node(Style(size = Size(Dimension.Points(100f), Dimension.Points(100f))), listOf())
-        val child3 = Node(Style(size = Size(Dimension.Points(100f), Dimension.Points(100f))), listOf())
-
-        val node = Node(
-            Style(
-                flexDirection = FlexDirection.Column,
-                size = Size(
-                    width = Dimension.Points(100f),
-                    height = Dimension.Undefined
-                )
-            ),
-            listOf(child1, child2, child3)
-        )
-
-        val layout = node.computeLayout(Size(null, null))
-
-        Log.d("visly-debug-log", "" + layout)
-    }
-}
diff --git a/bindings/kotlin/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/bindings/kotlin/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index 6348baae3..000000000
--- a/bindings/kotlin/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:aapt="http://schemas.android.com/aapt"
-        android:width="108dp"
-        android:height="108dp"
-        android:viewportHeight="108"
-        android:viewportWidth="108">
-    <path
-            android:fillType="evenOdd"
-            android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
-            android:strokeColor="#00000000"
-            android:strokeWidth="1">
-        <aapt:attr name="android:fillColor">
-            <gradient
-                    android:endX="78.5885"
-                    android:endY="90.9159"
-                    android:startX="48.7653"
-                    android:startY="61.0927"
-                    android:type="linear">
-                <item
-                        android:color="#44000000"
-                        android:offset="0.0"/>
-                <item
-                        android:color="#00000000"
-                        android:offset="1.0"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path
-            android:fillColor="#FFFFFF"
-            android:fillType="nonZero"
-            android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
-            android:strokeColor="#00000000"
-            android:strokeWidth="1"/>
-</vector>
diff --git a/bindings/kotlin/sample/src/main/res/drawable/ic_launcher_background.xml b/bindings/kotlin/sample/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index a0ad202f9..000000000
--- a/bindings/kotlin/sample/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:height="108dp"
-        android:width="108dp"
-        android:viewportHeight="108"
-        android:viewportWidth="108">
-    <path android:fillColor="#008577"
-          android:pathData="M0,0h108v108h-108z"/>
-    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
-          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
-</vector>
diff --git a/bindings/kotlin/sample/src/main/res/layout/activity_main.xml b/bindings/kotlin/sample/src/main/res/layout/activity_main.xml
deleted file mode 100644
index b87aade91..000000000
--- a/bindings/kotlin/sample/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        xmlns:app="http://schemas.android.com/apk/res-auto"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        tools:context=".sample.MainActivity">
-
-    <TextView
-            android:id="@+id/sample_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Hello World!"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintLeft_toLeftOf="parent"
-            app:layout_constraintRight_toRightOf="parent"
-            app:layout_constraintTop_toTopOf="parent"/>
-
-</android.support.constraint.ConstraintLayout>
\ No newline at end of file
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index bbd3e0212..000000000
--- a/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background"/>
-    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
-</adaptive-icon>
\ No newline at end of file
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index bbd3e0212..000000000
--- a/bindings/kotlin/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background"/>
-    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
-</adaptive-icon>
\ No newline at end of file
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 898f3ed59..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index dffca3601..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 64ba76f75..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index dae5e0823..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e5ed46597..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 14ed0af35..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index b0907cac3..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index d8ae03154..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 2c18de9e6..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index beed3cdd2..000000000
Binary files a/bindings/kotlin/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/bindings/kotlin/sample/src/main/res/values/colors.xml b/bindings/kotlin/sample/src/main/res/values/colors.xml
deleted file mode 100644
index 69b22338c..000000000
--- a/bindings/kotlin/sample/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="colorPrimary">#008577</color>
-    <color name="colorPrimaryDark">#00574B</color>
-    <color name="colorAccent">#D81B60</color>
-</resources>
diff --git a/bindings/kotlin/sample/src/main/res/values/strings.xml b/bindings/kotlin/sample/src/main/res/values/strings.xml
deleted file mode 100644
index 50ca77cff..000000000
--- a/bindings/kotlin/sample/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-    <string name="app_name">stretch</string>
-</resources>
diff --git a/bindings/kotlin/sample/src/main/res/values/styles.xml b/bindings/kotlin/sample/src/main/res/values/styles.xml
deleted file mode 100644
index 5885930df..000000000
--- a/bindings/kotlin/sample/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<resources>
-
-    <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
-        <!-- Customize your theme here. -->
-        <item name="colorPrimary">@color/colorPrimary</item>
-        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
-        <item name="colorAccent">@color/colorAccent</item>
-    </style>
-
-</resources>
diff --git a/bindings/kotlin/settings.gradle b/bindings/kotlin/settings.gradle
index 32450087d..7f07d77b1 100644
--- a/bindings/kotlin/settings.gradle
+++ b/bindings/kotlin/settings.gradle
@@ -1 +1 @@
-include ':sample', ':stretch'
+include ':stretch'
diff --git a/bindings/kotlin/stretch/build.gradle b/bindings/kotlin/stretch/build.gradle
index 5673f010c..1409021ce 100644
--- a/bindings/kotlin/stretch/build.gradle
+++ b/bindings/kotlin/stretch/build.gradle
@@ -11,7 +11,7 @@ android {
         versionCode 1
         versionName "1.0"
 
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 
     }
 
@@ -30,6 +30,20 @@ android {
     }
 }
 
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation "androidx.core:core:1.0.1"
+
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test:runner:1.1.1'
+    androidTestImplementation 'androidx.test:rules:1.1.1'
+    androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
+}
+
 def rustBasePath = "src/main/rust"
 def cargoOutputDir = "${rustBasePath}/target"
 def archTriplets = ['arm64-v8a': 'aarch64-linux-android',
@@ -65,9 +79,3 @@ archTriplets.each { arch, target ->
 
     clean.dependsOn "clean-${arch}"
 }
-repositories {
-    mavenCentral()
-}
-dependencies {
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
diff --git a/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/NodeTest.kt b/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/NodeTest.kt
new file mode 100644
index 000000000..fd90b6d6e
--- /dev/null
+++ b/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/NodeTest.kt
@@ -0,0 +1,101 @@
+package app.visly.stretch
+
+import org.hamcrest.CoreMatchers.*
+import org.hamcrest.MatcherAssert.*
+import org.junit.Test
+
+class NodeTest {
+
+    @Test
+    fun createNode() {
+        val child = Node(Style(), listOf())
+        val node = Node(Style(), listOf(child))
+        assertThat(node.getChildCount(), `is`(1))
+    }
+
+    @Test
+    fun createLeaf() {
+        val node = Node(Style(), object: MeasureFunc {
+            override fun measure(constraints: Size<Float?>): Size<Float> {
+                return Size(100f, 100f)
+            }
+        })
+
+        val layout = node.computeLayout(Size(null, null))
+        assertThat(layout.width, `is`(100f))
+        assertThat(layout.height, `is`(100f))
+    }
+
+    @Test
+    fun setMeasure() {
+        val node = Node(Style(), listOf())
+        node.setMeasure(object: MeasureFunc {
+            override fun measure(constraints: Size<Float?>): Size<Float> {
+                return Size(100f, 100f)
+            }
+        })
+
+        val layout = node.computeLayout(Size(null, null))
+        assertThat(layout.width, `is`(100f))
+        assertThat(layout.height, `is`(100f))
+    }
+
+    @Test
+    fun addChild() {
+        val node = Node(Style(), listOf())
+        val child = Node(Style(), listOf())
+        node.addChild(child)
+        assertThat(node.getChildCount(), `is`(1))
+    }
+
+    @Test
+    fun removeChild() {
+        val child = Node(Style(), listOf())
+        val node = Node(Style(), listOf(child))
+        node.removeChild(child)
+        assertThat(node.getChildCount(), `is`(0))
+    }
+
+    @Test
+    fun removeChildAtIndex() {
+        val child = Node(Style(), listOf())
+        val node = Node(Style(), listOf(child))
+        node.removeChildAtIndex(0)
+        assertThat(node.getChildCount(), `is`(0))
+    }
+
+    @Test
+    fun replaceChildAtIndex() {
+        val child1 = Node(Style(size = Size(Dimension.Points(100f), Dimension.Points(100f))), listOf())
+        val child2 = Node(Style(size = Size(Dimension.Points(200f), Dimension.Points(200f))), listOf())
+        val node = Node(Style(), listOf(child1))
+        node.replaceChildAtIndex(0, child2)
+
+        val layout = node.computeLayout(Size(null, null))
+        assertThat(layout.width, `is`(200f))
+        assertThat(layout.height, `is`(200f))
+    }
+
+    @Test
+    fun setStyle() {
+        val node = Node(Style(), listOf())
+        node.setStyle(Style())
+    }
+
+    @Test
+    fun setChildren() {
+        val node = Node(Style(), listOf())
+        val child = Node(Style(), listOf())
+        node.setChildren(listOf(child))
+        assertThat(node.getChildCount(), `is`(1))
+    }
+
+    @Test
+    fun markDirty() {
+        val node = Node(Style(), listOf())
+        node.computeLayout(Size(null, null))
+        assertThat(node.isDirty(), `is`(false))
+        node.markDirty()
+        assertThat(node.isDirty(), `is`(true))
+    }
+}
\ No newline at end of file
diff --git a/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/StretchTestSuite.kt b/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/StretchTestSuite.kt
new file mode 100644
index 000000000..055475676
--- /dev/null
+++ b/bindings/kotlin/stretch/src/androidTest/java/app/visly/stretch/StretchTestSuite.kt
@@ -0,0 +1,8 @@
+package app.visly.stretch
+
+import org.junit.runner.RunWith
+import org.junit.runners.Suite
+
+@RunWith(Suite::class)
+@Suite.SuiteClasses(NodeTest::class)
+class StretchTestSuite
\ No newline at end of file
diff --git a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Layout.kt b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Layout.kt
index fb6458f85..bb699452b 100644
--- a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Layout.kt
+++ b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Layout.kt
@@ -8,7 +8,7 @@ data class Layout(
     val children: List<Layout>) {
 
     companion object {
-        fun fromFloatArray(args: FloatArray, offset: Int): Pair<Int, Layout> {
+        internal fun fromFloatArray(args: FloatArray, offset: Int): Pair<Int, Layout> {
             var offset = offset
 
             val x = args[offset++]
diff --git a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Node.kt b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Node.kt
index 5dec50ac8..32494f754 100644
--- a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Node.kt
+++ b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Node.kt
@@ -1,8 +1,13 @@
 package app.visly.stretch
+import androidx.annotation.Keep
 
-private class MeasureFunc(val measure: (Size<Float?>) -> Size<Float>) {
-    fun measure(width: Float, height: Float): FloatArray {
-        val result = measure(Size(
+interface MeasureFunc {
+    fun measure(constraints: Size<Float?>): Size<Float>
+}
+
+private class MeasureFuncImpl(val measureFunc: MeasureFunc) {
+    @Keep fun measure(width: Float, height: Float): FloatArray {
+        val result = measureFunc.measure(Size(
             if (width.isNaN()) null else width,
             if (height.isNaN()) null else height
         ))
@@ -13,16 +18,16 @@ private class MeasureFunc(val measure: (Size<Float?>) -> Size<Float>) {
 class Node {
     companion object {
         init {
-            System.loadLibrary("stretch")
+            Stretch.init()
         }
     }
 
-    internal val rustptr: Long
+    private val rustptr: Long
     private var style: Style
     private var children: MutableList<Node>
 
-    constructor(style: Style, measure: (Size<Float?>) -> Size<Float>) {
-        this.rustptr = nConstructLeaf(style.rustptr, MeasureFunc(measure))
+    constructor(style: Style, measure: MeasureFunc) {
+        this.rustptr = nConstructLeaf(style.rustptr, MeasureFuncImpl(measure))
         this.style = style
         this.children = mutableListOf()
     }
@@ -37,6 +42,10 @@ class Node {
         nFree(rustptr)
     }
 
+    fun setMeasure(measure: MeasureFunc) {
+        nSetMeasure(rustptr, MeasureFuncImpl(measure))
+    }
+
     fun getChildren(): List<Node> {
         return this.children
     }
@@ -84,8 +93,8 @@ class Node {
         return nIsDirty(rustptr)
     }
 
-    fun childCount(): Int {
-        return nChildCount(rustptr)
+    fun getChildCount(): Int {
+        return children.size
     }
 
     fun computeLayout(size: Size<Float?>): Layout {
@@ -93,10 +102,10 @@ class Node {
         return result.second
     }
 
-    private external fun nFree(ptr: Long)
-    private external fun nChildCount(ptr: Long): Int
     private external fun nConstruct(style: Long, children: LongArray): Long
-    private external fun nConstructLeaf(style: Long, measure: MeasureFunc): Long
+    private external fun nConstructLeaf(style: Long, measure: MeasureFuncImpl): Long
+    private external fun nFree(ptr: Long)
+    private external fun nSetMeasure(ptr: Long, measure: MeasureFuncImpl)
     private external fun nSetChildren(ptr: Long, children: LongArray)
     private external fun nAddChild(ptr: Long, child: Long)
     private external fun nReplaceChildAtIndex(ptr: Long, index: Int, child: Long): Long
diff --git a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Stretch.kt b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Stretch.kt
new file mode 100644
index 000000000..149c6c82f
--- /dev/null
+++ b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Stretch.kt
@@ -0,0 +1,18 @@
+package app.visly.stretch
+
+// TODO
+// - Change measure to be a weak ref to node instance which in turn calls passed in measure function implementation from java.
+//   Problem with this is jni library currently does not support weak refs. This can be worked around by creating weak ref on java side.
+// - Integration tests to ensure bindings don't break
+
+class Stretch {
+    companion object {
+        private var didInit = false
+        internal fun init() {
+            if (!didInit) {
+                System.loadLibrary("stretch")
+                didInit = true
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Style.kt b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Style.kt
index 28b888997..54244fbbd 100644
--- a/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Style.kt
+++ b/bindings/kotlin/stretch/src/main/java/app/visly/stretch/Style.kt
@@ -93,10 +93,6 @@ sealed class Dimension {
         }
 }
 
-data class FloatSize(
-    val width: Float,
-    val height: Float)
-
 data class Size<T>(
     val width: T,
     val height: T)
@@ -132,7 +128,7 @@ data class Style(
 
     companion object {
         init {
-            System.loadLibrary("stretch")
+            Stretch.init()
         }
     }
 
@@ -289,4 +285,160 @@ data class Style(
 
         aspectRatio: Float
     ): Long
+
+    // This class exists for use with Java which does not have support for named / default arguments as in kotlin.
+    class Builder {
+        private var display: Display = Display.Flex
+        private var positionType: PositionType = PositionType.Relative
+        private var direction: Direction = Direction.Inherit
+        private var flexDirection: FlexDirection = FlexDirection.Row
+        private var flexWrap: FlexWrap = FlexWrap.NoWrap
+        private var overflow: Overflow = Overflow.Hidden
+        private var alignItems: AlignItems = AlignItems.Stretch
+        private var alignSelf: AlignSelf = AlignSelf.Auto
+        private var alignContent: AlignContent = AlignContent.FlexStart
+        private var justifyContent: JustifyContent = JustifyContent.FlexStart
+        private var position: Rect<Dimension> = Rect(Dimension.Undefined, Dimension.Undefined, Dimension.Undefined, Dimension.Undefined)
+        private var margin: Rect<Dimension> = Rect(Dimension.Undefined, Dimension.Undefined, Dimension.Undefined, Dimension.Undefined)
+        private var padding: Rect<Dimension> = Rect(Dimension.Undefined, Dimension.Undefined, Dimension.Undefined, Dimension.Undefined)
+        private var border: Rect<Dimension> = Rect(Dimension.Undefined, Dimension.Undefined, Dimension.Undefined, Dimension.Undefined)
+        private var flexGrow: Float = 0f
+        private var flexShrink: Float = 1f
+        private var flexBasis: Dimension = Dimension.Auto
+        private var size: Size<Dimension> = Size(Dimension.Auto, Dimension.Auto)
+        private var minSize: Size<Dimension> = Size(Dimension.Auto, Dimension.Auto)
+        private var maxSize: Size<Dimension> = Size(Dimension.Auto, Dimension.Auto)
+        private var aspectRatio: Float? = null
+
+        fun build(): Style {
+            return Style(
+                display,
+                positionType,
+                direction,
+                flexDirection,
+                flexWrap,
+                overflow,
+                alignItems,
+                alignSelf,
+                alignContent,
+                justifyContent,
+                position,
+                margin,
+                padding,
+                border,
+                flexGrow,
+                flexShrink,
+                flexBasis,
+                size,
+                minSize,
+                maxSize,
+                aspectRatio
+            )
+        }
+
+        fun display(display: Display): Builder {
+            this.display = display
+            return this
+        }
+
+        fun positionType(positionType: PositionType): Builder {
+            this.positionType = positionType
+            return this
+        }
+
+        fun direction(direction: Direction): Builder {
+            this.direction = direction
+            return this
+        }
+
+        fun flexDirection(flexDirection: FlexDirection): Builder {
+            this.flexDirection = flexDirection
+            return this
+        }
+
+        fun flexWrap(flexWrap: FlexWrap): Builder {
+            this.flexWrap = flexWrap
+            return this
+        }
+
+        fun overflow(overflow: Overflow): Builder {
+            this.overflow = overflow
+            return this
+        }
+
+        fun alignItems(alignItems: AlignItems): Builder {
+            this.alignItems = alignItems
+            return this
+        }
+
+        fun alignSelf(alignSelf: AlignSelf): Builder {
+            this.alignSelf = alignSelf
+            return this
+        }
+
+        fun alignContent(alignContent: AlignContent): Builder {
+            this.alignContent = alignContent
+            return this
+        }
+
+        fun justifyContent(justifyContent: JustifyContent): Builder {
+            this.justifyContent = justifyContent
+            return this
+        }
+
+        fun position(position: Rect<Dimension>): Builder {
+            this.position = position
+            return this
+        }
+
+        fun margin(margin: Rect<Dimension>): Builder {
+            this.margin = margin
+            return this
+        }
+
+        fun padding(padding: Rect<Dimension>): Builder {
+            this.padding = padding
+            return this
+        }
+
+        fun border(border: Rect<Dimension>): Builder {
+            this.border = border
+            return this
+        }
+
+        fun flexGrow(flexGrow: Float): Builder {
+            this.flexGrow = flexGrow
+            return this
+        }
+
+        fun flexShrink(flexShrink: Float): Builder {
+            this.flexShrink = flexShrink
+            return this
+        }
+
+        fun flexBasis(flexBasis: Dimension): Builder {
+            this.flexBasis = flexBasis
+            return this
+        }
+
+        fun size(size: Size<Dimension>): Builder {
+            this.size = size
+            return this
+        }
+
+        fun minSize(minSize: Size<Dimension>): Builder {
+            this.minSize = minSize
+            return this
+        }
+
+        fun maxSize(maxSize: Size<Dimension>): Builder {
+            this.maxSize = maxSize
+            return this
+        }
+
+        fun aspectRatio(aspectRatio: Float?): Builder {
+            this.aspectRatio = aspectRatio
+            return this
+        }
+    }
 }
diff --git a/bindings/kotlin/stretch/src/main/libs/arm64-v8a/libstretch.so b/bindings/kotlin/stretch/src/main/libs/arm64-v8a/libstretch.so
index 4d05d07d5..62b879c13 100755
Binary files a/bindings/kotlin/stretch/src/main/libs/arm64-v8a/libstretch.so and b/bindings/kotlin/stretch/src/main/libs/arm64-v8a/libstretch.so differ
diff --git a/bindings/kotlin/stretch/src/main/libs/armeabi-v7a/libstretch.so b/bindings/kotlin/stretch/src/main/libs/armeabi-v7a/libstretch.so
index bcca778e9..6b9df4547 100755
Binary files a/bindings/kotlin/stretch/src/main/libs/armeabi-v7a/libstretch.so and b/bindings/kotlin/stretch/src/main/libs/armeabi-v7a/libstretch.so differ
diff --git a/bindings/kotlin/stretch/src/main/libs/x86/libstretch.so b/bindings/kotlin/stretch/src/main/libs/x86/libstretch.so
index f39f1cdba..4a2fca50f 100755
Binary files a/bindings/kotlin/stretch/src/main/libs/x86/libstretch.so and b/bindings/kotlin/stretch/src/main/libs/x86/libstretch.so differ
diff --git a/bindings/kotlin/stretch/src/main/rust/Cargo.toml b/bindings/kotlin/stretch/src/main/rust/Cargo.toml
index 9a73d2b9b..760b33c13 100644
--- a/bindings/kotlin/stretch/src/main/rust/Cargo.toml
+++ b/bindings/kotlin/stretch/src/main/rust/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 
 [dependencies]
 jni = { version = "0.10.2", default-features = false }
-stretch = "0.2.1"
+stretch = "0.2.2"
 
 [lib]
 name = "stretch"
diff --git a/bindings/kotlin/stretch/src/main/rust/src/lib.rs b/bindings/kotlin/stretch/src/main/rust/src/lib.rs
index b5c3881dd..04a32c925 100644
--- a/bindings/kotlin/stretch/src/main/rust/src/lib.rs
+++ b/bindings/kotlin/stretch/src/main/rust/src/lib.rs
@@ -274,7 +274,7 @@ pub unsafe extern "C" fn Java_app_visly_stretch_Node_nConstructLeaf(
 ) -> jlong {
     let measure = env.new_global_ref(measure).unwrap();
     let style: Box<Style> = Box::from_raw(style as *mut Style);
-    let node = Node::new_leaf(*Box::leak(style), Some(Box::new(move |constraint| {
+    let node = Node::new_leaf(*Box::leak(style), Box::new(move |constraint| {
         let result = env.call_method(
             measure.as_obj(),
             "measure",
@@ -291,7 +291,7 @@ pub unsafe extern "C" fn Java_app_visly_stretch_Node_nConstructLeaf(
             }
             Err(err) => Err(Box::new(err)),
         }
-    })));
+    }));
     Box::into_raw(Box::new(node)) as jlong
 }
 
@@ -305,13 +305,33 @@ pub unsafe extern "C" fn Java_app_visly_stretch_Node_nFree(
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn Java_app_visly_stretch_Node_nChildCount(
-    _: JNIEnv,
+pub unsafe extern "C" fn Java_app_visly_stretch_Node_nSetMeasure(
+    env: JNIEnv<'static>,
     _: JObject,
     node: jlong,
-) -> jint {
+    measure: JObject,
+) {
+    let measure = env.new_global_ref(measure).unwrap();
     let node: Box<Node> = Box::from_raw(node as *mut Node);
-    Box::leak(node).children().len() as i32
+
+    Box::leak(node).set_measure(Some(Box::new(move |constraint| {
+         let result = env.call_method(
+             measure.as_obj(),
+             "measure",
+             "(FF)[F",
+             &[JValue::from(constraint.width.or_else(f32::NAN)), JValue::from(constraint.height.or_else(f32::NAN))],
+         );
+
+         match result {
+             Ok(result) => {
+                 let size = result.l().unwrap().into_inner() as jfloatArray;
+                 let mut buff: [f32; 2] = [0.0, 0.0];
+                 env.get_float_array_region(size, 0, &mut buff).unwrap();
+                 Ok(Size { width: buff[0], height: buff[1] })
+             }
+             Err(err) => Err(Box::new(err)),
+         }
+     })));
 }
 
 #[no_mangle]
diff --git a/src/node.rs b/src/node.rs
index e9369b1a7..5221c464a 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -26,12 +26,12 @@ pub(crate) struct InternalNode {
 pub struct Node(Rc<RefCell<InternalNode>>);
 
 impl Node {
-    pub fn new_leaf(style: Style, measure: Option<MeasureFunc>) -> Node {
+    pub fn new_leaf(style: Style, measure: MeasureFunc) -> Node {
         Node(Rc::new(RefCell::new(InternalNode {
             style,
             parents: Vec::with_capacity(1),
             children: Vec::with_capacity(0),
-            measure,
+            measure: Some(measure),
             layout_cache: None,
             is_dirty: true,
         })))
@@ -55,6 +55,11 @@ impl Node {
         parent
     }
 
+    pub fn set_measure(&mut self, measure: Option<MeasureFunc>) {
+        self.0.borrow_mut().measure = measure;
+        self.mark_dirty();
+    }
+
     pub fn add_child(&mut self, child: &Node) {
         child.0.borrow_mut().parents.push(Rc::downgrade(&self.0));
         self.0.borrow_mut().children.push(Rc::clone(&child.0));
@@ -117,6 +122,11 @@ impl Node {
         node.children.iter().map(|child| Node(Rc::clone(child))).collect()
     }
 
+    pub fn child_count(&self) -> usize {
+        let node = self.0.borrow_mut();
+        node.children.len()
+    }
+
     pub fn set_style(&mut self, style: Style) {
         self.0.borrow_mut().style = style;
         self.mark_dirty();
diff --git a/tests/measure.rs b/tests/measure.rs
index afed43f61..a5f3fb7f0 100644
--- a/tests/measure.rs
+++ b/tests/measure.rs
@@ -6,12 +6,12 @@ mod measure {
     fn measure_root() {
         let layout = stretch::node::Node::new_leaf(
             stretch::style::Style { ..Default::default() },
-            Some(Box::new(|constraint| {
+            Box::new(|constraint| {
                 Ok(stretch::geometry::Size {
                     width: constraint.width.or_else(100.0),
                     height: constraint.height.or_else(100.0),
                 })
-            })),
+            }),
         )
         .compute_layout(stretch::geometry::Size::undefined())
         .unwrap();
@@ -26,12 +26,12 @@ mod measure {
             stretch::style::Style { ..Default::default() },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(100.0),
                         height: constraint.height.or_else(100.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -53,12 +53,12 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(100.0),
                         height: constraint.height.or_else(100.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -86,12 +86,12 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(100.0),
                         height: constraint.height.or_else(100.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -124,12 +124,12 @@ mod measure {
                 ),
                 &stretch::node::Node::new_leaf(
                     stretch::style::Style { flex_grow: 1.0, ..Default::default() },
-                    Some(Box::new(|constraint| {
+                    Box::new(|constraint| {
                         Ok(stretch::geometry::Size {
                             width: constraint.width.or_else(10.0),
                             height: constraint.height.or_else(50.0),
                         })
-                    })),
+                    }),
                 ),
             ],
         )
@@ -161,12 +161,12 @@ mod measure {
                 ),
                 &stretch::node::Node::new_leaf(
                     stretch::style::Style { ..Default::default() },
-                    Some(Box::new(|constraint| {
+                    Box::new(|constraint| {
                         Ok(stretch::geometry::Size {
                             width: constraint.width.or_else(100.0),
                             height: constraint.height.or_else(50.0),
                         })
-                    })),
+                    }),
                 ),
             ],
         )
@@ -198,11 +198,11 @@ mod measure {
                 ),
                 &stretch::node::Node::new_leaf(
                     stretch::style::Style { flex_grow: 1.0, ..Default::default() },
-                    Some(Box::new(|constraint| {
+                    Box::new(|constraint| {
                         let width = constraint.width.or_else(10.0);
                         let height = constraint.height.or_else(width * 2.0);
                         Ok(stretch::geometry::Size { width, height })
-                    })),
+                    }),
                 ),
             ],
         )
@@ -235,11 +235,11 @@ mod measure {
                 ),
                 &stretch::node::Node::new_leaf(
                     stretch::style::Style { ..Default::default() },
-                    Some(Box::new(|constraint| {
+                    Box::new(|constraint| {
                         let width = constraint.width.or_else(100.0);
                         let height = constraint.height.or_else(width * 2.0);
                         Ok(stretch::geometry::Size { width, height })
-                    })),
+                    }),
                 ),
             ],
         )
@@ -262,11 +262,11 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     let height = constraint.height.or_else(50.0);
                     let width = constraint.width.or_else(height);
                     Ok(stretch::geometry::Size { width, height })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -288,12 +288,12 @@ mod measure {
                     },
                     ..Default::default()
                 },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(100.0),
                         height: constraint.height.or_else(100.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -315,12 +315,12 @@ mod measure {
                     },
                     ..Default::default()
                 },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(100.0),
                         height: constraint.height.or_else(100.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -355,12 +355,12 @@ mod measure {
                         flex_grow: 1.0,
                         ..Default::default()
                     },
-                    Some(Box::new(|constraint| {
+                    Box::new(|constraint| {
                         Ok(stretch::geometry::Size {
                             width: constraint.width.or_else(100.0),
                             height: constraint.height.or_else(100.0),
                         })
-                    })),
+                    }),
                 ),
             ],
         )
@@ -385,12 +385,12 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(50.0),
                         height: constraint.height.or_else(50.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -412,12 +412,12 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { position_type: stretch::style::PositionType::Absolute, ..Default::default() },
-                Some(Box::new(|constraint| {
+                Box::new(|constraint| {
                     Ok(stretch::geometry::Size {
                         width: constraint.width.or_else(50.0),
                         height: constraint.height.or_else(50.0),
                     })
-                })),
+                }),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -439,7 +439,7 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { flex_grow: 1.0, ..Default::default() },
-                Some(Box::new(|_| Ok(stretch::geometry::Size { width: 200.0, height: 200.0 }))),
+                Box::new(|_| Ok(stretch::geometry::Size { width: 200.0, height: 200.0 })),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined())
@@ -460,13 +460,13 @@ mod measure {
                 stretch::style::Style { ..Default::default() },
                 vec![&stretch::node::Node::new_leaf(
                     stretch::style::Style { ..Default::default() },
-                    Some(Box::new(move |constraint| {
+                    Box::new(move |constraint| {
                         unsafe { (*num_measure_ptr) += 1 };
                         Ok(stretch::geometry::Size {
                             width: constraint.width.or_else(50.0),
                             height: constraint.height.or_else(50.0),
                         })
-                    })),
+                    }),
                 )],
             )],
         )
@@ -488,7 +488,7 @@ mod measure {
             },
             vec![&stretch::node::Node::new_leaf(
                 stretch::style::Style { flex_grow: 1.0, ..Default::default() },
-                Some(Box::new(|_| Err(Box::new("")))),
+                Box::new(|_| Err(Box::new(""))),
             )],
         )
         .compute_layout(stretch::geometry::Size::undefined());
diff --git a/tests/node.rs b/tests/node.rs
index 1f29dddaf..ea35cb990 100644
--- a/tests/node.rs
+++ b/tests/node.rs
@@ -1,5 +1,6 @@
 #[cfg(test)]
 mod node {
+    use stretch::geometry::*;
     use stretch::node::Node;
     use stretch::style::*;
 
@@ -9,21 +10,30 @@ mod node {
         let child2 = Node::new(Style::default(), vec![]);
         let node = Node::new(Style::default(), vec![&child1, &child2]);
 
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
         assert_eq!(node.children()[0], child1);
         assert_eq!(node.children()[1], child2);
     }
 
+    #[test]
+    fn set_measure() {
+        let mut node = Node::new_leaf(Style::default(), Box::new(|_| Ok(Size { width: 200.0, height: 200.0 })));
+        assert_eq!(node.compute_layout(Size::undefined()).unwrap().size.width, 200.0);
+
+        node.set_measure(Some(Box::new(|_| Ok(Size { width: 100.0, height: 100.0 }))));
+        assert_eq!(node.compute_layout(Size::undefined()).unwrap().size.width, 100.0);
+    }
+
     #[test]
     fn add_child() {
         let mut node = Node::new(Style::default(), vec![]);
-        assert_eq!(node.children().len(), 0);
+        assert_eq!(node.child_count(), 0);
 
         node.add_child(&Node::new(Style::default(), vec![]));
-        assert_eq!(node.children().len(), 1);
+        assert_eq!(node.child_count(), 1);
 
         node.add_child(&Node::new(Style::default(), vec![]));
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
     }
 
     #[test]
@@ -32,14 +42,14 @@ mod node {
         let child2 = Node::new(Style::default(), vec![]);
 
         let mut node = Node::new(Style::default(), vec![&child1, &child2]);
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
 
         node.remove_child(&child1);
-        assert_eq!(node.children().len(), 1);
+        assert_eq!(node.child_count(), 1);
         assert_eq!(node.children()[0], child2);
 
         node.remove_child(&child2);
-        assert_eq!(node.children().len(), 0);
+        assert_eq!(node.child_count(), 0);
     }
 
     #[test]
@@ -48,14 +58,14 @@ mod node {
         let child2 = Node::new(Style::default(), vec![]);
 
         let mut node = Node::new(Style::default(), vec![&child1, &child2]);
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
 
         node.remove_child_at_index(0);
-        assert_eq!(node.children().len(), 1);
+        assert_eq!(node.child_count(), 1);
         assert_eq!(node.children()[0], child2);
 
         node.remove_child_at_index(0);
-        assert_eq!(node.children().len(), 0);
+        assert_eq!(node.child_count(), 0);
     }
 
     #[test]
@@ -64,11 +74,11 @@ mod node {
         let child2 = Node::new(Style::default(), vec![]);
 
         let mut node = Node::new(Style::default(), vec![&child1]);
-        assert_eq!(node.children().len(), 1);
+        assert_eq!(node.child_count(), 1);
         assert_eq!(node.children()[0], child1);
 
         node.replace_child_at_index(0, &child2);
-        assert_eq!(node.children().len(), 1);
+        assert_eq!(node.child_count(), 1);
         assert_eq!(node.children()[0], child2);
     }
 
@@ -78,7 +88,7 @@ mod node {
         let child2 = Node::new(Style::default(), vec![]);
         let mut node = Node::new(Style::default(), vec![&child1, &child2]);
 
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
         assert_eq!(node.children()[0], child1);
         assert_eq!(node.children()[1], child2);
 
@@ -86,7 +96,7 @@ mod node {
         let child4 = Node::new(Style::default(), vec![]);
         node.set_children(vec![&child3, &child4]);
 
-        assert_eq!(node.children().len(), 2);
+        assert_eq!(node.child_count(), 2);
         assert_eq!(node.children()[0], child3);
         assert_eq!(node.children()[1], child4);
     }