Browse Source

Search android (#432)

* Move event emmiter logic out of Application

* Implement searchView

Usage:
```js
rightButtons: [
      {
        id: 'searchView',
        label: 'Search',
        hint: 'Search...'
      }
    ]
```

* Translucent TitleBar

* Collapse TopBar

* Less noticeable translucent effect

* Implement ScrollViewDetector

* Collapse on fling

* Collapse on touch up

Complete collapse on touch up instead of fling
Guy Carmeli 8 years ago
parent
commit
c10f41ff0e
47 changed files with 826 additions and 259 deletions
  1. 7
    30
      android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java
  2. 41
    0
      android/app/src/main/java/com/reactnativenavigation/bridge/EventEmitter.java
  3. 1
    1
      android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java
  4. 1
    1
      android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java
  5. 10
    1
      android/app/src/main/java/com/reactnativenavigation/params/CollapsingTopBarParams.java
  6. 2
    0
      android/app/src/main/java/com/reactnativenavigation/params/StyleParams.java
  7. 1
    0
      android/app/src/main/java/com/reactnativenavigation/params/TitleBarButtonParams.java
  8. 13
    9
      android/app/src/main/java/com/reactnativenavigation/params/parsers/CollapsingTopBarParamsParser.java
  9. 6
    6
      android/app/src/main/java/com/reactnativenavigation/params/parsers/Parser.java
  10. 11
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/StyleParamsParser.java
  11. 5
    3
      android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarButtonParamsParser.java
  12. 41
    22
      android/app/src/main/java/com/reactnativenavigation/screens/CollapsingSingleScreen.java
  13. 1
    1
      android/app/src/main/java/com/reactnativenavigation/screens/ContentViewPagerAdapter.java
  14. 2
    6
      android/app/src/main/java/com/reactnativenavigation/screens/ScreenStack.java
  15. 36
    0
      android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  16. 18
    0
      android/app/src/main/java/com/reactnativenavigation/views/ButtonFactory.java
  17. 79
    0
      android/app/src/main/java/com/reactnativenavigation/views/CollapsingContentView.java
  18. 1
    42
      android/app/src/main/java/com/reactnativenavigation/views/ContentView.java
  19. 1
    1
      android/app/src/main/java/com/reactnativenavigation/views/ContextualMenu.java
  20. 3
    3
      android/app/src/main/java/com/reactnativenavigation/views/FloatingActionButtonCoordinator.java
  21. 9
    9
      android/app/src/main/java/com/reactnativenavigation/views/LeftButton.java
  22. 5
    5
      android/app/src/main/java/com/reactnativenavigation/views/Snakbar.java
  23. 8
    1
      android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java
  24. 4
    4
      android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  25. 156
    0
      android/app/src/main/java/com/reactnativenavigation/views/TitleBarSearchButton.java
  26. 0
    1
      android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  27. 36
    0
      android/app/src/main/java/com/reactnativenavigation/views/TranslucentTitleBarBackground.java
  28. 17
    5
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseAmount.java
  29. 99
    20
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseCalculator.java
  30. 38
    14
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingTopBar.java
  31. 5
    0
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/OnFlingListener.java
  32. 5
    0
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/OnScrollListener.java
  33. 0
    50
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollDirection.java
  34. 13
    13
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollListener.java
  35. 18
    11
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollViewDelegate.java
  36. 57
    0
      android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ViewCollapser.java
  37. 64
    0
      android/app/src/main/java/com/reactnativenavigation/views/utils/ScrollViewDetector.java
  38. BIN
      android/app/src/main/res/drawable-hdpi/ic_action_name.png
  39. BIN
      android/app/src/main/res/drawable-hdpi/search.png
  40. BIN
      android/app/src/main/res/drawable-mdpi/ic_action_name.png
  41. BIN
      android/app/src/main/res/drawable-mdpi/search.png
  42. BIN
      android/app/src/main/res/drawable-xhdpi/ic_action_name.png
  43. BIN
      android/app/src/main/res/drawable-xhdpi/search.png
  44. BIN
      android/app/src/main/res/drawable-xxhdpi/ic_action_name.png
  45. BIN
      android/app/src/main/res/drawable-xxhdpi/search.png
  46. 10
    0
      android/app/src/main/res/menu/search_item.xml
  47. 2
    0
      src/deprecated/platformSpecificDeprecated.android.js

+ 7
- 30
android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java View File

@@ -8,7 +8,7 @@ import com.facebook.react.ReactApplication;
8 8
 import com.facebook.react.ReactNativeHost;
9 9
 import com.facebook.react.ReactPackage;
10 10
 import com.facebook.react.bridge.ReactContext;
11
-import com.facebook.react.bridge.WritableMap;
11
+import com.reactnativenavigation.bridge.EventEmitter;
12 12
 import com.reactnativenavigation.controllers.ActivityCallbacks;
13 13
 import com.reactnativenavigation.react.NavigationReactGateway;
14 14
 import com.reactnativenavigation.react.ReactGateway;
@@ -20,6 +20,7 @@ public abstract class NavigationApplication extends Application implements React
20 20
     public static NavigationApplication instance;
21 21
 
22 22
     private NavigationReactGateway reactGateway;
23
+    private EventEmitter eventEmitter;
23 24
     private Handler handler;
24 25
     private ActivityCallbacks activityCallbacks;
25 26
 
@@ -29,6 +30,7 @@ public abstract class NavigationApplication extends Application implements React
29 30
         instance = this;
30 31
         handler = new Handler(getMainLooper());
31 32
         reactGateway = new NavigationReactGateway();
33
+        eventEmitter = new EventEmitter(reactGateway);
32 34
         activityCallbacks = new ActivityCallbacks();
33 35
     }
34 36
 
@@ -69,6 +71,10 @@ public abstract class NavigationApplication extends Application implements React
69 71
         return reactGateway.getReactNativeHost();
70 72
     }
71 73
 
74
+    public EventEmitter getEventEmitter() {
75
+        return eventEmitter;
76
+    }
77
+
72 78
     /**
73 79
      * @see ReactNativeHost#getJSMainModuleName()
74 80
      */
@@ -97,33 +103,4 @@ public abstract class NavigationApplication extends Application implements React
97 103
 
98 104
     @Nullable
99 105
     public abstract List<ReactPackage> createAdditionalReactPackages();
100
-
101
-    //TODO move all these navigator junk elsewhere
102
-    public void sendNavigatorEvent(String eventId, String navigatorEventId) {
103
-        if (!isReactContextInitialized()) {
104
-            return;
105
-        }
106
-        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId);
107
-    }
108
-
109
-    public void sendNavigatorEvent(String eventId, String navigatorEventId, WritableMap data) {
110
-        if (!isReactContextInitialized()) {
111
-            return;
112
-        }
113
-        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId, data);
114
-    }
115
-
116
-    public void sendEvent(String eventId, String navigatorEventId) {
117
-        if (!isReactContextInitialized()) {
118
-            return;
119
-        }
120
-        reactGateway.getReactEventEmitter().sendEvent(eventId, navigatorEventId);
121
-    }
122
-
123
-    public void sendNavigatorEvent(String eventId, WritableMap arguments) {
124
-        if (!isReactContextInitialized()) {
125
-            return;
126
-        }
127
-        reactGateway.getReactEventEmitter().sendEvent(eventId, arguments);
128
-    }
129 106
 }

+ 41
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/EventEmitter.java View File

@@ -0,0 +1,41 @@
1
+package com.reactnativenavigation.bridge;
2
+
3
+import com.facebook.react.bridge.WritableMap;
4
+import com.reactnativenavigation.NavigationApplication;
5
+import com.reactnativenavigation.react.ReactGateway;
6
+
7
+public class EventEmitter {
8
+    private ReactGateway reactGateway;
9
+
10
+    public EventEmitter(ReactGateway reactGateway) {
11
+        this.reactGateway = reactGateway;
12
+    }
13
+
14
+    public void sendNavigatorEvent(String eventId, String navigatorEventId) {
15
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
16
+            return;
17
+        }
18
+        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId);
19
+    }
20
+
21
+    public void sendNavigatorEvent(String eventId, String navigatorEventId, WritableMap data) {
22
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
23
+            return;
24
+        }
25
+        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId, data);
26
+    }
27
+
28
+    public void sendEvent(String eventId, String navigatorEventId) {
29
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
30
+            return;
31
+        }
32
+        reactGateway.getReactEventEmitter().sendEvent(eventId, navigatorEventId);
33
+    }
34
+
35
+    public void sendNavigatorEvent(String eventId, WritableMap arguments) {
36
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
37
+            return;
38
+        }
39
+        reactGateway.getReactEventEmitter().sendEvent(eventId, arguments);
40
+    }
41
+}

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java View File

@@ -366,7 +366,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
366 366
             sideMenu.openDrawer();
367 367
         } else {
368 368
             final String navigatorEventId = getCurrentScreenStack().peek().getNavigatorEventId();
369
-            NavigationApplication.instance.sendNavigatorEvent("sideMenu", navigatorEventId);
369
+            NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("sideMenu", navigatorEventId);
370 370
         }
371 371
     }
372 372
 }

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java View File

@@ -236,7 +236,7 @@ public class SingleScreenLayout extends RelativeLayout implements Layout {
236 236
             sideMenu.openDrawer();
237 237
         } else {
238 238
             final String navigatorEventId = stack.peek().getNavigatorEventId();
239
-            NavigationApplication.instance.sendNavigatorEvent("sideMenu", navigatorEventId);
239
+            NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("sideMenu", navigatorEventId);
240 240
         }
241 241
     }
242 242
 }

+ 10
- 1
android/app/src/main/java/com/reactnativenavigation/params/CollapsingTopBarParams.java View File

@@ -1,6 +1,15 @@
1 1
 package com.reactnativenavigation.params;
2 2
 
3
+import android.support.annotation.Nullable;
4
+
3 5
 public class CollapsingTopBarParams {
4
-    public String imageUri;
6
+    public enum CollapseBehaviour {TitleBarHideOnScroll, CollapseTopBar}
7
+
8
+    public @Nullable String imageUri;
5 9
     public StyleParams.Color scrimColor;
10
+    public CollapseBehaviour collapseBehaviour;
11
+
12
+    public boolean hasBackgroundImage() {
13
+        return imageUri != null;
14
+    }
6 15
 }

+ 2
- 0
android/app/src/main/java/com/reactnativenavigation/params/StyleParams.java View File

@@ -47,7 +47,9 @@ public class StyleParams {
47 47
     public boolean drawScreenBelowTopBar;
48 48
 
49 49
     public boolean titleBarHidden;
50
+    public boolean titleBarHideOnScroll;
50 51
     public boolean topBarTransparent;
52
+    public boolean topBarTranslucent;
51 53
     public Color titleBarTitleColor;
52 54
     public Color titleBarSubtitleColor;
53 55
     public Color titleBarButtonColor;

+ 1
- 0
android/app/src/main/java/com/reactnativenavigation/params/TitleBarButtonParams.java View File

@@ -5,6 +5,7 @@ public class TitleBarButtonParams extends BaseTitleBarButtonParams {
5 5
     public StyleParams.Color color;
6 6
     public StyleParams.Color disabledColor;
7 7
     public boolean enabled = true;
8
+    public String hint;
8 9
 
9 10
     public void setColorFromScreenStyle(StyleParams.Color titleBarButtonColor) {
10 11
         if (!color.hasColor() && titleBarButtonColor.hasColor()) {

+ 13
- 9
android/app/src/main/java/com/reactnativenavigation/params/parsers/CollapsingTopBarParamsParser.java View File

@@ -4,35 +4,39 @@ import android.graphics.Color;
4 4
 import android.os.Bundle;
5 5
 
6 6
 import com.reactnativenavigation.params.CollapsingTopBarParams;
7
+import com.reactnativenavigation.params.CollapsingTopBarParams.CollapseBehaviour;
7 8
 import com.reactnativenavigation.params.StyleParams;
8 9
 
9
-public class CollapsingTopBarParamsParser extends Parser {
10
+class CollapsingTopBarParamsParser extends Parser {
10 11
     private Bundle params;
11 12
 
12
-    public CollapsingTopBarParamsParser(Bundle params) {
13
+    CollapsingTopBarParamsParser(Bundle params) {
13 14
         this.params = params;
14 15
     }
15 16
 
16 17
     public CollapsingTopBarParams parse() {
17
-        if (!hasLocalImageResource() && !hasImageUrl()) {
18
+        if (!hasBackgroundImage() && !shouldHideTitleBarOnScroll()) {
18 19
             return null;
19 20
         }
20 21
 
21 22
         CollapsingTopBarParams result = new CollapsingTopBarParams();
22
-        if (hasLocalImageResource()) {
23
-            result.imageUri = params.getString("collapsingToolBarImage");
24
-        } else {
23
+        if (hasBackgroundImage()) {
25 24
             result.imageUri = params.getString("collapsingToolBarImage");
26 25
         }
27 26
         result.scrimColor = getColor(params, "collapsingToolBarCollapsedColor", new StyleParams.Color(Color.WHITE));
27
+        result.collapseBehaviour = getCollapseBehaviour();
28 28
         return result;
29 29
     }
30 30
 
31
-    private boolean hasLocalImageResource() {
31
+    private CollapseBehaviour getCollapseBehaviour() {
32
+        return shouldHideTitleBarOnScroll() ? CollapseBehaviour.TitleBarHideOnScroll : CollapseBehaviour.CollapseTopBar;
33
+    }
34
+
35
+    private boolean hasBackgroundImage() {
32 36
         return params.containsKey("collapsingToolBarImage");
33 37
     }
34 38
 
35
-    private boolean hasImageUrl() {
36
-        return params.containsKey("collapsingToolBarImageUrl");
39
+    private boolean shouldHideTitleBarOnScroll() {
40
+        return params.getBoolean("titleBarHideOnScroll", false);
37 41
     }
38 42
 }

+ 6
- 6
android/app/src/main/java/com/reactnativenavigation/params/parsers/Parser.java View File

@@ -9,27 +9,27 @@ import java.util.Collections;
9 9
 import java.util.List;
10 10
 
11 11
 public class Parser {
12
-    protected static boolean hasKey(Bundle bundle, String key) {
12
+    static boolean hasKey(Bundle bundle, String key) {
13 13
         return bundle.keySet().contains(key);
14 14
     }
15 15
 
16
-    protected static void assertKeyExists(Bundle bundle, String key) {
16
+    static void assertKeyExists(Bundle bundle, String key) {
17 17
         if (!hasKey(bundle, key)) {
18 18
             throw new KeyDoesNotExistsException(key);
19 19
         }
20 20
     }
21 21
 
22
-    public static class KeyDoesNotExistsException extends RuntimeException {
23
-        public KeyDoesNotExistsException(String key) {
22
+    private static class KeyDoesNotExistsException extends RuntimeException {
23
+        KeyDoesNotExistsException(String key) {
24 24
             super(key);
25 25
         }
26 26
     }
27 27
 
28
-    protected interface ParseStrategy<T> {
28
+    interface ParseStrategy<T> {
29 29
         T parse(Bundle params);
30 30
     }
31 31
 
32
-    protected <T> List<T> parseBundle(Bundle params, ParseStrategy<T> strategy) {
32
+    <T> List<T> parseBundle(Bundle params, ParseStrategy<T> strategy) {
33 33
         ArrayList<T> result = new ArrayList<>(Collections.nCopies(params.keySet().size(), (T) null));
34 34
         for (String key : params.keySet()) {
35 35
             result.set(Integer.parseInt(key), strategy.parse(params.getBundle(key)));

+ 11
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/StyleParamsParser.java View File

@@ -29,6 +29,9 @@ public class StyleParamsParser {
29 29
         result.titleBarHidden = getBoolean("titleBarHidden", getDefaultTopBarHidden());
30 30
         result.topBarTransparent = getBoolean("topBarTransparent", getDefaultTopBarHidden());
31 31
         result.titleBarTitleColor = getColor("titleBarTitleColor", getDefaultTitleBarColor());
32
+        result.topBarTranslucent = getBoolean("topBarTranslucent", getDefaultTopBarTranslucent());
33
+        result.titleBarHideOnScroll = getBoolean("titleBarHideOnScroll", getDefaultTitleBarHideOnScroll());
34
+
32 35
         result.titleBarSubtitleColor = getColor("titleBarSubtitleColor", getDefaultSubtitleBarColor());
33 36
         result.titleBarButtonColor = getColor("titleBarButtonColor", getTitleBarButtonColor());
34 37
         result.titleBarDisabledButtonColor = getColor("titleBarDisabledButtonColor", getTitleBarDisabledButtonColor());
@@ -163,6 +166,14 @@ public class StyleParamsParser {
163 166
         return AppStyle.appStyle != null && AppStyle.appStyle.topBarTransparent;
164 167
     }
165 168
 
169
+    private boolean getDefaultTopBarTranslucent() {
170
+        return AppStyle.appStyle != null && AppStyle.appStyle.topBarTranslucent;
171
+    }
172
+
173
+    private boolean getDefaultTitleBarHideOnScroll() {
174
+        return AppStyle.appStyle != null && AppStyle.appStyle.titleBarHideOnScroll;
175
+    }
176
+
166 177
     private StyleParams.Color getDefaultTopBarColor() {
167 178
         return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.topBarColor;
168 179
     }

+ 5
- 3
android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarButtonParamsParser.java View File

@@ -22,18 +22,20 @@ public class TitleBarButtonParamsParser extends Parser {
22 22
     public TitleBarButtonParams parseSingleButton(Bundle bundle) {
23 23
         TitleBarButtonParams result = new TitleBarButtonParams();
24 24
         result.label = bundle.getString("title");
25
-        if (hasKey(bundle,"icon")) {
25
+        if (hasKey(bundle, "icon")) {
26 26
             result.icon = ImageLoader.loadImage(bundle.getString("icon"));
27 27
         }
28 28
         result.color = getColor(bundle, "color", AppStyle.appStyle.titleBarButtonColor);
29
-        result.disabledColor = getColor(bundle, "disabledColor", AppStyle.appStyle.titleBarDisabledButtonColor);
29
+        result.disabledColor =
30
+                getColor(bundle, "disabledColor", AppStyle.appStyle.titleBarDisabledButtonColor);
30 31
         result.showAsAction = parseShowAsAction(bundle.getString("showAsAction"));
31 32
         result.enabled = bundle.getBoolean("enabled", true);
33
+        result.hint = bundle.getString("hint", "");
32 34
         result.eventId = bundle.getString("id");
33 35
         return result;
34 36
     }
35 37
 
36
-    protected BaseTitleBarButtonParams.ShowAsAction parseShowAsAction(String showAsAction) {
38
+    BaseTitleBarButtonParams.ShowAsAction parseShowAsAction(String showAsAction) {
37 39
         if (showAsAction == null) {
38 40
             return BaseTitleBarButtonParams.ShowAsAction.IfRoom;
39 41
         }

+ 41
- 22
android/app/src/main/java/com/reactnativenavigation/screens/CollapsingSingleScreen.java View File

@@ -3,11 +3,15 @@ package com.reactnativenavigation.screens;
3 3
 import android.support.v7.app.AppCompatActivity;
4 4
 import android.widget.ScrollView;
5 5
 
6
+import com.reactnativenavigation.params.CollapsingTopBarParams;
6 7
 import com.reactnativenavigation.params.ScreenParams;
7
-import com.reactnativenavigation.views.ContentView;
8
+import com.reactnativenavigation.views.CollapsingContentView;
8 9
 import com.reactnativenavigation.views.LeftButtonOnClickListener;
10
+import com.reactnativenavigation.views.collapsingToolbar.CollapseAmount;
11
+import com.reactnativenavigation.views.collapsingToolbar.CollapseCalculator;
9 12
 import com.reactnativenavigation.views.collapsingToolbar.CollapsingContentViewMeasurer;
10 13
 import com.reactnativenavigation.views.collapsingToolbar.CollapsingTopBar;
14
+import com.reactnativenavigation.views.collapsingToolbar.OnScrollListener;
11 15
 import com.reactnativenavigation.views.collapsingToolbar.OnScrollViewAddedListener;
12 16
 import com.reactnativenavigation.views.collapsingToolbar.ScrollListener;
13 17
 
@@ -17,44 +21,59 @@ public class CollapsingSingleScreen extends SingleScreen {
17 21
         super(activity, screenParams, titleBarBarBackButtonListener);
18 22
     }
19 23
 
24
+    @Override
25
+    public void destroy() {
26
+        super.destroy();
27
+        ((CollapsingContentView) contentView).destroy();
28
+    }
29
+
20 30
     @Override
21 31
     protected void createTopBar() {
22 32
         final CollapsingTopBar topBar = new CollapsingTopBar(getContext(), styleParams.collapsingTopBarParams);
23
-        topBar.setScrollListener(new ScrollListener(topBar,
24
-                new ScrollListener.OnScrollListener() {
25
-                    @Override
26
-                    public void onScroll(float amount) {
27
-                        contentView.collapse(amount);
28
-                        topBar.collapse(amount);
29
-                    }
30
-                }
31
-        ));
33
+        topBar.setScrollListener(getScrollListener(topBar));
32 34
         this.topBar = topBar;
33 35
     }
34 36
 
35 37
     @Override
36 38
     protected void createContent() {
37
-        contentView = new ContentView(getContext(), screenParams.screenId, screenParams.navigationParams);
38
-        contentView.setViewMeasurer(new CollapsingContentViewMeasurer((CollapsingTopBar) topBar, this));
39
+        contentView = new CollapsingContentView(getContext(), screenParams.screenId, screenParams.navigationParams);
40
+        if (screenParams.styleParams.drawScreenBelowTopBar) {
41
+            contentView.setViewMeasurer(new CollapsingContentViewMeasurer((CollapsingTopBar) topBar, this));
42
+        }
39 43
         setupCollapseDetection((CollapsingTopBar) topBar);
40 44
         addView(contentView, createLayoutParams());
41 45
     }
42 46
 
43 47
     private void setupCollapseDetection(final CollapsingTopBar topBar) {
44
-        contentView.setupCollapseDetection(new ScrollListener(topBar,
45
-                new ScrollListener.OnScrollListener() {
46
-                    @Override
47
-                    public void onScroll(float amount) {
48
-                        contentView.collapse(amount);
49
-                        topBar.collapse(amount);
50
-                    }
51
-                }
52
-        ));
53
-        contentView.setOnScrollViewAddedListener(new OnScrollViewAddedListener() {
48
+        ((CollapsingContentView) contentView).setupCollapseDetection(getScrollListener(topBar), new OnScrollViewAddedListener() {
54 49
             @Override
55 50
             public void onScrollViewAdded(ScrollView scrollView) {
56 51
                 topBar.onScrollViewAdded(scrollView);
57 52
             }
58 53
         });
59 54
     }
55
+
56
+    private ScrollListener getScrollListener(final CollapsingTopBar topBar) {
57
+        return new ScrollListener(new CollapseCalculator(topBar, getCollapseBehaviour()),
58
+                new OnScrollListener() {
59
+                    @Override
60
+                    public void onScroll(CollapseAmount amount) {
61
+                        if (!screenParams.styleParams.titleBarHideOnScroll) {
62
+                            ((CollapsingContentView) contentView).collapse(amount);
63
+                        }
64
+                        topBar.collapse(amount);
65
+                    }
66
+
67
+                    @Override
68
+                    public void onFling(CollapseAmount amount) {
69
+                        topBar.collapse(amount);
70
+                    }
71
+                },
72
+                getCollapseBehaviour()
73
+        );
74
+    }
75
+
76
+    private CollapsingTopBarParams.CollapseBehaviour getCollapseBehaviour() {
77
+        return screenParams.styleParams.collapsingTopBarParams.collapseBehaviour;
78
+    }
60 79
 }

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/screens/ContentViewPagerAdapter.java View File

@@ -67,6 +67,6 @@ class ContentViewPagerAdapter extends PagerAdapter implements ViewPager.OnPageCh
67 67
     private void sendTabSelectedEventToJs() {
68 68
         WritableMap data = Arguments.createMap();
69 69
         String navigatorEventId = contentViews.get(currentPosition).getNavigatorEventId();
70
-        NavigationApplication.instance.sendNavigatorEvent("tabSelected", navigatorEventId, data);
70
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("tabSelected", navigatorEventId, data);
71 71
     }
72 72
 }

+ 2
- 6
android/app/src/main/java/com/reactnativenavigation/screens/ScreenStack.java View File

@@ -176,12 +176,8 @@ public class ScreenStack {
176 176
         stack.clear();
177 177
     }
178 178
 
179
-    public int getStackSize() {
180
-        return stack.size();
181
-    }
182
-
183 179
     public boolean canPop() {
184
-        return getStackSize() > 1 && !isPreviousScreenAttachedToWindow();
180
+        return stack.size() > 1 && !isPreviousScreenAttachedToWindow();
185 181
     }
186 182
 
187 183
     private boolean isPreviousScreenAttachedToWindow() {
@@ -263,7 +259,7 @@ public class ScreenStack {
263 259
     public boolean handleBackPressInJs() {
264 260
         ScreenParams currentScreen = stack.peek().screenParams;
265 261
         if (currentScreen.overrideBackPressInJs) {
266
-            NavigationApplication.instance.sendNavigatorEvent("backPress", currentScreen.getNavigatorEventId());
262
+            NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("backPress", currentScreen.getNavigatorEventId());
267 263
             return true;
268 264
         }
269 265
         return false;

+ 36
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java View File

@@ -5,8 +5,10 @@ import android.graphics.PorterDuff;
5 5
 import android.graphics.PorterDuffColorFilter;
6 6
 import android.graphics.drawable.Drawable;
7 7
 import android.os.Build;
8
+import android.support.annotation.Nullable;
8 9
 import android.util.DisplayMetrics;
9 10
 import android.view.View;
11
+import android.view.ViewGroup;
10 12
 import android.view.ViewTreeObserver;
11 13
 import android.view.WindowManager;
12 14
 
@@ -79,5 +81,39 @@ public class ViewUtils {
79 81
             }
80 82
         }
81 83
     }
84
+
85
+    /**
86
+     * Returns the first instance of clazz in root
87
+     */
88
+    @Nullable public static <T> T findChildByClass(ViewGroup root, Class clazz) {
89
+        for (int i = 0; i < root.getChildCount(); i++) {
90
+            View view = root.getChildAt(i);
91
+            if (clazz.isAssignableFrom(view.getClass())) {
92
+                return (T) view;
93
+            }
94
+
95
+            if (view instanceof ViewGroup) {
96
+                view = findChildByClass((ViewGroup) view, clazz);
97
+                if (view != null && clazz.isAssignableFrom(view.getClass())) {
98
+                    return (T) view;
99
+                }
100
+            }
101
+        }
102
+        return null;
103
+    }
104
+
105
+    public static void performOnChildren(ViewGroup root, PerformOnViewTask task) {
106
+        for (int i = 0; i < root.getChildCount(); i++) {
107
+            View child = root.getChildAt(i);
108
+            if (child instanceof ViewGroup) {
109
+                performOnChildren((ViewGroup) child, task);
110
+            }
111
+            task.runOnView(child);
112
+        }
113
+    }
114
+
115
+    public interface PerformOnViewTask {
116
+        void runOnView(View view);
117
+    }
82 118
 }
83 119
 

+ 18
- 0
android/app/src/main/java/com/reactnativenavigation/views/ButtonFactory.java View File

@@ -0,0 +1,18 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.view.Menu;
4
+import android.view.View;
5
+
6
+import com.reactnativenavigation.params.TitleBarButtonParams;
7
+
8
+class ButtonFactory {
9
+
10
+    public static TitleBarButton create(Menu menu, View parent, TitleBarButtonParams params, String navigatorEventId) {
11
+        switch (params.eventId) {
12
+            case TitleBarSearchButton.BUTTON_ID:
13
+                return new TitleBarSearchButton(menu, parent, params, navigatorEventId);
14
+            default:
15
+                return new TitleBarButton(menu, parent, params, navigatorEventId);
16
+        }
17
+    }
18
+}

+ 79
- 0
android/app/src/main/java/com/reactnativenavigation/views/CollapsingContentView.java View File

@@ -0,0 +1,79 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.support.annotation.Nullable;
5
+import android.view.MotionEvent;
6
+import android.view.View;
7
+
8
+import com.reactnativenavigation.params.NavigationParams;
9
+import com.reactnativenavigation.views.collapsingToolbar.CollapseAmount;
10
+import com.reactnativenavigation.views.collapsingToolbar.CollapsingView;
11
+import com.reactnativenavigation.views.collapsingToolbar.OnScrollViewAddedListener;
12
+import com.reactnativenavigation.views.collapsingToolbar.ScrollListener;
13
+import com.reactnativenavigation.views.collapsingToolbar.ScrollViewDelegate;
14
+import com.reactnativenavigation.views.collapsingToolbar.ViewCollapser;
15
+import com.reactnativenavigation.views.utils.ScrollViewDetector;
16
+
17
+public class CollapsingContentView extends ContentView implements CollapsingView {
18
+
19
+    private @Nullable ScrollViewDelegate scrollViewDelegate;
20
+    private @Nullable ScrollViewDetector scrollViewDetector;
21
+    private final ViewCollapser viewCollapser;
22
+
23
+    public CollapsingContentView(Context context, String screenId, NavigationParams navigationParams) {
24
+        super(context, screenId, navigationParams);
25
+        viewCollapser = new ViewCollapser(this);
26
+    }
27
+
28
+    public void setupCollapseDetection(ScrollListener scrollListener, OnScrollViewAddedListener onScrollViewAddedListener) {
29
+        scrollViewDelegate = new ScrollViewDelegate(scrollListener);
30
+        scrollViewDetector = new ScrollViewDetector(this, onScrollViewAddedListener, scrollViewDelegate);
31
+    }
32
+
33
+    @Override
34
+    public boolean dispatchTouchEvent(MotionEvent ev) {
35
+        if (scrollViewDelegate != null) {
36
+            boolean consumed = scrollViewDelegate.didInterceptTouchEvent(ev);
37
+            if (consumed) {
38
+                return true;
39
+            }
40
+        }
41
+        return super.dispatchTouchEvent(ev);
42
+    }
43
+
44
+    @Override
45
+    public void onViewAdded(final View child) {
46
+        super.onViewAdded(child);
47
+        if (scrollViewDetector != null) {
48
+            scrollViewDetector.detectScrollViewAdded(child);
49
+        }
50
+    }
51
+
52
+    public void collapse(CollapseAmount amount) {
53
+        viewCollapser.collapse(amount);
54
+    }
55
+
56
+    public void destroy() {
57
+        if (scrollViewDelegate != null) {
58
+            scrollViewDelegate.destroy();
59
+        }
60
+        if (scrollViewDetector != null) {
61
+            scrollViewDetector.destroy();
62
+        }
63
+    }
64
+
65
+    @Override
66
+    public float getFinalCollapseValue() {
67
+        return 0;
68
+    }
69
+
70
+    @Override
71
+    public float getCurrentCollapseValue() {
72
+        return 0;
73
+    }
74
+
75
+    @Override
76
+    public View asView() {
77
+        return this;
78
+    }
79
+}

+ 1
- 42
android/app/src/main/java/com/reactnativenavigation/views/ContentView.java View File

@@ -1,19 +1,13 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3 3
 import android.content.Context;
4
-import android.support.annotation.Nullable;
5
-import android.view.MotionEvent;
6 4
 import android.view.View;
7
-import android.widget.ScrollView;
8 5
 
9 6
 import com.facebook.react.ReactRootView;
10 7
 import com.reactnativenavigation.NavigationApplication;
11 8
 import com.reactnativenavigation.params.NavigationParams;
12 9
 import com.reactnativenavigation.screens.SingleScreen;
13 10
 import com.reactnativenavigation.utils.ViewUtils;
14
-import com.reactnativenavigation.views.collapsingToolbar.OnScrollViewAddedListener;
15
-import com.reactnativenavigation.views.collapsingToolbar.ScrollListener;
16
-import com.reactnativenavigation.views.collapsingToolbar.ScrollViewDelegate;
17 11
 import com.reactnativenavigation.views.utils.ViewMeasurer;
18 12
 
19 13
 public class ContentView extends ReactRootView {
@@ -22,18 +16,12 @@ public class ContentView extends ReactRootView {
22 16
 
23 17
     boolean isContentVisible = false;
24 18
     private SingleScreen.OnDisplayListener onDisplayListener;
25
-    @Nullable private ScrollViewDelegate scrollViewDelegate;
26
-    private ViewMeasurer viewMeasurer;
27
-    private OnScrollViewAddedListener scrollViewAddedListener;
19
+    protected ViewMeasurer viewMeasurer;
28 20
 
29 21
     public void setOnDisplayListener(SingleScreen.OnDisplayListener onDisplayListener) {
30 22
         this.onDisplayListener = onDisplayListener;
31 23
     }
32 24
 
33
-    public void setOnScrollViewAddedListener(OnScrollViewAddedListener scrollViewAddedListener) {
34
-        this.scrollViewAddedListener = scrollViewAddedListener;
35
-    }
36
-
37 25
     public ContentView(Context context, String screenId, NavigationParams navigationParams) {
38 26
         super(context);
39 27
         this.screenId = screenId;
@@ -42,10 +30,6 @@ public class ContentView extends ReactRootView {
42 30
         viewMeasurer = new ViewMeasurer();
43 31
     }
44 32
 
45
-    public void setupCollapseDetection(ScrollListener scrollListener) {
46
-        scrollViewDelegate = new ScrollViewDelegate(scrollListener);
47
-    }
48
-
49 33
     public void setViewMeasurer(ViewMeasurer viewMeasurer) {
50 34
         this.viewMeasurer = viewMeasurer;
51 35
     }
@@ -70,31 +54,10 @@ public class ContentView extends ReactRootView {
70 54
                 viewMeasurer.getMeasuredHeight(heightMeasureSpec));
71 55
     }
72 56
 
73
-    @Override
74
-    public boolean dispatchTouchEvent(MotionEvent ev) {
75
-        if (scrollViewDelegate != null) {
76
-            boolean consumed = scrollViewDelegate.didInterceptTouchEvent(ev);
77
-            if (consumed) {
78
-                return true;
79
-            }
80
-        }
81
-        return super.dispatchTouchEvent(ev);
82
-    }
83
-
84 57
     @Override
85 58
     public void onViewAdded(final View child) {
86 59
         super.onViewAdded(child);
87 60
         detectContentViewVisible(child);
88
-        if (child instanceof ScrollView) {
89
-            onScrollViewAdded((ScrollView) child);
90
-        }
91
-    }
92
-
93
-    private void onScrollViewAdded(ScrollView scrollView) {
94
-        if (scrollViewDelegate != null) {
95
-            scrollViewDelegate.onScrollViewAdded(scrollView);
96
-            scrollViewAddedListener.onScrollViewAdded(scrollView);
97
-        }
98 61
     }
99 62
 
100 63
     private void detectContentViewVisible(View child) {
@@ -111,8 +74,4 @@ public class ContentView extends ReactRootView {
111 74
             });
112 75
         }
113 76
     }
114
-
115
-    public void collapse(float collapse) {
116
-        setTranslationY(collapse);
117
-    }
118 77
 }

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/views/ContextualMenu.java View File

@@ -78,6 +78,6 @@ public class ContextualMenu extends TitleBar implements LeftButtonOnClickListene
78 78
                 ((ViewManager) getParent()).removeView(ContextualMenu.this);
79 79
             }
80 80
         });
81
-        NavigationApplication.instance.sendNavigatorEvent("contextualMenuDismissed", navigatorEventId);
81
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("contextualMenuDismissed", navigatorEventId);
82 82
     }
83 83
 }

+ 3
- 3
android/app/src/main/java/com/reactnativenavigation/views/FloatingActionButtonCoordinator.java View File

@@ -94,7 +94,7 @@ public class FloatingActionButtonCoordinator {
94 94
                     fabAnimator.showExpended();
95 95
                     showActions();
96 96
                 }
97
-                NavigationApplication.instance.sendNavigatorEvent(params.collapsedId, params.navigatorEventId);
97
+                NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(params.collapsedId, params.navigatorEventId);
98 98
             }
99 99
         });
100 100
     }
@@ -108,7 +108,7 @@ public class FloatingActionButtonCoordinator {
108 108
             @Override
109 109
             public void onClick(View v) {
110 110
                 fabAnimator.collapse();
111
-                NavigationApplication.instance.sendNavigatorEvent(params.expendedId, params.navigatorEventId);
111
+                NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(params.expendedId, params.navigatorEventId);
112 112
             }
113 113
         });
114 114
     }
@@ -153,7 +153,7 @@ public class FloatingActionButtonCoordinator {
153 153
         action.setOnClickListener(new View.OnClickListener() {
154 154
             @Override
155 155
             public void onClick(View v) {
156
-                NavigationApplication.instance.sendNavigatorEvent(actionParams.id, actionParams.navigatorEventId);
156
+                NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(actionParams.id, actionParams.navigatorEventId);
157 157
                 fabAnimator.collapse();
158 158
             }
159 159
         });

+ 9
- 9
android/app/src/main/java/com/reactnativenavigation/views/LeftButton.java View File

@@ -9,7 +9,7 @@ import com.reactnativenavigation.NavigationApplication;
9 9
 import com.reactnativenavigation.params.TitleBarButtonParams;
10 10
 import com.reactnativenavigation.params.TitleBarLeftButtonParams;
11 11
 
12
-public class LeftButton extends MaterialMenuDrawable implements View.OnClickListener {
12
+class LeftButton extends MaterialMenuDrawable implements View.OnClickListener {
13 13
 
14 14
     private static int getColor(TitleBarButtonParams params) {
15 15
         return params != null && params.color.hasColor() ?
@@ -22,11 +22,11 @@ public class LeftButton extends MaterialMenuDrawable implements View.OnClickList
22 22
     private final String navigatorEventId;
23 23
     private final boolean overrideBackPressInJs;
24 24
 
25
-    public LeftButton(Context context,
26
-                      TitleBarLeftButtonParams params,
27
-                      LeftButtonOnClickListener onClickListener,
28
-                      String navigatorEventId,
29
-                      boolean overrideBackPressInJs) {
25
+    LeftButton(Context context,
26
+               TitleBarLeftButtonParams params,
27
+               LeftButtonOnClickListener onClickListener,
28
+               String navigatorEventId,
29
+               boolean overrideBackPressInJs) {
30 30
         super(context, getColor(params), Stroke.THIN);
31 31
         this.params = params;
32 32
         this.onClickListener = onClickListener;
@@ -35,7 +35,7 @@ public class LeftButton extends MaterialMenuDrawable implements View.OnClickList
35 35
         setInitialState();
36 36
     }
37 37
 
38
-    public void setIconState(TitleBarLeftButtonParams params) {
38
+    void setIconState(TitleBarLeftButtonParams params) {
39 39
         this.params = params;
40 40
         if (params.color.hasColor()) {
41 41
             setColor(params.color.getColor());
@@ -56,7 +56,7 @@ public class LeftButton extends MaterialMenuDrawable implements View.OnClickList
56 56
 
57 57
     private void handleBackButtonClick() {
58 58
         if (overrideBackPressInJs) {
59
-            NavigationApplication.instance.sendNavigatorEvent("backPress", navigatorEventId);
59
+            NavigationApplication.instance.getEventEmitter().sendNavigatorEvent("backPress", navigatorEventId);
60 60
         } else {
61 61
             onClickListener.onTitleBarBackButtonClick();
62 62
         }
@@ -79,6 +79,6 @@ public class LeftButton extends MaterialMenuDrawable implements View.OnClickList
79 79
     }
80 80
 
81 81
     private void sendClickEvent() {
82
-        NavigationApplication.instance.sendNavigatorEvent(params.eventId, navigatorEventId);
82
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(params.eventId, navigatorEventId);
83 83
     }
84 84
 }

+ 5
- 5
android/app/src/main/java/com/reactnativenavigation/views/Snakbar.java View File

@@ -6,13 +6,13 @@ import android.view.View;
6 6
 import com.reactnativenavigation.NavigationApplication;
7 7
 import com.reactnativenavigation.params.SnackbarParams;
8 8
 
9
-public class Snakbar {
9
+class Snakbar {
10 10
     private final OnDismissListener parent;
11 11
     private final String navigatorEventId;
12 12
     private final SnackbarParams params;
13 13
     private Snackbar snackbar;
14 14
 
15
-    public interface OnDismissListener {
15
+    interface OnDismissListener {
16 16
         void onDismiss(Snakbar snakbar);
17 17
     }
18 18
 
@@ -20,7 +20,7 @@ public class Snakbar {
20 20
         snackbar.show();
21 21
     }
22 22
 
23
-    public void dismiss() {
23
+    void dismiss() {
24 24
         snackbar.dismiss();
25 25
     }
26 26
 
@@ -28,7 +28,7 @@ public class Snakbar {
28 28
         return snackbar.getView();
29 29
     }
30 30
 
31
-    public Snakbar(OnDismissListener parent, String navigatorEventId, SnackbarParams params) {
31
+    Snakbar(OnDismissListener parent, String navigatorEventId, SnackbarParams params) {
32 32
         this.parent = parent;
33 33
         this.navigatorEventId = navigatorEventId;
34 34
         this.params = params;
@@ -47,7 +47,7 @@ public class Snakbar {
47 47
             snackbar.setAction(params.buttonText, new View.OnClickListener() {
48 48
                 @Override
49 49
                 public void onClick(View v) {
50
-                    NavigationApplication.instance.sendNavigatorEvent(params.eventId, navigatorEventId);
50
+                    NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(params.eventId, navigatorEventId);
51 51
                 }
52 52
             });
53 53
         }

+ 8
- 1
android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java View File

@@ -62,6 +62,7 @@ public class TitleBar extends Toolbar {
62 62
         setTitleTextColor(params);
63 63
         setSubtitleTextColor(params);
64 64
         colorOverflowButton(params);
65
+        setTranslucent(params);
65 66
     }
66 67
 
67 68
     private void colorOverflowButton(StyleParams params) {
@@ -71,6 +72,12 @@ public class TitleBar extends Toolbar {
71 72
         }
72 73
     }
73 74
 
75
+    private void setTranslucent(StyleParams params) {
76
+        if (params.topBarTranslucent) {
77
+            setBackground(new TranslucentTitleBarBackground());
78
+        }
79
+    }
80
+
74 81
     private boolean shouldColorOverflowButton(StyleParams params, Drawable overflowIcon) {
75 82
         return overflowIcon != null && params.titleBarButtonColor.hasColor();
76 83
     }
@@ -89,7 +96,7 @@ public class TitleBar extends Toolbar {
89 96
 
90 97
     private void addButtonsToTitleBar(List<TitleBarButtonParams> rightButtons, String navigatorEventId, Menu menu) {
91 98
         for (int i = 0; i < rightButtons.size(); i++) {
92
-            final TitleBarButton button = new TitleBarButton(menu, this, rightButtons.get(i), navigatorEventId);
99
+            final TitleBarButton button = ButtonFactory.create(menu, this, rightButtons.get(i), navigatorEventId);
93 100
             addButtonInReverseOrder(rightButtons, i, button);
94 101
         }
95 102
     }

+ 4
- 4
android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java View File

@@ -15,10 +15,10 @@ import java.util.ArrayList;
15 15
 
16 16
 class TitleBarButton implements MenuItem.OnMenuItemClickListener {
17 17
 
18
-    private final Menu menu;
19
-    private final View parent;
18
+    protected final Menu menu;
19
+    protected final View parent;
20 20
     protected TitleBarButtonParams buttonParams;
21
-    @Nullable private String navigatorEventId;
21
+    @Nullable protected String navigatorEventId;
22 22
 
23 23
     TitleBarButton(Menu menu, View parent, TitleBarButtonParams buttonParams, @Nullable String navigatorEventId) {
24 24
         this.menu = menu;
@@ -93,7 +93,7 @@ class TitleBarButton implements MenuItem.OnMenuItemClickListener {
93 93
 
94 94
     @Override
95 95
     public boolean onMenuItemClick(MenuItem item) {
96
-        NavigationApplication.instance.sendNavigatorEvent(buttonParams.eventId, navigatorEventId);
96
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(buttonParams.eventId, navigatorEventId);
97 97
         return true;
98 98
     }
99 99
 }

+ 156
- 0
android/app/src/main/java/com/reactnativenavigation/views/TitleBarSearchButton.java View File

@@ -0,0 +1,156 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.app.Activity;
4
+import android.graphics.drawable.Drawable;
5
+import android.support.annotation.Nullable;
6
+import android.support.v4.view.MenuItemCompat;
7
+import android.support.v7.widget.SearchView;
8
+import android.support.v7.widget.Toolbar;
9
+import android.view.Menu;
10
+import android.view.MenuItem;
11
+import android.view.View;
12
+import android.view.ViewGroup;
13
+import android.widget.EditText;
14
+import android.widget.ImageButton;
15
+import android.widget.ImageView;
16
+
17
+import com.facebook.react.bridge.Arguments;
18
+import com.facebook.react.bridge.WritableMap;
19
+import com.reactnativenavigation.NavigationApplication;
20
+import com.reactnativenavigation.R;
21
+import com.reactnativenavigation.params.TitleBarButtonParams;
22
+import com.reactnativenavigation.utils.ReflectionUtils;
23
+import com.reactnativenavigation.utils.ViewUtils;
24
+
25
+class TitleBarSearchButton extends TitleBarButton implements SearchView.OnQueryTextListener, View.OnFocusChangeListener, View.OnClickListener {
26
+    static final String BUTTON_ID = "searchView";
27
+    private SearchView searchView;
28
+
29
+    TitleBarSearchButton(Menu menu, View parent, TitleBarButtonParams buttonParams, @Nullable String navigatorEventId) {
30
+        super(menu, parent, buttonParams, navigatorEventId);
31
+    }
32
+
33
+    MenuItem addToMenu(int index) {
34
+        ((Activity) parent.getContext()).getMenuInflater().inflate(R.menu.search_item, menu);
35
+        MenuItem item = menu.findItem(R.id.toolbar_action_search);
36
+        item.setOnMenuItemClickListener(this);
37
+        if (buttonParams.icon != null) {
38
+            item.setIcon(buttonParams.icon);
39
+        }
40
+        searchView = (SearchView) MenuItemCompat.getActionView(item);
41
+        searchView.setQueryHint(buttonParams.hint);
42
+        searchView.setOnQueryTextFocusChangeListener(this);
43
+        searchView.setOnQueryTextListener(this);
44
+        searchView.setOnSearchClickListener(this);
45
+        setColor();
46
+        return item;
47
+    }
48
+
49
+    private void setColor() {
50
+        EditText searchEditText = ViewUtils.findChildByClass(searchView, EditText.class);
51
+        if (searchEditText != null) {
52
+            if (buttonParams.color.hasColor()) {
53
+                searchEditText.setTextColor(buttonParams.color.getColor());
54
+                searchEditText.setHintTextColor(buttonParams.color.getColor());
55
+            }
56
+            colorCloseButton(searchEditText);
57
+            setImagePlateColor();
58
+        }
59
+    }
60
+
61
+    private void colorCloseButton(EditText searchEditText) {
62
+        ViewUtils.performOnChildren((ViewGroup) searchEditText.getParent(), new ViewUtils.PerformOnViewTask() {
63
+            @Override
64
+            public void runOnView(View view) {
65
+                if (view instanceof ImageView) {
66
+                    if (buttonParams.color.hasColor()) {
67
+                        ((ImageView) view).setColorFilter(buttonParams.color.getColor());
68
+                    }
69
+                }
70
+            }
71
+        });
72
+    }
73
+
74
+    private void setImagePlateColor() {
75
+        if (buttonParams.color.hasColor()) {
76
+            Object mSearchPlate = ReflectionUtils.getDeclaredField(searchView, "mSearchPlate");
77
+            if (mSearchPlate != null) {
78
+                Drawable background = ((View) mSearchPlate).getBackground();
79
+                if (background != null) {
80
+                    ViewUtils.tintDrawable(background, buttonParams.color.getColor(), true);
81
+                }
82
+            }
83
+        }
84
+    }
85
+
86
+    @Override
87
+    public boolean onMenuItemClick(MenuItem item) {
88
+        setupBackButtonAfterSearchViewIsExpended();
89
+        return false;
90
+    }
91
+
92
+    private void setupBackButtonAfterSearchViewIsExpended() {
93
+        ViewUtils.runOnPreDraw(searchView, new Runnable() {
94
+            @Override
95
+            public void run() {
96
+                Object backButton = ViewUtils.findChildByClass((ViewGroup) searchView.getParent(), ImageButton.class);
97
+                if (backButton != null) {
98
+                    setBackButtonClickListener((View) backButton);
99
+                    colorBackButton((ImageView) backButton);
100
+                }
101
+            }
102
+
103
+            private void colorBackButton(ImageView backButton) {
104
+                if (buttonParams.color.hasColor()) {
105
+                    ViewUtils.tintDrawable(backButton.getDrawable(), buttonParams.color.getColor(), true);
106
+                }
107
+            }
108
+
109
+            private void setBackButtonClickListener(View backButton) {
110
+                backButton.setOnClickListener(new View.OnClickListener() {
111
+                    @Override
112
+                    public void onClick(View v) {
113
+                        ((Toolbar) searchView.getParent()).collapseActionView();
114
+                        sendEvent("searchViewHidden");
115
+                    }
116
+                });
117
+            }
118
+        });
119
+    }
120
+
121
+    @Override
122
+    public boolean onQueryTextSubmit(String query) {
123
+        WritableMap arguments = Arguments.createMap();
124
+        arguments.putString("query", query);
125
+        sendEvent("searchQuerySubmit", arguments);
126
+        return false;
127
+    }
128
+
129
+    @Override
130
+    public boolean onQueryTextChange(String newText) {
131
+        WritableMap arguments = Arguments.createMap();
132
+        arguments.putString("query", newText);
133
+        sendEvent("searchQueryChange", arguments);
134
+        return false;
135
+    }
136
+
137
+    @Override
138
+    public void onFocusChange(View v, boolean hasFocus) {
139
+        WritableMap arguments = Arguments.createMap();
140
+        arguments.putBoolean("hasFocus", hasFocus);
141
+        sendEvent("searchFocusChange", arguments);
142
+    }
143
+
144
+    @Override
145
+    public void onClick(View v) {
146
+        sendEvent("searchViewShown");
147
+    }
148
+
149
+    private void sendEvent(String eventId, WritableMap arguments) {
150
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(eventId, navigatorEventId, arguments);
151
+    }
152
+
153
+    private void sendEvent(String eventId) {
154
+        NavigationApplication.instance.getEventEmitter().sendNavigatorEvent(eventId, navigatorEventId);
155
+    }
156
+}

+ 0
- 1
android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

@@ -78,7 +78,6 @@ public class TopBar extends AppBarLayout {
78 78
     private void setTransparent() {
79 79
         setBackgroundColor(Color.TRANSPARENT);
80 80
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
81
-            setElevation(0);
82 81
             setOutlineProvider(null);
83 82
         }
84 83
     }

+ 36
- 0
android/app/src/main/java/com/reactnativenavigation/views/TranslucentTitleBarBackground.java View File

@@ -0,0 +1,36 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.graphics.Color;
4
+import android.graphics.LinearGradient;
5
+import android.graphics.Shader;
6
+import android.graphics.drawable.PaintDrawable;
7
+import android.graphics.drawable.ShapeDrawable;
8
+import android.graphics.drawable.shapes.RectShape;
9
+
10
+class TranslucentTitleBarBackground extends PaintDrawable {
11
+
12
+    TranslucentTitleBarBackground() {
13
+        setShape(new RectShape());
14
+        createShader();
15
+    }
16
+
17
+    private void createShader() {
18
+        ShapeDrawable.ShaderFactory sf = new ShapeDrawable.ShaderFactory() {
19
+            @Override
20
+            public Shader resize(int width, int height) {
21
+                double angleInRadians = Math.toRadians(90);
22
+
23
+                int x1 = (int) (Math.cos(angleInRadians) * width);
24
+                int y1 = (int) (Math.sin(angleInRadians) * height);
25
+                int[] colors = new int[]{Color.argb(90, 0, 0, 0), Color.argb(15, 0, 0, 0), Color.TRANSPARENT};
26
+                float[] positions = {0, 0.78f, 1};
27
+                LinearGradient lg = new LinearGradient(0, 0, x1, y1,
28
+                        colors,
29
+                        positions,
30
+                        Shader.TileMode.REPEAT);
31
+                return lg;
32
+            }
33
+        };
34
+        setShaderFactory(sf);
35
+    }
36
+}

+ 17
- 5
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseAmount.java View File

@@ -1,19 +1,31 @@
1 1
 package com.reactnativenavigation.views.collapsingToolbar;
2 2
 
3 3
 public class CollapseAmount {
4
-    public final static CollapseAmount None = new CollapseAmount();
4
+    final static CollapseAmount None = new CollapseAmount();
5
+
6
+    private CollapseAmount() {}
7
+
5 8
     private Float amount;
9
+    private CollapseCalculator.Direction direction;
6 10
 
7
-    public CollapseAmount(float amount) {
11
+    CollapseAmount(float amount) {
8 12
         this.amount = amount;
9 13
     }
10 14
 
11
-    private CollapseAmount() {
15
+    CollapseAmount(CollapseCalculator.Direction direction) {
16
+        this.direction = direction;
17
+    }
18
+
19
+    boolean canCollapse() {
20
+        return amount != null || this != None;
21
+    }
12 22
 
23
+    boolean collapseToTop() {
24
+        return direction == CollapseCalculator.Direction.Up;
13 25
     }
14 26
 
15
-    public boolean canCollapse() {
16
-        return amount != null;
27
+    boolean collapseToBottom() {
28
+        return direction == CollapseCalculator.Direction.Down;
17 29
     }
18 30
 
19 31
     public float get() {

+ 99
- 20
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseCalculator.java View File

@@ -2,10 +2,20 @@ package com.reactnativenavigation.views.collapsingToolbar;
2 2
 
3 3
 import android.support.annotation.NonNull;
4 4
 import android.support.annotation.Nullable;
5
+import android.view.GestureDetector;
5 6
 import android.view.MotionEvent;
6 7
 import android.widget.ScrollView;
7 8
 
8
-class CollapseCalculator {
9
+import com.reactnativenavigation.NavigationApplication;
10
+import com.reactnativenavigation.params.CollapsingTopBarParams.CollapseBehaviour;
11
+
12
+import static com.reactnativenavigation.params.CollapsingTopBarParams.CollapseBehaviour.TitleBarHideOnScroll;
13
+
14
+public class CollapseCalculator {
15
+    enum Direction {
16
+        Up, Down, None
17
+    }
18
+
9 19
     private float collapse;
10 20
     private MotionEvent previousTouchEvent;
11 21
     private float touchDownY = -1;
@@ -15,24 +25,67 @@ class CollapseCalculator {
15 25
     private boolean canCollapse = true;
16 26
     private boolean canExpend = false;
17 27
     private CollapsingView view;
28
+    private CollapseBehaviour collapseBehaviour;
18 29
     protected ScrollView scrollView;
30
+    private GestureDetector flingDetector;
31
+    private OnFlingListener flingListener;
32
+    private int scrollY = 0;
33
+    private int totalCollapse = 0;
19 34
 
20
-    CollapseCalculator(final CollapsingView collapsingView) {
35
+    public CollapseCalculator(final CollapsingView collapsingView, CollapseBehaviour collapseBehaviour) {
21 36
         this.view = collapsingView;
37
+        this.collapseBehaviour = collapseBehaviour;
38
+        setFlingDetector(collapseBehaviour);
39
+    }
40
+
41
+    private void setFlingDetector(CollapseBehaviour collapseBehaviour) {
42
+        if (collapseBehaviour == TitleBarHideOnScroll) {
43
+            flingDetector =
44
+                    new GestureDetector(NavigationApplication.instance, new GestureDetector.SimpleOnGestureListener() {
45
+                        @Override
46
+                        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
47
+                            final Direction direction = getScrollDirection(e1, e2);
48
+                            if (canCollapse(direction) && totalCollapse != 0) {
49
+                                flingListener.onFling(new CollapseAmount(direction));
50
+                                return true;
51
+                            }
52
+                            return false;
53
+                        }
54
+
55
+                        private Direction getScrollDirection(MotionEvent e1, MotionEvent e2) {
56
+                            if (e1.getRawY() == e2.getRawY()) {
57
+                                return Direction.None;
58
+                            }
59
+                            return e1.getRawY() - e2.getRawY() > 0 ? Direction.Up : Direction.Down;
60
+                        }
61
+                    });
62
+        }
22 63
     }
23 64
 
24 65
     void setScrollView(ScrollView scrollView) {
25 66
         this.scrollView = scrollView;
26 67
     }
27 68
 
69
+    void setFlingListener(OnFlingListener flingListener) {
70
+        this.flingListener = flingListener;
71
+    }
72
+
28 73
     @NonNull
29 74
     CollapseAmount calculate(MotionEvent event) {
30 75
         updateInitialTouchY(event);
76
+        CollapseAmount touchUpCollapse = shouldCollapseOnTouchUp(event);
77
+        if (touchUpCollapse != CollapseAmount.None) {
78
+            previousTouchEvent = MotionEvent.obtain(event);
79
+            return touchUpCollapse;
80
+        }
81
+
31 82
         if (!isMoveEvent(event)) {
83
+            previousTouchEvent = MotionEvent.obtain(event);
32 84
             return CollapseAmount.None;
33 85
         }
34 86
 
35
-        if (shouldCollapse(event)) {
87
+        final Direction direction = calculateScrollDirection(event.getRawY());
88
+        if (shouldCollapseAfterMoveEvent(direction)) {
36 89
             return calculateCollapse(event);
37 90
         } else {
38 91
             previousCollapseY = -1;
@@ -41,24 +94,48 @@ class CollapseCalculator {
41 94
         }
42 95
     }
43 96
 
44
-    private boolean shouldCollapse(MotionEvent event) {
97
+    private CollapseAmount shouldCollapseOnTouchUp(MotionEvent event) {
98
+        if (collapseBehaviour == TitleBarHideOnScroll && !flingDetector.onTouchEvent(event) && isTouchUp(event)) {
99
+            final float visibilityPercentage = view.getCurrentCollapseValue() / view.getFinalCollapseValue();
100
+            Direction direction = visibilityPercentage >= 0.5f ? Direction.Up : Direction.Down;
101
+            if (canCollapse(direction) && totalCollapse != 0) {
102
+                return new CollapseAmount(direction);
103
+            }
104
+        }
105
+        return CollapseAmount.None;
106
+    }
107
+
108
+    private boolean shouldCollapseAfterMoveEvent(Direction direction) {
109
+        if (collapseBehaviour == CollapseBehaviour.TitleBarHideOnScroll && !isScrolling()) {
110
+            return false;
111
+        }
112
+        return canCollapse(direction);
113
+    }
114
+
115
+    private boolean canCollapse(Direction direction) {
45 116
         checkCollapseLimits();
46
-        ScrollDirection.Direction direction = getScrollDirection(event.getRawY());
47
-        return isNotCollapsedOrExpended() ||
48
-                (canCollapse && isExpendedAndScrollingUp(direction)) ||
49
-                (canExpend && isCollapsedAndScrollingDown(direction));
117
+        return (isNotCollapsedOrExpended() ||
118
+               (canCollapse && isExpendedAndScrollingUp(direction)) ||
119
+               (canExpend && isCollapsedAndScrollingDown(direction)));
120
+    }
121
+
122
+    private boolean isScrolling() {
123
+        final int currentScrollY = scrollView.getScrollY();
124
+        final boolean isScrolling = currentScrollY != scrollY;
125
+        scrollY = currentScrollY;
126
+        return isScrolling;
50 127
     }
51 128
 
52
-    private ScrollDirection.Direction getScrollDirection(float y) {
129
+    private Direction calculateScrollDirection(float y) {
53 130
         if (y == (previousCollapseY == -1 ? touchDownY : previousCollapseY)) {
54
-            return ScrollDirection.Direction.None;
131
+            return Direction.None;
55 132
         }
56 133
         if (previousTouchEvent == null) {
57
-            return ScrollDirection.Direction.None;
134
+            return Direction.None;
58 135
         }
59 136
         return y < previousTouchEvent.getRawY() ?
60
-                ScrollDirection.Direction.Up :
61
-                ScrollDirection.Direction.Down;
137
+                Direction.Up :
138
+                Direction.Down;
62 139
     }
63 140
 
64 141
     private void checkCollapseLimits() {
@@ -78,18 +155,18 @@ class CollapseCalculator {
78 155
     private boolean calculateCanExpend(float currentTopBarTranslation, float finalExpendedTranslation, float finalCollapsedTranslation) {
79 156
         return currentTopBarTranslation >= finalCollapsedTranslation &&
80 157
                currentTopBarTranslation < finalExpendedTranslation &&
81
-               scrollView.getScrollY() == 0;
158
+               (scrollView.getScrollY() == 0 || collapseBehaviour == CollapseBehaviour.TitleBarHideOnScroll);
82 159
     }
83 160
 
84
-    private boolean isCollapsedAndScrollingDown(ScrollDirection.Direction direction) {
85
-        return isCollapsed && direction == ScrollDirection.Direction.Down;
161
+    private boolean isCollapsedAndScrollingDown(Direction direction) {
162
+        return isCollapsed && direction == Direction.Down;
86 163
     }
87 164
 
88
-    private boolean isExpendedAndScrollingUp(ScrollDirection.Direction direction) {
89
-        return isExpended && direction == ScrollDirection.Direction.Up;
165
+    private boolean isExpendedAndScrollingUp(Direction direction) {
166
+        return isExpended && direction == Direction.Up;
90 167
     }
91 168
 
92
-    private  boolean isNotCollapsedOrExpended() {
169
+    private boolean isNotCollapsedOrExpended() {
93 170
         return canExpend && canCollapse;
94 171
     }
95 172
 
@@ -107,6 +184,7 @@ class CollapseCalculator {
107 184
             previousCollapseY = y;
108 185
         }
109 186
         collapse = calculateCollapse(y);
187
+        totalCollapse += collapse;
110 188
         previousCollapseY = y;
111 189
         previousTouchEvent = MotionEvent.obtain(event);
112 190
         return new CollapseAmount(collapse);
@@ -146,12 +224,13 @@ class CollapseCalculator {
146 224
     }
147 225
 
148 226
     private void saveInitialTouchY(MotionEvent event) {
227
+        totalCollapse = 0;
149 228
         touchDownY = event.getRawY();
229
+        scrollY = scrollView.getScrollY();
150 230
         previousCollapseY = touchDownY;
151 231
     }
152 232
 
153 233
     private void clearInitialTouchY() {
154
-        touchDownY = -1;
155 234
         previousCollapseY = -1;
156 235
         collapse = 0;
157 236
     }

+ 38
- 14
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingTopBar.java View File

@@ -13,17 +13,29 @@ public class CollapsingTopBar extends TopBar implements CollapsingView {
13 13
     private CollapsingTopBarBackground collapsingTopBarBackground;
14 14
     private ScrollListener scrollListener;
15 15
     private float finalCollapsedTranslation;
16
+    private CollapsingTopBarParams params;
17
+    private final ViewCollapser viewCollapser;
16 18
 
17
-    public CollapsingTopBar(Context context, CollapsingTopBarParams params) {
19
+    public CollapsingTopBar(Context context, final CollapsingTopBarParams params) {
18 20
         super(context);
21
+        this.params = params;
19 22
         createCollapsingTopBar(params);
23
+        calculateFinalCollapsedTranslation(params);
24
+        viewCollapser = new ViewCollapser(this);
25
+    }
26
+
27
+    private void calculateFinalCollapsedTranslation(final CollapsingTopBarParams params) {
20 28
         ViewUtils.runOnPreDraw(this, new Runnable() {
21 29
             @Override
22 30
             public void run() {
23
-                finalCollapsedTranslation = getCollapsingTopBarBackground().getCollapsedTopBarHeight() - getHeight();
31
+                if (params.hasBackgroundImage()) {
32
+                    finalCollapsedTranslation =
33
+                            getCollapsingTopBarBackground().getCollapsedTopBarHeight() - getHeight();
34
+                } else {
35
+                    finalCollapsedTranslation = -titleBar.getHeight();
36
+                }
24 37
             }
25 38
         });
26
-
27 39
     }
28 40
 
29 41
     public void setScrollListener(ScrollListener scrollListener) {
@@ -31,26 +43,36 @@ public class CollapsingTopBar extends TopBar implements CollapsingView {
31 43
     }
32 44
 
33 45
     private void createCollapsingTopBar(CollapsingTopBarParams params) {
34
-        collapsingTopBarBackground = new CollapsingTopBarBackground(getContext(), params);
35
-        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, (int) CollapsingTopBarBackground.MAX_HEIGHT);
36
-        titleBarAndContextualMenuContainer.addView(collapsingTopBarBackground, lp);
46
+        if (params.hasBackgroundImage()) {
47
+            collapsingTopBarBackground = new CollapsingTopBarBackground(getContext(), params);
48
+            LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, (int) CollapsingTopBarBackground.MAX_HEIGHT);
49
+            titleBarAndContextualMenuContainer.addView(collapsingTopBarBackground, lp);
50
+        }
37 51
     }
38 52
 
39 53
     @Override
40 54
     protected TitleBar createTitleBar() {
41
-        return new CollapsingTitleBar(getContext(),
42
-                collapsingTopBarBackground.getCollapsedTopBarHeight(),
43
-                scrollListener);
55
+        if (params.hasBackgroundImage()) {
56
+            return new CollapsingTitleBar(getContext(),
57
+                    collapsingTopBarBackground.getCollapsedTopBarHeight(),
58
+                    scrollListener);
59
+        } else {
60
+            return super.createTitleBar();
61
+        }
44 62
     }
45 63
 
46 64
     public CollapsingTopBarBackground getCollapsingTopBarBackground() {
47 65
         return collapsingTopBarBackground;
48 66
     }
49 67
 
50
-    public void collapse(float collapse) {
51
-        setTranslationY(collapse);
52
-        ((CollapsingTitleBar) titleBar).collapse(collapse);
53
-        collapsingTopBarBackground.collapse(collapse);
68
+    public void collapse(CollapseAmount amount) {
69
+        viewCollapser.collapse(amount);
70
+        if (titleBar instanceof CollapsingTitleBar) {
71
+            ((CollapsingTitleBar) titleBar).collapse(amount.get());
72
+        }
73
+        if (collapsingTopBarBackground != null) {
74
+            collapsingTopBarBackground.collapse(amount.get());
75
+        }
54 76
     }
55 77
 
56 78
     public void onScrollViewAdded(ScrollView scrollView) {
@@ -63,7 +85,9 @@ public class CollapsingTopBar extends TopBar implements CollapsingView {
63 85
     }
64 86
 
65 87
     public int getCollapsedHeight() {
66
-        return collapsingTopBarBackground.getCollapsedTopBarHeight();
88
+        return params.hasBackgroundImage() ?
89
+                collapsingTopBarBackground.getCollapsedTopBarHeight() :
90
+                titleBar.getHeight();
67 91
     }
68 92
 
69 93
     @Override

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/OnFlingListener.java View File

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.views.collapsingToolbar;
2
+
3
+interface OnFlingListener {
4
+    void onFling(CollapseAmount amount);
5
+}

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/OnScrollListener.java View File

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.views.collapsingToolbar;
2
+
3
+public interface  OnScrollListener extends OnFlingListener {
4
+    void onScroll(CollapseAmount amount);
5
+}

+ 0
- 50
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollDirection.java View File

@@ -1,50 +0,0 @@
1
-package com.reactnativenavigation.views.collapsingToolbar;
2
-
3
-import android.widget.ScrollView;
4
-
5
-public class ScrollDirection {
6
-
7
-    public enum Direction {
8
-        Up, Down, None
9
-    }
10
-
11
-    private final ScrollView scrollView;
12
-    private int lastScrollY = 0;
13
-
14
-    public ScrollDirection(ScrollView scrollView) {
15
-        this.scrollView = scrollView;
16
-    }
17
-
18
-    public Direction getScrollDirection() {
19
-        Direction direction = Direction.None;
20
-
21
-        final int scrollY = scrollView.getScrollY();
22
-        if (isScrollPositionChanged(scrollY) && !isTopOverscroll(scrollY) && !isBottomOverscroll(scrollY)) {
23
-            direction = getScrollDirection(scrollY);
24
-            lastScrollY = scrollY;
25
-        }
26
-        return direction;
27
-    }
28
-
29
-    public int getScrollDelta() {
30
-        return scrollView.getScrollY() - lastScrollY;
31
-    }
32
-
33
-
34
-    private Direction getScrollDirection(int scrollY) {
35
-        return scrollY > lastScrollY ? Direction.Up : Direction.Down;
36
-    }
37
-
38
-    private boolean isBottomOverscroll(int scrollY) {
39
-        return scrollY >= (scrollView.getChildAt(0).getHeight() - scrollView.getHeight());
40
-    }
41
-
42
-    private boolean isTopOverscroll(int scrollY) {
43
-        return scrollY <= 0;
44
-    }
45
-
46
-    private boolean isScrollPositionChanged(int scrollY) {
47
-        return scrollY != lastScrollY;
48
-    }
49
-
50
-}

+ 13
- 13
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollListener.java View File

@@ -3,30 +3,30 @@ package com.reactnativenavigation.views.collapsingToolbar;
3 3
 import android.view.MotionEvent;
4 4
 import android.widget.ScrollView;
5 5
 
6
-public class ScrollListener implements ScrollViewDelegate.OnScrollListener {
6
+import com.reactnativenavigation.params.CollapsingTopBarParams.CollapseBehaviour;
7
+
8
+public class ScrollListener {
7 9
     private CollapseCalculator collapseCalculator;
8 10
     private OnScrollListener scrollListener;
11
+    private CollapseBehaviour collapseBehaviour;
9 12
 
10
-    public interface  OnScrollListener {
11
-        void onScroll(float amount);
12
-    }
13
-
14
-    public ScrollListener(CollapsingView collapsingView, OnScrollListener scrollListener) {
15
-        this.collapseCalculator = new CollapseCalculator(collapsingView);
13
+    public ScrollListener(CollapseCalculator collapseCalculator, OnScrollListener scrollListener,
14
+                          CollapseBehaviour collapseBehaviour) {
15
+        this.collapseCalculator = collapseCalculator;
16 16
         this.scrollListener = scrollListener;
17
+        this.collapseBehaviour = collapseBehaviour;
18
+        collapseCalculator.setFlingListener(scrollListener);
17 19
     }
18 20
 
19
-    @Override
20
-    public void onScrollViewAdded(ScrollView scrollView) {
21
+    void onScrollViewAdded(ScrollView scrollView) {
21 22
         collapseCalculator.setScrollView(scrollView);
22 23
     }
23 24
 
24
-    @Override
25
-    public boolean onTouch(MotionEvent event) {
25
+    boolean onTouch(MotionEvent event) {
26 26
         CollapseAmount amount = collapseCalculator.calculate(event);
27 27
         if (amount.canCollapse()) {
28
-            scrollListener.onScroll(amount.get());
29
-            return true;
28
+            scrollListener.onScroll(amount);
29
+            return CollapseBehaviour.CollapseTopBar.equals(collapseBehaviour);
30 30
         }
31 31
         return false;
32 32
     }

+ 18
- 11
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ScrollViewDelegate.java View File

@@ -5,18 +5,19 @@ import android.view.View;
5 5
 import android.widget.ScrollView;
6 6
 
7 7
 public class ScrollViewDelegate implements View.OnTouchListener {
8
-    interface OnScrollListener {
9
-        boolean onTouch(MotionEvent event);
8
+    private ScrollView scrollView;
9
+    private ScrollListener listener;
10 10
 
11
-        void onScrollViewAdded(ScrollView scrollView);
11
+    public ScrollViewDelegate(ScrollListener scrollListener) {
12
+        listener = scrollListener;
12 13
     }
13 14
 
14
-    private ScrollView scrollView;
15
-    private OnScrollListener listener;
16
-    private Boolean didInterceptLastTouchEvent = null;
15
+    public boolean hasScrollView() {
16
+        return scrollView != null;
17
+    }
17 18
 
18
-    public ScrollViewDelegate(OnScrollListener scrollListener) {
19
-        listener = scrollListener;
19
+    public ScrollView getScrollView() {
20
+        return scrollView;
20 21
     }
21 22
 
22 23
     public void onScrollViewAdded(ScrollView scrollView) {
@@ -24,15 +25,21 @@ public class ScrollViewDelegate implements View.OnTouchListener {
24 25
         listener.onScrollViewAdded(this.scrollView);
25 26
     }
26 27
 
28
+    public void onScrollViewRemoved() {
29
+        this.scrollView = null;
30
+    }
31
+
27 32
     public boolean didInterceptTouchEvent(MotionEvent ev) {
28 33
             return listener.onTouch(ev);
29 34
     }
30 35
 
31 36
     @Override
32 37
     public boolean onTouch(View view, MotionEvent event) {
33
-        if (!didInterceptLastTouchEvent) {
34
-            scrollView.onTouchEvent(event);
35
-        }
38
+        scrollView.onTouchEvent(event);
36 39
         return this.listener.onTouch(event);
37 40
     }
41
+
42
+    public void destroy() {
43
+        scrollView = null;
44
+    }
38 45
 }

+ 57
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ViewCollapser.java View File

@@ -0,0 +1,57 @@
1
+package com.reactnativenavigation.views.collapsingToolbar;
2
+
3
+import android.animation.Animator;
4
+import android.animation.AnimatorListenerAdapter;
5
+
6
+public class ViewCollapser {
7
+    private static final int DURATION = 160;
8
+    private CollapsingView view;
9
+    private boolean animating;
10
+
11
+    public ViewCollapser(CollapsingView view) {
12
+        this.view = view;
13
+    }
14
+
15
+    public void collapse(CollapseAmount amount) {
16
+        if (amount.collapseToTop()) {
17
+            collapseView(true, view.getFinalCollapseValue());
18
+        } else if (amount.collapseToBottom()) {
19
+            collapseView(true, 0);
20
+        } else {
21
+            collapse(amount.get());
22
+        }
23
+    }
24
+
25
+    public void collapse(float amount) {
26
+        view.asView().setTranslationY(amount);
27
+    }
28
+
29
+    private void collapseView(boolean animate, float translation) {
30
+        if (animate) {
31
+            animate(translation);
32
+        } else {
33
+            view.asView().setTranslationY(translation);
34
+        }
35
+    }
36
+
37
+    private void animate(final float translation) {
38
+        if (animating) {
39
+            return;
40
+        }
41
+        view.asView().animate()
42
+                .translationY(translation)
43
+                .setDuration(DURATION)
44
+                .setListener(new AnimatorListenerAdapter() {
45
+                    @Override
46
+                    public void onAnimationStart(Animator animation) {
47
+                        animating = true;
48
+                    }
49
+
50
+                    @Override
51
+                    public void onAnimationEnd(Animator animation) {
52
+                        animating = false;
53
+                    }
54
+                })
55
+                .start();
56
+    }
57
+}

+ 64
- 0
android/app/src/main/java/com/reactnativenavigation/views/utils/ScrollViewDetector.java View File

@@ -0,0 +1,64 @@
1
+package com.reactnativenavigation.views.utils;
2
+
3
+import android.view.View;
4
+import android.view.ViewGroup;
5
+import android.widget.ScrollView;
6
+
7
+import com.reactnativenavigation.utils.ViewUtils;
8
+import com.reactnativenavigation.views.ContentView;
9
+import com.reactnativenavigation.views.collapsingToolbar.OnScrollViewAddedListener;
10
+import com.reactnativenavigation.views.collapsingToolbar.ScrollViewDelegate;
11
+
12
+public class ScrollViewDetector {
13
+    private OnScrollViewAddedListener scrollViewAddedListener;
14
+    private ScrollViewDelegate scrollViewDelegate;
15
+    private View.OnAttachStateChangeListener stateChangeListener;
16
+
17
+    public ScrollViewDetector(final ContentView contentView,
18
+                              OnScrollViewAddedListener onScrollViewAddedListener,
19
+                              final ScrollViewDelegate scrollViewDelegate) {
20
+        this.scrollViewAddedListener = onScrollViewAddedListener;
21
+        this.scrollViewDelegate = scrollViewDelegate;
22
+        stateChangeListener = new View.OnAttachStateChangeListener() {
23
+            @Override
24
+            public void onViewAttachedToWindow(View v) {
25
+
26
+            }
27
+
28
+            @Override
29
+            public void onViewDetachedFromWindow(View v) {
30
+                scrollViewDelegate.getScrollView().removeOnAttachStateChangeListener(this);
31
+                scrollViewDelegate.onScrollViewRemoved();
32
+                contentView.post(new Runnable() {
33
+                    @Override
34
+                    public void run() {
35
+                        detectScrollViewAdded(contentView);
36
+                    }
37
+                });
38
+            }
39
+        };
40
+    }
41
+
42
+    public void detectScrollViewAdded(View child) {
43
+        if (child instanceof ScrollView) {
44
+            onScrollViewAdded((ScrollView) child);
45
+        } else if (child instanceof ViewGroup) {
46
+            Object maybeScrollView = ViewUtils.findChildByClass((ViewGroup) child, ScrollView.class);
47
+            if (maybeScrollView instanceof ScrollView) {
48
+                onScrollViewAdded((ScrollView) maybeScrollView);
49
+            }
50
+        }
51
+    }
52
+
53
+    private void onScrollViewAdded(final ScrollView scrollView) {
54
+        if (scrollViewDelegate != null && !scrollViewDelegate.hasScrollView()) {
55
+            scrollViewDelegate.onScrollViewAdded(scrollView);
56
+            scrollViewAddedListener.onScrollViewAdded(scrollView);
57
+            scrollView.addOnAttachStateChangeListener(stateChangeListener);
58
+        }
59
+    }
60
+
61
+    public void destroy() {
62
+        scrollViewDelegate.getScrollView().removeOnAttachStateChangeListener(stateChangeListener);
63
+    }
64
+}

BIN
android/app/src/main/res/drawable-hdpi/ic_action_name.png View File


BIN
android/app/src/main/res/drawable-hdpi/search.png View File


BIN
android/app/src/main/res/drawable-mdpi/ic_action_name.png View File


BIN
android/app/src/main/res/drawable-mdpi/search.png View File


BIN
android/app/src/main/res/drawable-xhdpi/ic_action_name.png View File


BIN
android/app/src/main/res/drawable-xhdpi/search.png View File


BIN
android/app/src/main/res/drawable-xxhdpi/ic_action_name.png View File


BIN
android/app/src/main/res/drawable-xxhdpi/search.png View File


+ 10
- 0
android/app/src/main/res/menu/search_item.xml View File

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android">
3
+    <item
4
+        android:id="@+id/toolbar_action_search"
5
+        android:title="Search"
6
+        android:icon="@drawable/search"
7
+        app:showAsAction="always|collapseActionView"
8
+        android:iconifiedByDefault="true"
9
+        app:actionViewClass="android.support.v7.widget.SearchView"/>
10
+</menu>

+ 2
- 0
src/deprecated/platformSpecificDeprecated.android.js View File

@@ -117,9 +117,11 @@ function convertStyleParams(originalStyleObject) {
117 117
     statusBarColor: originalStyleObject.statusBarColor,
118 118
     topBarColor: originalStyleObject.navBarBackgroundColor,
119 119
     topBarTransparent: originalStyleObject.navBarTransparent,
120
+    topBarTranslucent: originalStyleObject.navBarTranslucent,
120 121
     collapsingToolBarImage: originalStyleObject.collapsingToolBarImage,
121 122
     collapsingToolBarCollapsedColor: originalStyleObject.collapsingToolBarCollapsedColor,
122 123
     titleBarHidden: originalStyleObject.navBarHidden,
124
+    titleBarHideOnScroll: originalStyleObject.navBarHideOnScroll,
123 125
     titleBarTitleColor: originalStyleObject.navBarTextColor,
124 126
     titleBarSubtitleColor: originalStyleObject.navBarTextSubtitleColor,
125 127
     titleBarButtonColor: originalStyleObject.navBarButtonColor,