diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
index 1e77408dfc..f0b61f95b6 100644
--- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
+++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
@@ -20,6 +20,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.children
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.UIManagerHelper
import com.google.android.material.appbar.AppBarLayout
@@ -412,32 +413,43 @@ class ScreenStackFragment :
return super.onCreateOptionsMenu(menu, inflater)
}
- private fun shouldShowSearchBar(): Boolean {
+ private fun locateSearchBarSubviewInConfig(): ScreenStackHeaderSubview? {
val config = screen.headerConfig
val numberOfSubViews = config?.configSubviewsCount ?: 0
if (config != null && numberOfSubViews > 0) {
for (i in 0 until numberOfSubViews) {
val subView = config.getConfigSubview(i)
if (subView.type == ScreenStackHeaderSubview.Type.SEARCH_BAR) {
- return true
+ return subView
}
}
}
- return false
+ return null
+ }
+
+ private fun extractShowAsActionFromSearchBar(searchSubview: ScreenStackHeaderSubview): Int {
+ for (child in searchSubview.children) {
+ if (child is SearchBarView) {
+ return child.showAsAction
+ }
+ }
+
+ return MenuItem.SHOW_AS_ACTION_ALWAYS
}
private fun updateToolbarMenu(menu: Menu) {
menu.clear()
- if (shouldShowSearchBar()) {
+ locateSearchBarSubviewInConfig()?.let {
val currentContext = context
if (searchView == null && currentContext != null) {
val newSearchView = CustomSearchView(currentContext, this)
searchView = newSearchView
onSearchViewCreate?.invoke(newSearchView)
}
- menu.add("").apply {
- setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ menu.add("Search").apply {
actionView = searchView
+ setIcon(R.drawable.ic_action_search)
+ setShowAsAction(extractShowAsActionFromSearchBar(it))
}
}
}
diff --git a/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt b/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt
index 8be7cd731d..cf1a504393 100644
--- a/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt
+++ b/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt
@@ -78,6 +78,14 @@ class SearchBarManager :
view.shouldOverrideBackButton = disableBackButtonOverride != true
}
+ @ReactProp(name = "searchShowAsAction")
+ override fun setShowAsAction(
+ view: SearchBarView,
+ showAsAction: String?,
+ ) {
+ view.setShowAsAction(showAsAction)
+ }
+
@ReactProp(name = "inputType")
override fun setInputType(
view: SearchBarView,
diff --git a/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt b/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt
index dd666ba099..70c0c6fda8 100644
--- a/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt
+++ b/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt
@@ -2,6 +2,7 @@ package com.swmansion.rnscreens
import android.annotation.SuppressLint
import android.text.InputType
+import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
@@ -30,6 +31,17 @@ class SearchBarView(
var autoFocus: Boolean = false
var shouldShowHintSearchIcon: Boolean = true
+ var showAsAction: Int = MenuItem.SHOW_AS_ACTION_ALWAYS
+ private set
+
+ fun setShowAsAction(actionView: String?) {
+ showAsAction = when (actionView) {
+ "collapse" -> MenuItem.SHOW_AS_ACTION_ALWAYS or MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
+ "always" -> MenuItem.SHOW_AS_ACTION_ALWAYS
+ else -> MenuItem.SHOW_AS_ACTION_ALWAYS
+ }
+ }
+
private var searchViewFormatter: SearchViewFormatter? = null
private var areListenersSet: Boolean = false
diff --git a/android/src/main/res/base/drawable-anydpi/ic_action_search.xml b/android/src/main/res/base/drawable-anydpi/ic_action_search.xml
new file mode 100644
index 0000000000..6796805c38
--- /dev/null
+++ b/android/src/main/res/base/drawable-anydpi/ic_action_search.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/android/src/main/res/base/drawable-hdpi/ic_action_search.png b/android/src/main/res/base/drawable-hdpi/ic_action_search.png
new file mode 100644
index 0000000000..056f7f6270
Binary files /dev/null and b/android/src/main/res/base/drawable-hdpi/ic_action_search.png differ
diff --git a/android/src/main/res/base/drawable-mdpi/ic_action_search.png b/android/src/main/res/base/drawable-mdpi/ic_action_search.png
new file mode 100644
index 0000000000..ca8f4a7629
Binary files /dev/null and b/android/src/main/res/base/drawable-mdpi/ic_action_search.png differ
diff --git a/android/src/main/res/base/drawable-xhdpi/ic_action_search.png b/android/src/main/res/base/drawable-xhdpi/ic_action_search.png
new file mode 100644
index 0000000000..46183233ce
Binary files /dev/null and b/android/src/main/res/base/drawable-xhdpi/ic_action_search.png differ
diff --git a/android/src/main/res/base/drawable-xxhdpi/ic_action_search.png b/android/src/main/res/base/drawable-xxhdpi/ic_action_search.png
new file mode 100644
index 0000000000..96d063da4e
Binary files /dev/null and b/android/src/main/res/base/drawable-xxhdpi/ic_action_search.png differ
diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerDelegate.java
index 9d07db9b60..d3b167da1d 100644
--- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerDelegate.java
+++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerDelegate.java
@@ -57,6 +57,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "disableBackButtonOverride":
mViewManager.setDisableBackButtonOverride(view, value == null ? false : (boolean) value);
break;
+ case "showAsAction":
+ mViewManager.setShowAsAction(view, (String) value);
+ break;
case "inputType":
mViewManager.setInputType(view, value == null ? null : (String) value);
break;
diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerInterface.java
index 04cd889eb5..37f8b34b8e 100644
--- a/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerInterface.java
+++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSSearchBarManagerInterface.java
@@ -25,6 +25,7 @@ public interface RNSSearchBarManagerInterface {
void setTintColor(T view, @Nullable Integer value);
void setTextColor(T view, @Nullable Integer value);
void setDisableBackButtonOverride(T view, boolean value);
+ void setShowAsAction(T view, @Nullable String value);
void setInputType(T view, @Nullable String value);
void setHintTextColor(T view, @Nullable Integer value);
void setHeaderIconColor(T view, @Nullable Integer value);
diff --git a/apps/src/tests/Test2744.tsx b/apps/src/tests/Test2744.tsx
new file mode 100644
index 0000000000..4d57cfa163
--- /dev/null
+++ b/apps/src/tests/Test2744.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import { NavigationContainer, ParamListBase } from '@react-navigation/native';
+import { NativeStackNavigationProp, createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button, View, ScrollView } from 'react-native';
+
+type StackRouteParamList = {
+ Home: undefined;
+ SearchAlways: undefined;
+ SearchCollapse: undefined;
+};
+
+type NavigationProp = {
+ navigation: NativeStackNavigationProp;
+};
+
+type StackNavigationProp = NavigationProp;
+
+const SearchAlways = ({navigation}: StackNavigationProp) => {
+ return
+
+}
+
+const SearchCollapse = ({navigation}: StackNavigationProp) => {
+ return
+
+}
+
+const Home = ({navigation}: StackNavigationProp) =>
+
+
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/src/tests/index.ts b/apps/src/tests/index.ts
index 987d427c76..85b517de56 100644
--- a/apps/src/tests/index.ts
+++ b/apps/src/tests/index.ts
@@ -128,6 +128,7 @@ export { default as Test2668 } from './Test2668';
export { default as Test2675 } from './Test2675';
export { default as Test2714 } from './Test2714';
export { default as Test2717 } from './Test2717';
+export { default as Test2744 } from './Test2744';
export { default as Test2767 } from './Test2767';
export { default as Test2789 } from './Test2789';
export { default as Test2809 } from './Test2809';
diff --git a/src/fabric/SearchBarNativeComponent.ts b/src/fabric/SearchBarNativeComponent.ts
index 467442aa2a..4d6707b022 100644
--- a/src/fabric/SearchBarNativeComponent.ts
+++ b/src/fabric/SearchBarNativeComponent.ts
@@ -23,6 +23,8 @@ type SearchBarPlacement = 'automatic' | 'inline' | 'stacked';
type AutoCapitalizeType = 'none' | 'words' | 'sentences' | 'characters';
+type SearchShowAsAction = 'default' | 'always' | 'collapse';
+
export interface NativeProps extends ViewProps {
onSearchFocus?: DirectEventHandler | null;
onSearchBlur?: DirectEventHandler | null;
@@ -43,6 +45,7 @@ export interface NativeProps extends ViewProps {
// Android only
disableBackButtonOverride?: boolean;
+ showAsAction?: WithDefault
// TODO: consider creating enum here
inputType?: string;
onClose?: DirectEventHandler | null;
diff --git a/src/types.tsx b/src/types.tsx
index ce598e7ff9..8d0f4c199c 100644
--- a/src/types.tsx
+++ b/src/types.tsx
@@ -80,6 +80,7 @@ export type HeaderSubviewTypes =
| 'left'
| 'center'
| 'searchBar';
+export type SearchShowAsAction = 'default' | 'always' | 'collapse';
export type HeaderHeightChangeEventType = {
headerHeight: number;
@@ -733,6 +734,16 @@ export interface SearchBarProps {
* @platform ios
*/
tintColor?: ColorValue;
+ /**
+ * @platform android
+ *
+ * Controls the behavior of SearchView on the Toolbar/ActionBar. When set to `collapse`,
+ * clicking 'Up' for the first time takes the focus from the SearchView, and current screen
+ * is popped on the second click. When set to `default` or `always`, current screen is dismissed immediately.
+ * This is equivalent to setting android's `showAsAction` to `SHOW_AS_ACTION_ALWAYS`
+ * or `SHOW_AS_ACTION_ALWAYS | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW`.
+ */
+ showAsAction?: SearchShowAsAction;
/**
* The text to be used instead of default `Cancel` button text
*