mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
new android studio project working
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
android-widgets-app
|
||||||
22
.idea/compiler.xml
generated
Normal file
22
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<resourceExtensions />
|
||||||
|
<wildcardResourcePatterns>
|
||||||
|
<entry name="!?*.java" />
|
||||||
|
<entry name="!?*.form" />
|
||||||
|
<entry name="!?*.class" />
|
||||||
|
<entry name="!?*.groovy" />
|
||||||
|
<entry name="!?*.scala" />
|
||||||
|
<entry name="!?*.flex" />
|
||||||
|
<entry name="!?*.kt" />
|
||||||
|
<entry name="!?*.clj" />
|
||||||
|
<entry name="!?*.aj" />
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="false">
|
||||||
|
<processorPath useClasspath="true" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.idea/copyright/profiles_settings.xml
generated
Normal file
3
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
||||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
26
.idea/gradle.xml
generated
Normal file
26
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="1.8" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
<option value="$PROJECT_DIR$/widgets" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="myModules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
<option value="$PROJECT_DIR$/widgets" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
46
.idea/misc.xml
generated
Normal file
46
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<entry_points version="2.0" />
|
||||||
|
</component>
|
||||||
|
<component name="NullableNotNullManager">
|
||||||
|
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||||
|
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||||
|
<option name="myNullables">
|
||||||
|
<value>
|
||||||
|
<list size="4">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="myNotNulls">
|
||||||
|
<value>
|
||||||
|
<list size="4">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||||
|
<OptionsSetting value="true" id="Add" />
|
||||||
|
<OptionsSetting value="true" id="Remove" />
|
||||||
|
<OptionsSetting value="true" id="Checkout" />
|
||||||
|
<OptionsSetting value="true" id="Update" />
|
||||||
|
<OptionsSetting value="true" id="Status" />
|
||||||
|
<OptionsSetting value="true" id="Edit" />
|
||||||
|
<ConfirmationsSetting value="0" id="Add" />
|
||||||
|
<ConfirmationsSetting value="0" id="Remove" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/modules.xml
generated
Normal file
10
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/android-widgets-app.iml" filepath="$PROJECT_DIR$/android-widgets-app.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/widgets/widgets.iml" filepath="$PROJECT_DIR$/widgets/widgets.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/runConfigurations.xml
generated
Normal file
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
26
app/build.gradle
Normal file
26
app/build.gradle
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 23
|
||||||
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.nativescript.android_widgets_app"
|
||||||
|
minSdkVersion 17
|
||||||
|
targetSdkVersion 23
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||||
|
}
|
||||||
17
app/proguard-rules.pro
vendored
Normal file
17
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in d:\Android\sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# 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 *;
|
||||||
|
#}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.nativescript.android_widgets_app;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.test.ApplicationTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||||
|
*/
|
||||||
|
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||||
|
public ApplicationTest() {
|
||||||
|
super(Application.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/src/main/AndroidManifest.xml
Normal file
13
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.nativescript.android_widgets_app">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
6
app/src/main/res/values/colors.xml
Normal file
6
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
||||||
3
app/src/main/res/values/strings.xml
Normal file
3
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">android-widgets-app</string>
|
||||||
|
</resources>
|
||||||
11
app/src/main/res/values/styles.xml
Normal file
11
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.nativescript.android_widgets_app;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() throws Exception {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
build.gradle
Normal file
23
build.gradle
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:2.0.0-alpha1'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Wed Oct 21 11:34:03 PDT 2015
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
|
||||||
160
gradlew
vendored
Normal file
160
gradlew
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
include ':app', ':widgets'
|
||||||
1
widgets/.gitignore
vendored
Normal file
1
widgets/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
25
widgets/build.gradle
Normal file
25
widgets/build.gradle
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 23
|
||||||
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 17
|
||||||
|
targetSdkVersion 23
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||||
|
}
|
||||||
17
widgets/proguard-rules.pro
vendored
Normal file
17
widgets/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in d:\Android\sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# 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 *;
|
||||||
|
#}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.test.ApplicationTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||||
|
*/
|
||||||
|
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||||
|
public ApplicationTest() {
|
||||||
|
super(Application.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
widgets/src/main/AndroidManifest.xml
Normal file
11
widgets/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.nativescript.widgets">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true">
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*/
|
||||||
|
public class CommonLayoutParams extends FrameLayout.LayoutParams {
|
||||||
|
|
||||||
|
static final String TAG = "NSLayout";
|
||||||
|
static int debuggable = -1;
|
||||||
|
private static final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
public CommonLayoutParams() {
|
||||||
|
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float widthPercent = 0;
|
||||||
|
public float heightPercent = 0;
|
||||||
|
|
||||||
|
public float topMarginPercent = 0;
|
||||||
|
public float leftMarginPercent = 0;
|
||||||
|
public float bottomMarginPercent = 0;
|
||||||
|
public float rightMarginPercent = 0;
|
||||||
|
|
||||||
|
public int widthOriginal = -1;
|
||||||
|
public int heightOriginal = -1;
|
||||||
|
|
||||||
|
public int topMarginOriginal = -1;
|
||||||
|
public int leftMarginOriginal = -1;
|
||||||
|
public int bottomMarginOriginal = -1;
|
||||||
|
public int rightMarginOriginal = -1;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected static int getDesiredWidth(View view) {
|
||||||
|
CommonLayoutParams lp = (CommonLayoutParams) view.getLayoutParams();
|
||||||
|
return view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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")
|
||||||
|
protected static void layoutChild(View child, int left, int top, int right, int bottom) {
|
||||||
|
if (child.getVisibility() == View.GONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childTop = 0;
|
||||||
|
int childLeft = 0;
|
||||||
|
|
||||||
|
int childWidth = child.getMeasuredWidth();
|
||||||
|
int childHeight = child.getMeasuredHeight();
|
||||||
|
|
||||||
|
CommonLayoutParams lp = (CommonLayoutParams) child.getLayoutParams();
|
||||||
|
int gravity = lp.gravity;
|
||||||
|
if (gravity == -1) {
|
||||||
|
gravity = Gravity.FILL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||||
|
|
||||||
|
// If we have explicit height and gravity is FILL we need to be centered otherwise our explicit height won't be taken into account.
|
||||||
|
if (lp.height >= 0 && verticalGravity == Gravity.FILL_VERTICAL) {
|
||||||
|
verticalGravity = Gravity.CENTER_VERTICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (verticalGravity) {
|
||||||
|
case Gravity.TOP:
|
||||||
|
childTop = top + lp.topMargin;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.CENTER_VERTICAL:
|
||||||
|
childTop = top + (bottom - top - childHeight + lp.topMargin - lp.bottomMargin) / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.BOTTOM:
|
||||||
|
childTop = bottom - childHeight - lp.bottomMargin;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.FILL_VERTICAL:
|
||||||
|
default:
|
||||||
|
childTop = top + lp.topMargin;
|
||||||
|
childHeight = bottom - top - (lp.topMargin + lp.bottomMargin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int horizontalGravity = Gravity.getAbsoluteGravity(gravity, child.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||||
|
|
||||||
|
// If we have explicit width and gravity is FILL we need to be centered otherwise our explicit width won't be taken into account.
|
||||||
|
if (lp.width >= 0 && horizontalGravity == Gravity.FILL_HORIZONTAL) {
|
||||||
|
horizontalGravity = Gravity.CENTER_HORIZONTAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (horizontalGravity) {
|
||||||
|
case Gravity.LEFT:
|
||||||
|
childLeft = left + lp.leftMargin;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.CENTER_HORIZONTAL:
|
||||||
|
childLeft = left + (right - left - childWidth + lp.leftMargin - lp.rightMargin) / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.RIGHT:
|
||||||
|
childLeft = right - childWidth - lp.rightMargin;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Gravity.FILL_HORIZONTAL:
|
||||||
|
default:
|
||||||
|
childLeft = left + lp.leftMargin;
|
||||||
|
childWidth = right - left - (lp.leftMargin + lp.rightMargin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childRight = Math.round(childLeft + childWidth);
|
||||||
|
int childBottom = Math.round(childTop + childHeight);
|
||||||
|
childLeft = Math.round(childLeft);
|
||||||
|
childTop = Math.round(childTop);
|
||||||
|
|
||||||
|
// Re-measure TextView because it is not centered if layout width is larger than measure width.
|
||||||
|
if (child instanceof android.widget.TextView) {
|
||||||
|
|
||||||
|
boolean canChangeWidth = lp.width < 0;
|
||||||
|
boolean canChangeHeight = lp.height < 0;
|
||||||
|
|
||||||
|
int measuredWidth = child.getMeasuredWidth();
|
||||||
|
int measuredHeight = child.getMeasuredHeight();
|
||||||
|
|
||||||
|
int width = childRight - childLeft;
|
||||||
|
int height = childBottom - childTop;
|
||||||
|
if ((Math.abs(measuredWidth - width) > 1 && canChangeWidth) || (Math.abs(measuredHeight - height) > 1 && canChangeHeight)) {
|
||||||
|
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(canChangeWidth ? width : lp.width, MeasureSpec.EXACTLY);
|
||||||
|
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(canChangeHeight ? height : lp.height, MeasureSpec.EXACTLY);
|
||||||
|
if (debuggable > 0) {
|
||||||
|
sb.setLength(0);
|
||||||
|
sb.append("remeasure ");
|
||||||
|
sb.append(child);
|
||||||
|
sb.append(" with ");
|
||||||
|
sb.append(MeasureSpec.toString(widthMeasureSpec));
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(MeasureSpec.toString(heightMeasureSpec));
|
||||||
|
log(TAG, sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
child.measure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debuggable > 0) {
|
||||||
|
sb.setLength(0);
|
||||||
|
sb.append(child.getParent().toString());
|
||||||
|
sb.append(" :layoutChild: ");
|
||||||
|
sb.append(child.toString());
|
||||||
|
sb.append(" ");
|
||||||
|
sb.append(childLeft);
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(childTop);
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(childRight);
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(childBottom);
|
||||||
|
log(TAG, sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
child.layout(childLeft, childTop, childRight, childBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void measureChild(View child, int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
if (child.getVisibility() == View.GONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative means not initialized.
|
||||||
|
if (debuggable < 0) {
|
||||||
|
try {
|
||||||
|
Context context = child.getContext();
|
||||||
|
ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), android.content.pm.PackageManager.GET_META_DATA);
|
||||||
|
android.os.Bundle bundle = ai.metaData;
|
||||||
|
Boolean debugLayouts = bundle != null ? bundle.getBoolean("debugLayouts", false) : false;
|
||||||
|
debuggable = debugLayouts ? 1 : 0;
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
debuggable = 0;
|
||||||
|
Log.e(TAG, "Failed to load meta-data, NameNotFound: " + e.getMessage());
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
debuggable = 0;
|
||||||
|
Log.e(TAG, "Failed to load meta-data, NullPointer: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int childWidthMeasureSpec = getMeasureSpec(child, widthMeasureSpec, true);
|
||||||
|
int childHeightMeasureSpec = getMeasureSpec(child, heightMeasureSpec, false);
|
||||||
|
|
||||||
|
if (debuggable > 0) {
|
||||||
|
sb.setLength(0);
|
||||||
|
sb.append(child.getParent().toString());
|
||||||
|
sb.append(" :measureChild: ");
|
||||||
|
sb.append(child.toString());
|
||||||
|
sb.append(" ");
|
||||||
|
sb.append(MeasureSpec.toString(childWidthMeasureSpec));
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(MeasureSpec.toString(childHeightMeasureSpec));
|
||||||
|
log(TAG, sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over children and changes their width and height to one calculated from percentage
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param viewGroup The parent ViewGroup.
|
||||||
|
* @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
|
||||||
|
* @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
|
||||||
|
*/
|
||||||
|
protected static void adjustChildrenLayoutParams(ViewGroup viewGroup, int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
|
||||||
|
int availableWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int widthSpec = MeasureSpec.getMode(widthMeasureSpec);
|
||||||
|
|
||||||
|
int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
int heightSpec = MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
|
||||||
|
View child = viewGroup.getChildAt(i);
|
||||||
|
LayoutParams params = child.getLayoutParams();
|
||||||
|
|
||||||
|
if (params instanceof CommonLayoutParams) {
|
||||||
|
CommonLayoutParams lp = (CommonLayoutParams) child.getLayoutParams();
|
||||||
|
if (widthSpec != MeasureSpec.UNSPECIFIED) {
|
||||||
|
if (lp.widthPercent > 0) {
|
||||||
|
lp.widthOriginal = lp.width;
|
||||||
|
lp.width = (int) (availableWidth * lp.widthPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.widthOriginal = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.leftMarginPercent > 0) {
|
||||||
|
lp.leftMarginOriginal = lp.leftMargin;
|
||||||
|
lp.leftMargin = (int) (availableWidth * lp.leftMarginPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.leftMarginOriginal = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.rightMarginPercent > 0) {
|
||||||
|
lp.rightMarginOriginal = lp.rightMargin;
|
||||||
|
lp.rightMargin = (int) (availableWidth * lp.rightMarginPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.rightMarginOriginal = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heightSpec != MeasureSpec.UNSPECIFIED) {
|
||||||
|
if (lp.heightPercent > 0) {
|
||||||
|
lp.heightOriginal = lp.height;
|
||||||
|
lp.height = (int) (availableHeight * lp.heightPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.heightOriginal = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.topMarginPercent > 0) {
|
||||||
|
lp.topMarginOriginal = lp.topMargin;
|
||||||
|
lp.topMargin = (int) (availableHeight * lp.topMarginPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.topMarginOriginal = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.bottomMarginPercent > 0) {
|
||||||
|
lp.bottomMarginOriginal = lp.bottomMargin;
|
||||||
|
lp.bottomMargin = (int) (availableHeight * lp.bottomMarginPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp.bottomMarginOriginal = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over children and restores their original dimensions that were changed for
|
||||||
|
* percentage values.
|
||||||
|
*/
|
||||||
|
protected static void restoreOriginalParams(ViewGroup viewGroup) {
|
||||||
|
for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
|
||||||
|
View view = viewGroup.getChildAt(i);
|
||||||
|
LayoutParams params = view.getLayoutParams();
|
||||||
|
if (params instanceof CommonLayoutParams) {
|
||||||
|
CommonLayoutParams lp = (CommonLayoutParams) params;
|
||||||
|
if (lp.widthPercent > 0) {
|
||||||
|
lp.width = lp.widthOriginal;
|
||||||
|
}
|
||||||
|
if (lp.heightPercent > 0) {
|
||||||
|
lp.height = lp.heightOriginal;
|
||||||
|
}
|
||||||
|
if (lp.leftMarginPercent > 0) {
|
||||||
|
lp.leftMargin = lp.leftMarginOriginal;
|
||||||
|
}
|
||||||
|
if (lp.topMarginPercent > 0) {
|
||||||
|
lp.topMargin = lp.topMarginOriginal;
|
||||||
|
}
|
||||||
|
if (lp.rightMarginPercent > 0) {
|
||||||
|
lp.rightMargin = lp.rightMarginOriginal;
|
||||||
|
}
|
||||||
|
if (lp.bottomMarginPercent > 0) {
|
||||||
|
lp.bottomMargin = lp.bottomMarginOriginal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = MeasureSpec.UNSPECIFIED;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
widgets/src/main/java/org/nativescript/widgets/Dock.java
Normal file
15
widgets/src/main/java/org/nativescript/widgets/Dock.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum Dock {
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
right,
|
||||||
|
bottom
|
||||||
|
}
|
||||||
178
widgets/src/main/java/org/nativescript/widgets/DockLayout.java
Normal file
178
widgets/src/main/java/org/nativescript/widgets/DockLayout.java
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
1166
widgets/src/main/java/org/nativescript/widgets/GridLayout.java
Normal file
1166
widgets/src/main/java/org/nativescript/widgets/GridLayout.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum GridUnitType {
|
||||||
|
auto,
|
||||||
|
pixel,
|
||||||
|
star
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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).
|
||||||
|
// Check the previous line - this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
|
||||||
|
//this.contentMeasuredWidth += this.getPaddingLeft() + this.getPaddingRight();
|
||||||
|
//this.contentMeasuredHeight += this.getPaddingTop() + this.getPaddingBottom();
|
||||||
|
|
||||||
|
// Check against our minimum height
|
||||||
|
this.contentMeasuredWidth = Math.max(this.contentMeasuredWidth, this.getSuggestedMinimumWidth());
|
||||||
|
this.contentMeasuredHeight = Math.max(this.contentMeasuredHeight, this.getSuggestedMinimumHeight());
|
||||||
|
|
||||||
|
int widthSizeAndState = resolveSizeAndState(this.contentMeasuredWidth, widthMeasureSpec, 0);
|
||||||
|
int heightSizeAndState = resolveSizeAndState(this.contentMeasuredHeight, heightMeasureSpec, 0);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
int childWidth = 0;
|
||||||
|
if (this.getChildCount() > 0) {
|
||||||
|
View child = this.getChildAt(0);
|
||||||
|
childWidth = child.getMeasuredWidth();
|
||||||
|
|
||||||
|
int width = right - left;
|
||||||
|
int height = bottom - top;
|
||||||
|
|
||||||
|
this.scrollableLength = this.contentMeasuredWidth - width;
|
||||||
|
CommonLayoutParams.layoutChild(child, 0, 0, Math.max(this.contentMeasuredWidth, width), height);
|
||||||
|
this.scrollableLength = Math.max(0, this.scrollableLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mIsLayoutDirty = false;
|
||||||
|
// Give a child focus if it needs it
|
||||||
|
if (this.mChildToScrollTo != null && isViewDescendantOf(this.mChildToScrollTo, this)) {
|
||||||
|
this.scrollToChild(this.mChildToScrollTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mChildToScrollTo = null;
|
||||||
|
|
||||||
|
int scrollX = this.getScrollX();
|
||||||
|
int scrollY = this.getScrollY();
|
||||||
|
|
||||||
|
if (this.isFirstLayout) {
|
||||||
|
this.isFirstLayout = false;
|
||||||
|
|
||||||
|
final int scrollRange = Math.max(0, childWidth - (right - left - this.getPaddingLeft() - this.getPaddingRight()));
|
||||||
|
if (this.mSavedState != null) {
|
||||||
|
scrollX = (this.isLayoutRtl() == mSavedState.isLayoutRtl) ? mSavedState.scrollPosition : (scrollRange - this.mSavedState.scrollPosition);
|
||||||
|
mSavedState = null;
|
||||||
|
} else {
|
||||||
|
if (this.isLayoutRtl()) {
|
||||||
|
scrollX = scrollRange - scrollX;
|
||||||
|
} // mScrollX default value is "0" for LTR
|
||||||
|
}
|
||||||
|
// Don't forget to clamp
|
||||||
|
if (scrollX > scrollRange) {
|
||||||
|
scrollX = scrollRange;
|
||||||
|
} else if (scrollX < 0) {
|
||||||
|
scrollX = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling this with the present values causes it to re-claim them
|
||||||
|
this.scrollTo(scrollX, scrollY);
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 Creator<SavedState> CREATOR
|
||||||
|
= new Creator<SavedState>() {
|
||||||
|
public SavedState createFromParcel(Parcel in) {
|
||||||
|
return new SavedState(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedState[] newArray(int size) {
|
||||||
|
return new SavedState[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
168
widgets/src/main/java/org/nativescript/widgets/ImageView.java
Normal file
168
widgets/src/main/java/org/nativescript/widgets/ImageView.java
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ImageView extends android.widget.ImageView {
|
||||||
|
private float cornerRadius = 0;
|
||||||
|
private float borderWidth = 0;
|
||||||
|
|
||||||
|
private Path path = new Path();
|
||||||
|
private RectF rect = new RectF();
|
||||||
|
|
||||||
|
private double scaleW = 1;
|
||||||
|
private double scaleH = 1;
|
||||||
|
|
||||||
|
public ImageView(Context context) {
|
||||||
|
super(context);
|
||||||
|
this.setScaleType(ScaleType.FIT_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getCornerRadius() {
|
||||||
|
return this.cornerRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCornerRadius(float radius) {
|
||||||
|
if (radius != this.cornerRadius) {
|
||||||
|
this.cornerRadius = radius;
|
||||||
|
this.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getBorderWidth() {
|
||||||
|
return this.borderWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderWidth(float radius) {
|
||||||
|
if (radius != this.borderWidth) {
|
||||||
|
this.borderWidth = radius;
|
||||||
|
this.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||||
|
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
Drawable drawable = this.getDrawable();
|
||||||
|
int measureWidth;
|
||||||
|
int measureHeight;
|
||||||
|
if (drawable != null) {
|
||||||
|
measureWidth = drawable.getIntrinsicWidth();
|
||||||
|
measureHeight = drawable.getIntrinsicHeight();
|
||||||
|
} else {
|
||||||
|
measureWidth = 0;
|
||||||
|
measureHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean finiteWidth = widthMode != MeasureSpec.UNSPECIFIED;
|
||||||
|
boolean finiteHeight = heightMode != MeasureSpec.UNSPECIFIED;
|
||||||
|
|
||||||
|
if (measureWidth != 0 && measureHeight != 0 && (finiteWidth || finiteHeight)) {
|
||||||
|
this.computeScaleFactor(width, height, finiteWidth, finiteHeight, measureWidth, measureHeight);
|
||||||
|
int resultW = (int) Math.floor(measureWidth * this.scaleW);
|
||||||
|
int resultH = (int) Math.floor(measureHeight * this.scaleH);
|
||||||
|
|
||||||
|
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
|
||||||
|
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
|
||||||
|
}
|
||||||
|
|
||||||
|
measureWidth += this.getPaddingLeft() + this.getPaddingRight();
|
||||||
|
measureHeight += this.getPaddingTop() + this.getPaddingBottom();
|
||||||
|
|
||||||
|
measureWidth = Math.max(measureWidth, getSuggestedMinimumWidth());
|
||||||
|
measureHeight = Math.max(measureHeight, getSuggestedMinimumHeight());
|
||||||
|
|
||||||
|
if (CommonLayoutParams.debuggable > 0) {
|
||||||
|
StringBuilder sb = CommonLayoutParams.getStringBuilder();
|
||||||
|
sb.append("ImageView onMeasure: ");
|
||||||
|
sb.append(MeasureSpec.toString(widthMeasureSpec));
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(MeasureSpec.toString(heightMeasureSpec));
|
||||||
|
sb.append(", stretch: ");
|
||||||
|
sb.append(this.getScaleType());
|
||||||
|
sb.append(", measureWidth: ");
|
||||||
|
sb.append(measureWidth);
|
||||||
|
sb.append(", measureHeight: ");
|
||||||
|
sb.append(measureHeight);
|
||||||
|
|
||||||
|
CommonLayoutParams.log(CommonLayoutParams.TAG, sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
|
||||||
|
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeScaleFactor(int measureWidth, int measureHeight, boolean widthIsFinite, boolean heightIsFinite, double nativeWidth, double nativeHeight) {
|
||||||
|
|
||||||
|
this.scaleW = 1;
|
||||||
|
this.scaleH = 1;
|
||||||
|
|
||||||
|
ScaleType scale = this.getScaleType();
|
||||||
|
if ((scale == ScaleType.CENTER_CROP || scale == ScaleType.FIT_CENTER || scale == ScaleType.FIT_XY) &&
|
||||||
|
(widthIsFinite || heightIsFinite)) {
|
||||||
|
|
||||||
|
this.scaleW = (nativeWidth > 0) ? measureWidth / nativeWidth : 0d;
|
||||||
|
this.scaleH = (nativeHeight > 0) ? measureHeight / nativeHeight : 0d;
|
||||||
|
|
||||||
|
if (!widthIsFinite) {
|
||||||
|
this.scaleW = scaleH;
|
||||||
|
} else if (!heightIsFinite) {
|
||||||
|
this.scaleH = scaleW;
|
||||||
|
} else {
|
||||||
|
// No infinite dimensions.
|
||||||
|
switch (scale) {
|
||||||
|
case FIT_CENTER:
|
||||||
|
this.scaleH = this.scaleW < this.scaleH ? this.scaleW : this.scaleH;
|
||||||
|
this.scaleW = this.scaleH;
|
||||||
|
break;
|
||||||
|
case CENTER_CROP:
|
||||||
|
this.scaleH = this.scaleW > this.scaleH ? this.scaleW : this.scaleH;
|
||||||
|
this.scaleW = this.scaleH;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
// floor the border width to avoid gaps between the border and the image
|
||||||
|
float roundedBorderWidth = (float) Math.floor(this.borderWidth);
|
||||||
|
float innerRadius = Math.max(0, this.cornerRadius - roundedBorderWidth);
|
||||||
|
|
||||||
|
// The border width is included in the padding so there is no need for
|
||||||
|
// clip if there is no inner border radius.
|
||||||
|
if (innerRadius != 0) {
|
||||||
|
this.rect.set(
|
||||||
|
roundedBorderWidth,
|
||||||
|
roundedBorderWidth,
|
||||||
|
this.getWidth() - roundedBorderWidth,
|
||||||
|
this.getHeight() - roundedBorderWidth);
|
||||||
|
|
||||||
|
this.path.reset();
|
||||||
|
this.path.addRoundRect(rect, innerRadius, innerRadius, Path.Direction.CW);
|
||||||
|
|
||||||
|
canvas.clipPath(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
widgets/src/main/java/org/nativescript/widgets/ItemSpec.java
Normal file
60
widgets/src/main/java/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class LayoutBase extends ViewGroup {
|
||||||
|
|
||||||
|
public LayoutBase(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LayoutParams generateDefaultLayoutParams() {
|
||||||
|
return new CommonLayoutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||||
|
return new CommonLayoutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean checkLayoutParams(LayoutParams p) {
|
||||||
|
return p instanceof CommonLayoutParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LayoutParams generateLayoutParams(LayoutParams p) {
|
||||||
|
return new CommonLayoutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldDelayChildPressedState() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getGravity(View view) {
|
||||||
|
int gravity = -1;
|
||||||
|
LayoutParams params = view.getLayoutParams();
|
||||||
|
if (params instanceof FrameLayout.LayoutParams) {
|
||||||
|
gravity = ((FrameLayout.LayoutParams)params).gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gravity == -1) {
|
||||||
|
gravity = Gravity.FILL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gravity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum Orientation {
|
||||||
|
horzontal,
|
||||||
|
vertical
|
||||||
|
}
|
||||||
218
widgets/src/main/java/org/nativescript/widgets/StackLayout.java
Normal file
218
widgets/src/main/java/org/nativescript/widgets/StackLayout.java
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class StackLayout extends LayoutBase {
|
||||||
|
|
||||||
|
private int _totalLength = 0;
|
||||||
|
private Orientation _orientation = Orientation.vertical;
|
||||||
|
|
||||||
|
public StackLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Orientation getOrientation() {
|
||||||
|
return this._orientation;
|
||||||
|
}
|
||||||
|
public void setOrientation(Orientation value) {
|
||||||
|
this._orientation = value;
|
||||||
|
this.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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), childMeasureSpec);
|
||||||
|
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
|
||||||
|
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
|
||||||
|
|
||||||
|
measureHeight = Math.max(measureHeight, childMeasuredHeight);
|
||||||
|
measureWidth += childMeasuredWidth;
|
||||||
|
remainingLength = Math.max(0, remainingLength - childMeasuredWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
childState = combineMeasuredStates(childState, child.getMeasuredState());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in our padding
|
||||||
|
measureWidth += horizontalPadding;
|
||||||
|
measureHeight += verticalPadding;
|
||||||
|
|
||||||
|
// Check against our minimum sizes
|
||||||
|
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
|
||||||
|
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
|
||||||
|
|
||||||
|
this._totalLength = isVertical ? measureHeight : measureWidth;
|
||||||
|
|
||||||
|
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, isVertical ? childState : 0);
|
||||||
|
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, isVertical ? 0 : childState);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
if (this._orientation == Orientation.vertical) {
|
||||||
|
this.layoutVertical(l, t, r, b);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.layoutHorizontal(l, t, r, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = LayoutBase.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childHeight = CommonLayoutParams.getDesiredHeight(child);
|
||||||
|
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 = LayoutBase.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childWidth = CommonLayoutParams.getDesiredWidth(child);
|
||||||
|
CommonLayoutParams.layoutChild(child, childLeft, childTop, childLeft + childWidth, childBottom);
|
||||||
|
childLeft += childWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
public class TabItemSpec {
|
||||||
|
public String title;
|
||||||
|
public int iconId;
|
||||||
|
public Drawable iconDrawable;
|
||||||
|
}
|
||||||
383
widgets/src/main/java/org/nativescript/widgets/TabLayout.java
Normal file
383
widgets/src/main/java/org/nativescript/widgets/TabLayout.java
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.support.v4.view.PagerAdapter;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ImageView.ScaleType;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be used with ViewPager to provide a tab indicator component which give
|
||||||
|
* constant feedback as to the user's scroll progress.
|
||||||
|
* <p>
|
||||||
|
* To use the component, simply add it to your view hierarchy. Then in your
|
||||||
|
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
|
||||||
|
* {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is
|
||||||
|
* being used for.
|
||||||
|
* <p>
|
||||||
|
* The colors can be customized in two ways. The first and simplest is to
|
||||||
|
* provide an array of colors via {@link #setSelectedIndicatorColors(int...)}.
|
||||||
|
* The alternative is via the {@link TabColorizer} interface which provides you
|
||||||
|
* complete control over which color is used for any individual position.
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public class TabLayout extends HorizontalScrollView {
|
||||||
|
/**
|
||||||
|
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||||
|
* {@link #setCustomTabColorizer(TabColorizer)}.
|
||||||
|
*/
|
||||||
|
public interface TabColorizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return the color of the indicator used when {@code position}
|
||||||
|
* is selected.
|
||||||
|
*/
|
||||||
|
int getIndicatorColor(int position);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int TITLE_OFFSET_DIPS = 24;
|
||||||
|
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||||
|
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||||
|
private static final int TEXT_MAX_WIDHT = 180;
|
||||||
|
private static final int SMALL_MIN_HEIGHT = 48;
|
||||||
|
private static final int LARGE_MIN_HEIGHT = 72;
|
||||||
|
|
||||||
|
private int mTitleOffset;
|
||||||
|
|
||||||
|
private boolean mDistributeEvenly = true;
|
||||||
|
|
||||||
|
private TabItemSpec[] mTabItems;
|
||||||
|
private ViewPager mViewPager;
|
||||||
|
private SparseArray<String> mContentDescriptions = new SparseArray<String>();
|
||||||
|
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
|
||||||
|
|
||||||
|
private final TabStrip mTabStrip;
|
||||||
|
|
||||||
|
public TabLayout(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabLayout(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
// Disable the Scroll Bar
|
||||||
|
setHorizontalScrollBarEnabled(false);
|
||||||
|
// Make sure that the Tab Strips fills this View
|
||||||
|
setFillViewport(true);
|
||||||
|
|
||||||
|
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
|
||||||
|
|
||||||
|
mTabStrip = new TabStrip(context);
|
||||||
|
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the custom {@link TabColorizer} to be used.
|
||||||
|
*
|
||||||
|
* If you only require simple customisation then you can use
|
||||||
|
* {@link #setSelectedIndicatorColors(int...)} to achieve similar effects.
|
||||||
|
*/
|
||||||
|
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||||
|
mTabStrip.setCustomTabColorizer(tabColorizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistributeEvenly(boolean distributeEvenly) {
|
||||||
|
mDistributeEvenly = distributeEvenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the colors to be used for indicating the selected tab. These colors
|
||||||
|
* are treated as a circular array. Providing one color will mean that all
|
||||||
|
* tabs are indicated with the same color.
|
||||||
|
*/
|
||||||
|
public void setSelectedIndicatorColors(int... colors) {
|
||||||
|
mTabStrip.setSelectedIndicatorColors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link ViewPager.OnPageChangeListener}. When using
|
||||||
|
* {@link TabLayout} you are required to set any
|
||||||
|
* {@link ViewPager.OnPageChangeListener} through this method. This is so
|
||||||
|
* that the layout can update it's scroll position correctly.
|
||||||
|
*
|
||||||
|
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
|
||||||
|
*/
|
||||||
|
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||||
|
mViewPagerPageChangeListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the associated view pager. Note that the assumption here is that the
|
||||||
|
* pager content (number of tabs and tab titles) does not change after this
|
||||||
|
* call has been made.
|
||||||
|
*/
|
||||||
|
public void setViewPager(ViewPager viewPager) {
|
||||||
|
this.setItems(null, viewPager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(TabItemSpec[] items, ViewPager viewPager) {
|
||||||
|
mTabStrip.removeAllViews();
|
||||||
|
|
||||||
|
mViewPager = viewPager;
|
||||||
|
mTabItems = items;
|
||||||
|
if (viewPager != null) {
|
||||||
|
viewPager.addOnPageChangeListener(new InternalViewPagerListener());
|
||||||
|
populateTabStrip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the UI of an item at specified index
|
||||||
|
*/
|
||||||
|
public void updateItemAt(int position, TabItemSpec tabItem) {
|
||||||
|
LinearLayout ll = (LinearLayout)mTabStrip.getChildAt(position);
|
||||||
|
ImageView imgView = (ImageView)ll.getChildAt(0);
|
||||||
|
TextView textView = (TextView)ll.getChildAt(1);
|
||||||
|
this.setupItem(ll, textView, imgView, tabItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the TextView for tab item at index
|
||||||
|
*/
|
||||||
|
public TextView getTextViewForItemAt(int index){
|
||||||
|
LinearLayout ll = this.getViewForItemAt(index);
|
||||||
|
return (ll != null) ? (TextView)ll.getChildAt(1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LinearLayout container for tab item at index
|
||||||
|
*/
|
||||||
|
public LinearLayout getViewForItemAt(int index){
|
||||||
|
LinearLayout result = null;
|
||||||
|
|
||||||
|
if(this.mTabStrip.getChildCount() > index){
|
||||||
|
result = (LinearLayout)this.mTabStrip.getChildAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default view to be used for tabs.
|
||||||
|
*/
|
||||||
|
protected View createDefaultTabView(Context context, TabItemSpec tabItem) {
|
||||||
|
float density = getResources().getDisplayMetrics().density;
|
||||||
|
int padding = (int) (TAB_VIEW_PADDING_DIPS * density);
|
||||||
|
|
||||||
|
LinearLayout ll = new LinearLayout(context);
|
||||||
|
ll.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
ll.setGravity(Gravity.CENTER);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||||
|
ll.setBackgroundResource(outValue.resourceId);
|
||||||
|
|
||||||
|
ImageView imgView = new ImageView(context);
|
||||||
|
imgView.setScaleType(ScaleType.FIT_CENTER);
|
||||||
|
LinearLayout.LayoutParams imgLP = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
imgLP.gravity = Gravity.CENTER;
|
||||||
|
imgView.setLayoutParams(imgLP);
|
||||||
|
|
||||||
|
TextView textView = new TextView(context);
|
||||||
|
textView.setGravity(Gravity.CENTER);
|
||||||
|
textView.setMaxWidth((int) (TEXT_MAX_WIDHT * density));
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
|
||||||
|
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
textView.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
|
textView.setAllCaps(true);
|
||||||
|
textView.setMaxLines(2);
|
||||||
|
textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
textView.setPadding(padding, 0, padding, 0);
|
||||||
|
|
||||||
|
this.setupItem(ll, textView, imgView, tabItem);
|
||||||
|
|
||||||
|
ll.addView(imgView);
|
||||||
|
ll.addView(textView);
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, TabItemSpec tabItem){
|
||||||
|
float density = getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
|
if (tabItem.iconId != 0) {
|
||||||
|
imgView.setImageResource(tabItem.iconId);
|
||||||
|
imgView.setVisibility(VISIBLE);
|
||||||
|
} else if (tabItem.iconDrawable != null) {
|
||||||
|
imgView.setImageDrawable(tabItem.iconDrawable);
|
||||||
|
imgView.setVisibility(VISIBLE);
|
||||||
|
} else {
|
||||||
|
imgView.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabItem.title != null && !tabItem.title.isEmpty()) {
|
||||||
|
textView.setText(tabItem.title);
|
||||||
|
textView.setVisibility(VISIBLE);
|
||||||
|
} else {
|
||||||
|
textView.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imgView.getVisibility() == VISIBLE && textView.getVisibility() == VISIBLE) {
|
||||||
|
ll.setMinimumHeight((int) (LARGE_MIN_HEIGHT * density));
|
||||||
|
} else {
|
||||||
|
ll.setMinimumHeight((int) (SMALL_MIN_HEIGHT * density));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDistributeEvenly) {
|
||||||
|
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
|
||||||
|
lp.width = 0;
|
||||||
|
lp.weight = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateTabStrip() {
|
||||||
|
final PagerAdapter adapter = mViewPager.getAdapter();
|
||||||
|
final OnClickListener tabClickListener = new TabClickListener();
|
||||||
|
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
View tabView = null;
|
||||||
|
|
||||||
|
TabItemSpec tabItem;
|
||||||
|
if (this.mTabItems != null && this.mTabItems.length > i) {
|
||||||
|
tabItem = this.mTabItems[i];
|
||||||
|
} else {
|
||||||
|
tabItem = new TabItemSpec();
|
||||||
|
tabItem.title = adapter.getPageTitle(i).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
tabView = createDefaultTabView(getContext(), tabItem);
|
||||||
|
|
||||||
|
tabView.setOnClickListener(tabClickListener);
|
||||||
|
String desc = mContentDescriptions.get(i, null);
|
||||||
|
if (desc != null) {
|
||||||
|
tabView.setContentDescription(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabStrip.addView(tabView);
|
||||||
|
if (i == mViewPager.getCurrentItem()) {
|
||||||
|
tabView.setSelected(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentDescription(int i, String desc) {
|
||||||
|
mContentDescriptions.put(i, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
if (mViewPager != null) {
|
||||||
|
scrollToTab(mViewPager.getCurrentItem(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scrollToTab(int tabIndex, int positionOffset) {
|
||||||
|
final int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
View selectedChild = mTabStrip.getChildAt(tabIndex);
|
||||||
|
if (selectedChild != null) {
|
||||||
|
int targetScrollX = selectedChild.getLeft() + positionOffset;
|
||||||
|
|
||||||
|
if (tabIndex > 0 || positionOffset > 0) {
|
||||||
|
// If we're not at the first child and are mid-scroll, make sure
|
||||||
|
// we obey the offset
|
||||||
|
targetScrollX -= mTitleOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(targetScrollX, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||||
|
private int mScrollState;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||||
|
|
||||||
|
View selectedTitle = mTabStrip.getChildAt(position);
|
||||||
|
int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0;
|
||||||
|
scrollToTab(position, extraOffset);
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
mScrollState = state;
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, 0f);
|
||||||
|
scrollToTab(position, 0);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||||
|
mTabStrip.getChildAt(i).setSelected(position == i);
|
||||||
|
}
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageSelected(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TabClickListener implements OnClickListener {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||||
|
if (v == mTabStrip.getChildAt(i)) {
|
||||||
|
mViewPager.setCurrentItem(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
widgets/src/main/java/org/nativescript/widgets/TabStrip.java
Normal file
169
widgets/src/main/java/org/nativescript/widgets/TabStrip.java
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
class TabStrip extends LinearLayout {
|
||||||
|
|
||||||
|
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
|
||||||
|
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||||
|
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
|
||||||
|
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||||
|
|
||||||
|
private final int mBottomBorderThickness;
|
||||||
|
private final Paint mBottomBorderPaint;
|
||||||
|
|
||||||
|
private final int mSelectedIndicatorThickness;
|
||||||
|
private final Paint mSelectedIndicatorPaint;
|
||||||
|
|
||||||
|
private final int mDefaultBottomBorderColor;
|
||||||
|
|
||||||
|
private int mSelectedPosition;
|
||||||
|
private float mSelectionOffset;
|
||||||
|
|
||||||
|
private TabLayout.TabColorizer mCustomTabColorizer;
|
||||||
|
private final SimpleTabColorizer mDefaultTabColorizer;
|
||||||
|
|
||||||
|
TabStrip(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
TabStrip(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
setWillNotDraw(false);
|
||||||
|
|
||||||
|
final float density = getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
|
||||||
|
final int themeForegroundColor = outValue.data;
|
||||||
|
|
||||||
|
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
|
||||||
|
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||||
|
|
||||||
|
mDefaultTabColorizer = new SimpleTabColorizer();
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
|
||||||
|
|
||||||
|
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||||
|
mBottomBorderPaint = new Paint();
|
||||||
|
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
|
||||||
|
|
||||||
|
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||||
|
mSelectedIndicatorPaint = new Paint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCustomTabColorizer(TabLayout.TabColorizer customTabColorizer) {
|
||||||
|
mCustomTabColorizer = customTabColorizer;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedIndicatorColors(int... colors) {
|
||||||
|
// Make sure that the custom colorizer is removed
|
||||||
|
mCustomTabColorizer = null;
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(colors);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||||
|
mSelectedPosition = position;
|
||||||
|
mSelectionOffset = positionOffset;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
final int height = getHeight();
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
final TabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
|
||||||
|
? mCustomTabColorizer
|
||||||
|
: mDefaultTabColorizer;
|
||||||
|
|
||||||
|
// Thick colored underline below the current selection
|
||||||
|
if (childCount > 0) {
|
||||||
|
View selectedTitle = getChildAt(mSelectedPosition);
|
||||||
|
int left = selectedTitle.getLeft();
|
||||||
|
int right = selectedTitle.getRight();
|
||||||
|
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
|
||||||
|
|
||||||
|
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
|
||||||
|
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
|
||||||
|
if (color != nextColor) {
|
||||||
|
color = blendColors(nextColor, color, mSelectionOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the selection partway between the tabs
|
||||||
|
View nextTitle = getChildAt(mSelectedPosition + 1);
|
||||||
|
left = (int) (mSelectionOffset * nextTitle.getLeft() +
|
||||||
|
(1.0f - mSelectionOffset) * left);
|
||||||
|
right = (int) (mSelectionOffset * nextTitle.getRight() +
|
||||||
|
(1.0f - mSelectionOffset) * right);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSelectedIndicatorPaint.setColor(color);
|
||||||
|
|
||||||
|
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
|
||||||
|
height, mSelectedIndicatorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin underline along the entire bottom edge
|
||||||
|
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||||
|
*/
|
||||||
|
private static int setColorAlpha(int color, byte alpha) {
|
||||||
|
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||||
|
*
|
||||||
|
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||||
|
* 0.0 will return {@code color2}.
|
||||||
|
*/
|
||||||
|
private static int blendColors(int color1, int color2, float ratio) {
|
||||||
|
final float inverseRation = 1f - ratio;
|
||||||
|
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||||
|
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||||
|
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||||
|
return Color.rgb((int) r, (int) g, (int) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SimpleTabColorizer implements TabLayout.TabColorizer {
|
||||||
|
private int[] mIndicatorColors;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getIndicatorColor(int position) {
|
||||||
|
return mIndicatorColors[position % mIndicatorColors.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIndicatorColors(int... colors) {
|
||||||
|
mIndicatorColors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, 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).
|
||||||
|
// check the previous line - this.setPadding(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
|
||||||
|
// this.contentMeasuredWidth += this.getPaddingLeft() + this.getPaddingRight();
|
||||||
|
// this.contentMeasuredHeight += this.getPaddingTop() + this.getPaddingBottom();
|
||||||
|
|
||||||
|
// Check against our minimum height
|
||||||
|
this.contentMeasuredWidth = Math.max(this.contentMeasuredWidth, this.getSuggestedMinimumWidth());
|
||||||
|
this.contentMeasuredHeight = Math.max(this.contentMeasuredHeight, this.getSuggestedMinimumHeight());
|
||||||
|
|
||||||
|
int widthSizeAndState = resolveSizeAndState(this.contentMeasuredWidth, widthMeasureSpec, 0);
|
||||||
|
int heightSizeAndState = resolveSizeAndState(this.contentMeasuredHeight, heightMeasureSpec, 0);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
int childHeight = 0;
|
||||||
|
if (this.getChildCount() > 0) {
|
||||||
|
View child = this.getChildAt(0);
|
||||||
|
childHeight = child.getMeasuredHeight();
|
||||||
|
|
||||||
|
int width = right - left;
|
||||||
|
int height = bottom - top;
|
||||||
|
|
||||||
|
this.scrollableLength = this.contentMeasuredHeight - height;
|
||||||
|
CommonLayoutParams.layoutChild(child, 0, 0, width, Math.max(this.contentMeasuredHeight, height));
|
||||||
|
this.scrollableLength = Math.max(0, this.scrollableLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mIsLayoutDirty = false;
|
||||||
|
// Give a child focus if it needs it
|
||||||
|
if (this.mChildToScrollTo != null && HorizontalScrollView.isViewDescendantOf(this.mChildToScrollTo, this)) {
|
||||||
|
this.scrollToChild(this.mChildToScrollTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mChildToScrollTo = null;
|
||||||
|
|
||||||
|
int scrollX = this.getScrollX();
|
||||||
|
int scrollY = this.getScrollY();
|
||||||
|
if (this.isFirstLayout) {
|
||||||
|
this.isFirstLayout = false;
|
||||||
|
|
||||||
|
final int scrollRange = Math.max(0, childHeight - (bottom - top - this.getPaddingTop() - this.getPaddingBottom()));
|
||||||
|
if (this.mSavedState != null) {
|
||||||
|
scrollY = mSavedState.scrollPosition;
|
||||||
|
mSavedState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't forget to clamp
|
||||||
|
if (scrollY > scrollRange) {
|
||||||
|
scrollY = scrollRange;
|
||||||
|
} else if (scrollY < 0) {
|
||||||
|
scrollY = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling this with the present values causes it to re-claim them
|
||||||
|
this.scrollTo(scrollX, scrollY);
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
237
widgets/src/main/java/org/nativescript/widgets/WrapLayout.java
Normal file
237
widgets/src/main/java/org/nativescript/widgets/WrapLayout.java
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hhristov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class WrapLayout extends LayoutBase {
|
||||||
|
|
||||||
|
private int _itemWidth = -1;
|
||||||
|
private int _itemHeight = -1;
|
||||||
|
private Orientation _orientation = Orientation.horzontal;
|
||||||
|
private ArrayList<Integer> _lengths = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
public WrapLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Orientation getOrientation() {
|
||||||
|
return this._orientation;
|
||||||
|
}
|
||||||
|
public void setOrientation(Orientation value) {
|
||||||
|
this._orientation = value;
|
||||||
|
this.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemWidth() {
|
||||||
|
return this._itemWidth;
|
||||||
|
}
|
||||||
|
public void setItemWidth(int value) {
|
||||||
|
this._itemWidth = value;
|
||||||
|
this.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemHeight() {
|
||||||
|
return this._itemHeight;
|
||||||
|
}
|
||||||
|
public void setItemHeight(int value) {
|
||||||
|
this._itemHeight = value;
|
||||||
|
this.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getViewMeasureSpec(int parentMode, int parentLength, int itemLength) {
|
||||||
|
if (itemLength > 0) {
|
||||||
|
return MeasureSpec.makeMeasureSpec(itemLength, MeasureSpec.EXACTLY);
|
||||||
|
}
|
||||||
|
else if (parentMode == MeasureSpec.UNSPECIFIED) {
|
||||||
|
return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return MeasureSpec.makeMeasureSpec(parentLength, MeasureSpec.AT_MOST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
|
int measureWidth = 0;
|
||||||
|
int measureHeight = 0;
|
||||||
|
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||||
|
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
boolean isVertical = this._orientation == Orientation.vertical;
|
||||||
|
int verticalPadding = this.getPaddingTop() + this.getPaddingBottom();
|
||||||
|
int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight();
|
||||||
|
|
||||||
|
int childWidthMeasureSpec = getViewMeasureSpec(widthMode, width, this._itemWidth);
|
||||||
|
int childHeightMeasureSpec = getViewMeasureSpec(heightMode, height, this._itemHeight);
|
||||||
|
|
||||||
|
int remainingWidth = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : width - horizontalPadding;
|
||||||
|
int remainingHeight = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : height - verticalPadding;
|
||||||
|
|
||||||
|
int count = this.getChildCount();
|
||||||
|
|
||||||
|
this._lengths.clear();
|
||||||
|
int rowOrColumn = 0;
|
||||||
|
int maxLength = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
View child = this.getChildAt(i);
|
||||||
|
if (child.getVisibility() == View.GONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec);
|
||||||
|
final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(child);
|
||||||
|
final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(child);
|
||||||
|
|
||||||
|
if (isVertical) {
|
||||||
|
if (childMeasuredHeight > remainingHeight) {
|
||||||
|
rowOrColumn++;
|
||||||
|
maxLength = Math.max(maxLength, measureHeight);
|
||||||
|
measureHeight = childMeasuredHeight;
|
||||||
|
remainingWidth = height - childMeasuredHeight;
|
||||||
|
this._lengths.add(rowOrColumn, childMeasuredWidth);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
remainingHeight -= childMeasuredHeight;
|
||||||
|
measureHeight += childMeasuredHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (childMeasuredWidth > remainingWidth) {
|
||||||
|
rowOrColumn++;
|
||||||
|
maxLength = Math.max(maxLength, measureWidth);
|
||||||
|
measureWidth = childMeasuredWidth;
|
||||||
|
remainingWidth = width - childMeasuredWidth;
|
||||||
|
this._lengths.add(rowOrColumn, childMeasuredHeight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
remainingWidth -= childMeasuredWidth;
|
||||||
|
measureWidth += childMeasuredWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._lengths.size() <= rowOrColumn) {
|
||||||
|
this._lengths.add(rowOrColumn, isVertical ? childMeasuredWidth : childMeasuredHeight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._lengths.set(rowOrColumn, Math.max(this._lengths.get(rowOrColumn), isVertical ? childMeasuredWidth : childMeasuredHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count = this._lengths.size();
|
||||||
|
if (isVertical) {
|
||||||
|
measureHeight = Math.max(maxLength, measureHeight);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
measureWidth += this._lengths.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
measureWidth = Math.max(maxLength, measureWidth);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
measureHeight += this._lengths.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in our padding
|
||||||
|
measureWidth += horizontalPadding;
|
||||||
|
measureHeight += verticalPadding;
|
||||||
|
|
||||||
|
// Check against our minimum sizes
|
||||||
|
measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth());
|
||||||
|
measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight());
|
||||||
|
|
||||||
|
int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
|
||||||
|
int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
boolean isVertical = this._orientation == Orientation.vertical;
|
||||||
|
int paddingLeft = this.getPaddingLeft();
|
||||||
|
int paddingRight = this.getPaddingRight();
|
||||||
|
int paddingTop = this.getPaddingTop();
|
||||||
|
int paddingBottom = this.getPaddingBottom();
|
||||||
|
|
||||||
|
int childLeft = paddingLeft;
|
||||||
|
int childTop = paddingTop;
|
||||||
|
int childrenLength = isVertical ? bottom - top - paddingBottom : right - left - paddingRight;
|
||||||
|
|
||||||
|
int rowOrColumn = 0;
|
||||||
|
int count = this.getChildCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
View child = this.getChildAt(i);
|
||||||
|
if (child.getVisibility() == View.GONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add margins because layoutChild will subtract them.
|
||||||
|
int childWidth = CommonLayoutParams.getDesiredWidth(child);
|
||||||
|
int childHeight = CommonLayoutParams.getDesiredHeight(child);
|
||||||
|
|
||||||
|
int length = this._lengths.get(rowOrColumn);
|
||||||
|
if (isVertical) {
|
||||||
|
childWidth = length;
|
||||||
|
childHeight = this._itemHeight > 0 ? this._itemHeight : childHeight;
|
||||||
|
if (childTop + childHeight > childrenLength) {
|
||||||
|
// Move to top.
|
||||||
|
childTop = paddingTop;
|
||||||
|
|
||||||
|
// Move to right with current column width.
|
||||||
|
childLeft += length;
|
||||||
|
|
||||||
|
// Move to next column.
|
||||||
|
rowOrColumn++;
|
||||||
|
|
||||||
|
// Take current column width.
|
||||||
|
childWidth = length = this._lengths.get(rowOrColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
childWidth = this._itemWidth > 0 ? this._itemWidth : childWidth;
|
||||||
|
childHeight = length;
|
||||||
|
if (childLeft + childWidth > childrenLength) {
|
||||||
|
// Move to left.
|
||||||
|
childLeft = paddingLeft;
|
||||||
|
|
||||||
|
// Move to bottom with current row height.
|
||||||
|
childTop += length;
|
||||||
|
|
||||||
|
// Move to next column.
|
||||||
|
rowOrColumn++;
|
||||||
|
|
||||||
|
// Take current row height.
|
||||||
|
childHeight = length = this._lengths.get(rowOrColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.layoutChild(child, childLeft, childTop, childLeft + childWidth, childTop + childHeight);
|
||||||
|
|
||||||
|
if (isVertical) {
|
||||||
|
// Move next child Top position to bottom.
|
||||||
|
childTop += childHeight;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Move next child Left position to right.
|
||||||
|
childLeft += childWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonLayoutParams.restoreOriginalParams(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
widgets/src/main/res/values/strings.xml
Normal file
3
widgets/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Widgets</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() throws Exception {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user