Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search Bar looks broken when used along with a custom header title in Android #1858

Open
ha3 opened this issue Aug 3, 2023 · 6 comments
Open
Assignees
Labels
Platform: Android This issue is specific to Android Repro provided A reproduction with a snack or repo is provided

Comments

@ha3
Copy link

ha3 commented Aug 3, 2023

Description

In Android, when search bar is enabled along with a custom header title, they are rendered on top of each other. The problem persists after search bar is focused. It works correctly if a string is provided for the headerTitle.

Steps to reproduce

  1. Add search bar and a custom title to a screen
  2. Navigate to that screen
  3. See search bar looks broken

Snack or a link to a repository

https://snack.expo.dev/@hozdemir/groaning-apples

Screens version

3.23.0

React Native version

0.72.3

Platforms

Android

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

Real device

Device model

Redmi Note 7

Acknowledgements

Yes

@github-actions github-actions bot added Platform: Android This issue is specific to Android Repro provided A reproduction with a snack or repo is provided labels Aug 3, 2023
@ha3
Copy link
Author

ha3 commented Aug 4, 2023

@kkafar hi. I created this patch, but not sure this is the correct approach–I'm quite inexperienced on the native side.

This solves the title and search bar being rendered on top of each other. But, in a real device, the search icon ends up aligned incorrectly to the left, if the search bar is blurred. On the emulator, everything works as expected.

diff --git a/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt b/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt
index 6984b10..17e23f0 100644
--- a/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt
+++ b/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt
@@ -4,6 +4,7 @@ import android.content.Context
 import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.widget.SearchView
 import androidx.fragment.app.Fragment
+import kotlin.collections.ArrayList
 
 class CustomSearchView(context: Context, fragment: Fragment) : SearchView(context) {
     /*
@@ -13,8 +14,9 @@ class CustomSearchView(context: Context, fragment: Fragment) : SearchView(contex
         setOnSearchClickListener - https://developer.android.com/reference/android/widget/SearchView#setOnSearchClickListener(android.view.View.OnClickListener)
         setOnCloseListener - https://developer.android.com/reference/android/widget/SearchView#setOnCloseListener(android.widget.SearchView.OnCloseListener)
     */
-    private var mCustomOnCloseListener: OnCloseListener? = null
-    private var mCustomOnSearchClickedListener: OnClickListener? = null
+
+    private var mCustomOnCloseListeners: MutableList<OnCloseListener> = ArrayList()
+    private var mCustomOnSearchClickedListeners: MutableList<OnClickListener> = ArrayList()
 
     private var mOnBackPressedCallback: OnBackPressedCallback =
         object : OnBackPressedCallback(true) {
@@ -40,12 +42,12 @@ class CustomSearchView(context: Context, fragment: Fragment) : SearchView(contex
 
     fun setText(text: String) = setQuery(text, false)
 
-    override fun setOnCloseListener(listener: OnCloseListener?) {
-        mCustomOnCloseListener = listener
+    override fun setOnCloseListener(listener: OnCloseListener) {
+        mCustomOnCloseListeners.add(listener)
     }
 
-    override fun setOnSearchClickListener(listener: OnClickListener?) {
-        mCustomOnSearchClickedListener = listener
+    override fun setOnSearchClickListener(listener: OnClickListener) {
+        mCustomOnSearchClickedListeners.add(listener)
     }
 
     override fun onAttachedToWindow() {
@@ -62,14 +64,14 @@ class CustomSearchView(context: Context, fragment: Fragment) : SearchView(contex
 
     init {
         super.setOnSearchClickListener { v ->
-            mCustomOnSearchClickedListener?.onClick(v)
+            mCustomOnSearchClickedListeners.forEach { it.onClick(v) }
             backPressOverrider.maybeAddBackCallback()
         }
 
         super.setOnCloseListener {
-            val result = mCustomOnCloseListener?.onClose() ?: false
+            mCustomOnCloseListeners.forEach { it.onClose() }
             backPressOverrider.removeBackCallbackIfAdded()
-            result
+            false
         }
 
         maxWidth = Integer.MAX_VALUE
diff --git a/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
index 86a7654..391ac4b 100644
--- a/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
+++ b/node_modules/react-native-screens/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt
@@ -147,6 +147,25 @@ class ScreenStackFragment : ScreenFragment {
         return false
     }
 
+    private val subViewsToHideDuringSearch: MutableList<ScreenStackHeaderSubview>
+        get() {
+            val arr: MutableList<ScreenStackHeaderSubview> = ArrayList()
+
+            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) {
+                        arr.add(subView)
+                    }
+                }
+            }
+
+            return arr
+        }
+
     private fun updateToolbarMenu(menu: Menu) {
         menu.clear()
         if (shouldShowSearchBar()) {
@@ -154,6 +173,13 @@ class ScreenStackFragment : ScreenFragment {
             if (searchView == null && currentContext != null) {
                 val newSearchView = CustomSearchView(currentContext, this)
                 searchView = newSearchView
+                newSearchView.setOnSearchClickListener {
+                    subViewsToHideDuringSearch.forEach { it.visibility = View.GONE }
+                }
+                newSearchView.setOnCloseListener {
+                    subViewsToHideDuringSearch.forEach { it.visibility = View.VISIBLE }
+                    false
+                }
                 onSearchViewCreate?.invoke(newSearchView)
             }
             menu.add("").apply {

@Acetyld
Copy link

Acetyld commented Aug 10, 2023

Exact problem i am facing, same with the headerRight icons, i cant seem to find a solid way, atm i got:

  const navigation = useNavigation<TasksScreenNavigationProp<'index'>>();
  const [search, setSearch] = React.useState('');
  const [isSearching, setIsSearching] = React.useState(false);
  const taskCount = 8381;
  const searchBarRef = useRef<SearchBarCommands>(null);
  useLayoutEffect(() => {
    navigation.setOptions({
      headerSearchBarOptions: {
        autoCapitalize: 'none',
        cancelButtonText: 'Annuleer',
        placeholder: 'Zoeken',
        shouldShowHintSearchIcon: false,
        textColor: colors.white,
        hintTextColor: colors.primary['300'],
        tintColor: colors.white,
        headerIconColor: colors.white,
        onChangeText: event => setSearch(event.nativeEvent.text),
        ref: searchBarRef,
        onOpen: async () => {
          setIsSearching(true);
        },
        onClose: async () => {
          console.log('close');
          setIsSearching(false);
        },
      },
      headerTitle: props =>
        !isSearching ? (
          <HeaderTitle
            style={{
              marginLeft: Platform.OS === 'android' ? -20 : 0,
            }}
            {...props}>{`${taskCount} openstaande taken`}</HeaderTitle>
        ) : null,
      headerRight: () =>
        !isSearching ? (
          <View
            className={'h-full flex-row'}
            style={{
              gap: 10,
            }}>
            <Pressable hitSlop={7} className={'active:opacity-50'}>
              <FontAwesomeIcon
                color={'#fff'}
                icon={['far', 'ellipsis-vertical']}
                size={20}
              />
            </Pressable>
            <Pressable hitSlop={7} className={'active:opacity-50'}>
              <FontAwesomeIcon
                color={'#fff'}
                icon={['far', 'filter']}
                size={20}
              />
            </Pressable>
          </View>
        ) : null,
    });
  }, [isSearching, navigation]);

But this is causing a small flicker on open

@ha3
Copy link
Author

ha3 commented Oct 29, 2023

@tboba thanks for the #1903 which also addresses this issue. Changing the visibility of header elements, Instead of pushing them to the side, was my approach to the problem too. Unfortunately, with 3.27.0 I encountered the same problem that I had–the search icon is misaligned after the blur on certain Android devices, i.e. Xiaomi and Huawei.

On these devices, the search icon is pushed significantly to the left after the blur whether a custom header element is used or not. Here are some screenshots of the problem. It is tested on Xiaomi Redmi:

No custom element - Before focus:
image

No custom element - After blur:
image

Custom header left - Before focus:
Screenshot_2023-10-29-16-17-34-764_com meel meel

Custom header left - After blur:
Screenshot_2023-10-29-16-17-03-775_com meel meel

@alduzy
Copy link
Member

alduzy commented Jun 28, 2024

Hi, I've tested it with the latest version and could not reproduce the issue. I'll try again using Xiaomi/Huawei device in upcoming days. In the meantime, can anyone confirm that the problem still persists?

@alduzy alduzy added the Close when stale This issue is going to be closed when there is no activity for a while label Jul 17, 2024
@ha3
Copy link
Author

ha3 commented Jul 24, 2024

@alduzy can confirm that the issue still persists with 3.32.0 on a Huawei device.

@github-actions github-actions bot removed the Close when stale This issue is going to be closed when there is no activity for a while label Jul 24, 2024
@alduzy
Copy link
Member

alduzy commented Aug 1, 2024

@ha3 Could you provide a new reproduction snack? I've just tried the last provided snack on Huawei, Redmi and Xiaomi device and wasn't able to reproduce the issue. Also, there's a @react-navigation dependency version mismatch and the options for the screen are set using screenOptions instead of options. Please ensure that the dependencies versions specified in the snack are correct.

@alduzy alduzy assigned kkafar and unassigned alduzy Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform: Android This issue is specific to Android Repro provided A reproduction with a snack or repo is provided
Projects
None yet
Development

No branches or pull requests

4 participants