| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,43 @@ | |||
| 1 | + Label, Button { | ||
| 2 | + text-align: center; | ||
| 3 | + padding: 10; | ||
| 4 | + margin: 16; | ||
| 5 | + background-color: #bbb; | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + .elevation-0 { | ||
| 9 | + android-elevation: 0; | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + .elevation-2 { | ||
| 13 | + android-elevation: 2; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + .elevation-4 { | ||
| 17 | + android-elevation: 4; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + .elevation-4:highlighted { | ||
| 21 | + android-elevation: 2; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + .elevation-8 { | ||
| 25 | + android-elevation: 8; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + .elevation-10 { | ||
| 29 | + android-elevation: 10; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + .pressed-z-10 { | ||
| 33 | + android-dynamic-elevation-offset: 10; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + .round { | ||
| 37 | + color: #fff; | ||
| 38 | + background-color: #ff1744; | ||
| 39 | + border-radius: 50%; /* TODO kills elevation */ | ||
| 40 | + width: 80; | ||
| 41 | + height: 80; | ||
| 42 | + android-elevation: 8; | ||
| 43 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,20 @@ | |||
| 1 | + import { Button } from "tns-core-modules/ui/button/button"; | ||
| 2 | + import { EventData } from "tns-core-modules/ui/page/page"; | ||
| 3 | + | ||
| 4 | + const states = [ | ||
| 5 | + { class: "", text: "default elevation" }, | ||
| 6 | + { class: "elevation-10", text: "elevetion 10" }, | ||
| 7 | + { class: "elevation-10 pressed-z-10", text: "elevetion 10 pressed-z 10" }, | ||
| 8 | + { class: "elevation-0", text: "elevetion 0" }, | ||
| 9 | + ] | ||
| 10 | + let currentState = 0; | ||
| 11 | + | ||
| 12 | + export function buttonTap(args: EventData) { | ||
| 13 | + let btn: Button = args.object as Button; | ||
| 14 | + currentState++; | ||
| 15 | + if (currentState >= states.length) { | ||
| 16 | + currentState = 0; | ||
| 17 | + } | ||
| 18 | + btn.className = states[currentState].class; | ||
| 19 | + btn.text = states[currentState].text; | ||
| 20 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,25 @@ | |||
| 1 | + <Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded"> | ||
| 2 | + <ScrollView> | ||
| 3 | + <StackLayout backgroundColor="aliceblue" androidElevation="2" padding="10" margin="20"> | ||
| 4 | + <Button text="default elevation" tap="buttonTap"/> | ||
| 5 | + | ||
| 6 | + <Button class="elevation-0" text="elevation 0"/> | ||
| 7 | + | ||
| 8 | + <Label class="elevation-2" text="Label with elevation 2"/> | ||
| 9 | + | ||
| 10 | + <StackLayout androidElevation="4" backgroundColor="#ff1744" horizontalAlignment="center" margin="20"> | ||
| 11 | + <Button class="elevation-2" text="elevation 2"/> | ||
| 12 | + <Button class="elevation-4" text="elevation 4"/> | ||
| 13 | + </StackLayout> | ||
| 14 | + | ||
| 15 | + <StackLayout orientation="horizontal" horizontalAlignment="center"> | ||
| 16 | + <Button class="round" androidElevation="4" text="el. 4"/> | ||
| 17 | + <Button class="elevation-8 round" text="el. 8"/> | ||
| 18 | + </StackLayout> | ||
| 19 | + | ||
| 20 | + <Label class="elevation-8" text="elevation 8"/> | ||
| 21 | + <Button class="elevation-8" text="elevation 8, with radius" borderRadius="4"/> | ||
| 22 | + <Button class="elevation-10" text="elevation 10"/> | ||
| 23 | + </StackLayout> | ||
| 24 | + </ScrollView> | ||
| 25 | + </Page> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,6 +38,7 @@ export function loadExamples() { | |||
| 38 | 38 | examples.set("margins-paddings-with-percentage", "css/margins-paddings-with-percentage"); | |
| 39 | 39 | examples.set("padding-and-border", "css/padding-and-border"); | |
| 40 | 40 | examples.set("combinators", "css/combinators"); | |
| 41 | + examples.set("elevation", "css/elevation"); | ||
| 41 | 42 | examples.set("styled-formatted-text", "css/styled-formatted-text"); | |
| 42 | 43 | examples.set("non-uniform-radius", "css/non-uniform-radius"); | |
| 43 | 44 | examples.set("missing-background-image", "css/missing-background-image"); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,11 +3,16 @@ | |||
| 3 | 3 | paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, | |
| 4 | 4 | Length, zIndexProperty, textAlignmentProperty, TextAlignment | |
| 5 | 5 | } from "./button-common"; | |
| 6 | + import { androidElevationProperty, androidDynamicElevationOffsetProperty } from "../styling/style-properties"; | ||
| 6 | 7 | import { profile } from "../../profiling"; | |
| 7 | 8 | import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures"; | |
| 9 | + import { device } from "../../platform"; | ||
| 10 | + import lazy from "../../utils/lazy"; | ||
| 8 | 11 | ||
| 9 | 12 | export * from "./button-common"; | |
| 10 | 13 | ||
| 14 | + const sdkVersion = lazy(() => parseInt(device.sdkVersion)); | ||
| 15 | + | ||
| 11 | 16 | interface ClickListener { | |
| 12 | 17 | new(owner: Button): android.view.View.OnClickListener; | |
| 13 | 18 | } | |
@@ -146,9 +151,28 @@ export class Button extends ButtonBase { | |||
| 146 | 151 | org.nativescript.widgets.ViewHelper.setZIndex(this.nativeViewProtected, value); | |
| 147 | 152 | } | |
| 148 | 153 | ||
| 154 | + [androidElevationProperty.getDefault](): number { | ||
| 155 | + if (sdkVersion() < 21) { | ||
| 156 | + return 0; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + // NOTE: Button widget has StateListAnimator that defines the elevation value and | ||
| 160 | + // at the time of the getDefault() query the animator is not applied yet so we | ||
| 161 | + // return the hardcoded @dimen/button_elevation_material value 2dp here instead | ||
| 162 | + return 2; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + [androidDynamicElevationOffsetProperty.getDefault](): number { | ||
| 166 | + if (sdkVersion() < 21) { | ||
| 167 | + return 0; | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + return 4; // 4dp @dimen/button_pressed_z_material | ||
| 171 | + } | ||
| 172 | + | ||
| 149 | 173 | [textAlignmentProperty.setNative](value: TextAlignment) { | |
| 150 | 174 | // Button initial value is center. | |
| 151 | 175 | const newValue = value === "initial" ? "center" : value; | |
| 152 | 176 | super[textAlignmentProperty.setNative](newValue); | |
| 153 | 177 | } | |
| 154 | - } | ||
| 178 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1018,7 +1018,7 @@ export const classNameProperty = new Property<ViewBase, string>({ | |||
| 1018 | 1018 | valueChanged(view: ViewBase, oldValue: string, newValue: string) { | |
| 1019 | 1019 | let classes = view.cssClasses; | |
| 1020 | 1020 | classes.clear(); | |
| 1021 | - if (typeof newValue === "string") { | ||
| 1021 | + if (typeof newValue === "string" && newValue !== "") { | ||
| 1022 | 1022 | newValue.split(" ").forEach(c => classes.add(c)); | |
| 1023 | 1023 | } | |
| 1024 | 1024 | view._onCssStateChange(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -683,6 +683,20 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { | |||
| 683 | 683 | this.style.scaleY = value; | |
| 684 | 684 | } | |
| 685 | 685 | ||
| 686 | + get androidElevation(): number { | ||
| 687 | + return this.style.androidElevation; | ||
| 688 | + } | ||
| 689 | + set androidElevation(value: number) { | ||
| 690 | + this.style.androidElevation = value; | ||
| 691 | + } | ||
| 692 | + | ||
| 693 | + get androidDynamicElevationOffset(): number { | ||
| 694 | + return this.style.androidDynamicElevationOffset; | ||
| 695 | + } | ||
| 696 | + set androidDynamicElevationOffset(value: number) { | ||
| 697 | + this.style.androidDynamicElevationOffset = value; | ||
| 698 | + } | ||
| 699 | + | ||
| 686 | 700 | //END Style property shortcuts | |
| 687 | 701 | ||
| 688 | 702 | public automationText: string; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,19 +15,27 @@ import { | |||
| 15 | 15 | minWidthProperty, minHeightProperty, widthProperty, heightProperty, | |
| 16 | 16 | marginLeftProperty, marginTopProperty, marginRightProperty, marginBottomProperty, | |
| 17 | 17 | rotateProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, | |
| 18 | - zIndexProperty, backgroundInternalProperty | ||
| 18 | + zIndexProperty, backgroundInternalProperty, androidElevationProperty, androidDynamicElevationOffsetProperty | ||
| 19 | 19 | } from "../../styling/style-properties"; | |
| 20 | 20 | ||
| 21 | 21 | import { Background, ad as androidBackground } from "../../styling/background"; | |
| 22 | 22 | import { profile } from "../../../profiling"; | |
| 23 | 23 | import { topmost } from "../../frame/frame-stack"; | |
| 24 | 24 | import { AndroidActivityBackPressedEventData, android as androidApp } from "../../../application"; | |
| 25 | + import { device } from "../../../platform"; | ||
| 26 | + import lazy from "../../../utils/lazy"; | ||
| 25 | 27 | ||
| 26 | 28 | export * from "./view-common"; | |
| 27 | 29 | ||
| 28 | 30 | const DOMID = "_domId"; | |
| 29 | 31 | const androidBackPressedEvent = "androidBackPressed"; | |
| 30 | 32 | ||
| 33 | + const shortAnimTime = 17694720; // android.R.integer.config_shortAnimTime | ||
| 34 | + const statePressed = 16842919; // android.R.attr.state_pressed | ||
| 35 | + const stateEnabled = 16842910; // android.R.attr.state_enabled | ||
| 36 | + | ||
| 37 | + const sdkVersion = lazy(() => parseInt(device.sdkVersion)); | ||
| 38 | + | ||
| 31 | 39 | const modalMap = new Map<number, DialogOptions>(); | |
| 32 | 40 | ||
| 33 | 41 | let TouchListener: TouchListener; | |
@@ -255,6 +263,8 @@ export class View extends ViewCommon { | |||
| 255 | 263 | private layoutChangeListener: android.view.View.OnLayoutChangeListener; | |
| 256 | 264 | private _manager: android.support.v4.app.FragmentManager; | |
| 257 | 265 | private _rootManager: android.support.v4.app.FragmentManager; | |
| 266 | + private _originalElevation: number; | ||
| 267 | + private _originalStateListAnimator: any; /* android.animation.StateListAnimator; */ | ||
| 258 | 268 | ||
| 259 | 269 | nativeViewProtected: android.view.View; | |
| 260 | 270 | ||
@@ -709,6 +719,74 @@ export class View extends ViewCommon { | |||
| 709 | 719 | this.nativeViewProtected.setAlpha(float(value)); | |
| 710 | 720 | } | |
| 711 | 721 | ||
| 722 | + [androidElevationProperty.getDefault](): number { | ||
| 723 | + if (sdkVersion() < 21) { | ||
| 724 | + return 0; | ||
| 725 | + } | ||
| 726 | + | ||
| 727 | + // NOTE: overriden in Button implementation as for widgets with StateListAnimator (Button) | ||
| 728 | + // nativeView.getElevation() returns 0 at the time of the getDefault() query | ||
| 729 | + return layout.toDeviceIndependentPixels((<any>this.nativeViewProtected).getElevation()); | ||
| 730 | + } | ||
| 731 | + [androidElevationProperty.setNative](value: number) { | ||
| 732 | + if (sdkVersion() < 21) { | ||
| 733 | + return; | ||
| 734 | + } | ||
| 735 | + | ||
| 736 | + this.refreshStateListAnimator(); | ||
| 737 | + } | ||
| 738 | + | ||
| 739 | + [androidDynamicElevationOffsetProperty.getDefault](): number { | ||
| 740 | + return 0; | ||
| 741 | + } | ||
| 742 | + [androidDynamicElevationOffsetProperty.setNative](value: number) { | ||
| 743 | + if (sdkVersion() < 21) { | ||
| 744 | + return; | ||
| 745 | + } | ||
| 746 | + | ||
| 747 | + this.refreshStateListAnimator(); | ||
| 748 | + } | ||
| 749 | + | ||
| 750 | + private refreshStateListAnimator() { | ||
| 751 | + const nativeView: any = this.nativeViewProtected; | ||
| 752 | + | ||
| 753 | + const ObjectAnimator = android.animation.ObjectAnimator; | ||
| 754 | + const AnimatorSet = android.animation.AnimatorSet; | ||
| 755 | + | ||
| 756 | + const duration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2; | ||
| 757 | + const elevation = layout.toDevicePixels(this.androidElevation || 0); | ||
| 758 | + const z = layout.toDevicePixels(0); | ||
| 759 | + const pressedZ = layout.toDevicePixels(this.androidDynamicElevationOffset || 0); | ||
| 760 | + | ||
| 761 | + const pressedSet = new AnimatorSet(); | ||
| 762 | + pressedSet.playTogether(java.util.Arrays.asList([ | ||
| 763 | + ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ]) | ||
| 764 | + .setDuration(duration), | ||
| 765 | + ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) | ||
| 766 | + .setDuration(0), | ||
| 767 | + ])); | ||
| 768 | + | ||
| 769 | + const notPressedSet = new AnimatorSet(); | ||
| 770 | + notPressedSet.playTogether(java.util.Arrays.asList([ | ||
| 771 | + ObjectAnimator.ofFloat(nativeView, "translationZ", [z]) | ||
| 772 | + .setDuration(duration), | ||
| 773 | + ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) | ||
| 774 | + .setDuration(0), | ||
| 775 | + ])); | ||
| 776 | + | ||
| 777 | + const defaultSet = new AnimatorSet(); | ||
| 778 | + defaultSet.playTogether(java.util.Arrays.asList([ | ||
| 779 | + ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0), | ||
| 780 | + ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0), | ||
| 781 | + ])); | ||
| 782 | + | ||
| 783 | + const stateListAnimator = new (<any>android.animation).StateListAnimator(); | ||
| 784 | + stateListAnimator.addState([statePressed, stateEnabled], pressedSet); | ||
| 785 | + stateListAnimator.addState([stateEnabled], notPressedSet); | ||
| 786 | + stateListAnimator.addState([], defaultSet); | ||
| 787 | + nativeView.setStateListAnimator(stateListAnimator); | ||
| 788 | + } | ||
| 789 | + | ||
| 712 | 790 | [horizontalAlignmentProperty.getDefault](): HorizontalAlignment { | |
| 713 | 791 | return <HorizontalAlignment>org.nativescript.widgets.ViewHelper.getHorizontalAlignment(this.nativeViewProtected); | |
| 714 | 792 | } | |
@@ -946,7 +1024,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO | |||
| 946 | 1024 | const { getter, setter, auto = 0 } = options; | |
| 947 | 1025 | let setPixels, getPixels, setPercent; | |
| 948 | 1026 | if (getter) { | |
| 949 | - View.prototype[getter] = function (this: View): PercentLength { | ||
| 1027 | + View.prototype[getter] = function(this: View): PercentLength { | ||
| 950 | 1028 | if (options) { | |
| 951 | 1029 | setPixels = options.setPixels; | |
| 952 | 1030 | getPixels = options.getPixels; | |
@@ -962,7 +1040,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO | |||
| 962 | 1040 | } | |
| 963 | 1041 | } | |
| 964 | 1042 | if (setter) { | |
| 965 | - View.prototype[setter] = function (this: View, length: PercentLength) { | ||
| 1043 | + View.prototype[setter] = function(this: View, length: PercentLength) { | ||
| 966 | 1044 | if (options) { | |
| 967 | 1045 | setPixels = options.setPixels; | |
| 968 | 1046 | getPixels = options.getPixels; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -224,6 +224,16 @@ export abstract class View extends ViewBase { | |||
| 224 | 224 | */ | |
| 225 | 225 | color: Color; | |
| 226 | 226 | ||
| 227 | + /** | ||
| 228 | + * Gets or sets the elevation of the android view. | ||
| 229 | + */ | ||
| 230 | + androidElevation: number; | ||
| 231 | + | ||
| 232 | + /** | ||
| 233 | + * Gets or sets the dynamic elevation offset of the android view. | ||
| 234 | + */ | ||
| 235 | + androidDynamicElevationOffset: number; | ||
| 236 | + | ||
| 227 | 237 | /** | |
| 228 | 238 | * Gets or sets the background style property. | |
| 229 | 239 | */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1099,3 +1099,9 @@ export const visibilityProperty = new CssProperty<Style, Visibility>({ | |||
| 1099 | 1099 | } | |
| 1100 | 1100 | }); | |
| 1101 | 1101 | visibilityProperty.register(Style); | |
| 1102 | + | ||
| 1103 | + export const androidElevationProperty = new CssProperty<Style, number>({ name: "androidElevation", cssName: "android-elevation", valueConverter: parseFloat }); | ||
| 1104 | + androidElevationProperty.register(Style); | ||
| 1105 | + | ||
| 1106 | + export const androidDynamicElevationOffsetProperty = new CssProperty<Style, number>({ name: "androidDynamicElevationOffset", cssName: "android-dynamic-elevation-offset", valueConverter: parseFloat }); | ||
| 1107 | + androidDynamicElevationOffsetProperty.register(Style); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments