Thursday, 19 August 2021

Using insets on an AutoCompleteTextView's dropdown background

I have an AutoCompleteTextView that's defined like this in my layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="20dp"
    tools:context=".ui.search.PlaceSearchFragment">

    <AutoCompleteTextView
        android:id="@+id/autoCompleteTxt"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="10dp"
        android:scrollbars="none"
        android:background="@drawable/search_view_bg"
        android:dropDownHeight="wrap_content"
        android:fontFamily="@font/work_sans_light"
        android:hint="@string/place_lookup_hint"
        android:paddingStart="20dp"
        android:paddingTop="2dp"
        android:paddingEnd="10dp"
        android:textColor="@color/black"
        android:textColorHint="@color/text_highlight_gray"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/getCurrentLocBtn"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.appcompat.widget.AppCompatImageButton
        android:id="@+id/getCurrentLocBtn"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:padding="10dp"
        android:layout_marginStart="10dp"
        android:tint="@color/black"
        android:background="@drawable/search_view_bg"
        android:src="@drawable/ic_map_pin_alt"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/autoCompleteTxt"
        app:layout_constraintBottom_toBottomOf="@id/autoCompleteTxt"/>

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animView"
        android:layout_width="wrap_content"
        android:layout_height="300dp"
        app:lottie_rawRes="@raw/loc_selection"
        app:lottie_autoPlay="true"
        app:lottie_loop="true"
        app:layout_constraintTop_toBottomOf="@id/getCurrentLocBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:id="@+id/waitingForLoc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/waiting_for_location_selection_msg"
        android:textColor="@color/black"
        android:textSize="16sp"
        android:fontFamily="@font/work_sans"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/animView"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Before being clicked, it uses the search_view_bg.xml drawable which is this one:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"/>
    <stroke android:color="@color/btn_bg" android:width="1dp"/>
    <corners android:radius="200dp"/>
</shape>

Once the user enters some text into it, I change the background to the following search_view_bg_clicked.xml drawable which uses a negative bottom inset in order to hide the stroke at the bottom in an attempt to make it blend in with the dropdown's background. Here's that drawable:

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:insetBottom="-1dp">
    <shape>
        <solid android:color="@color/white" />
        <stroke
            android:width="1dp"
            android:color="@color/btn_bg" />
        <corners
            android:bottomLeftRadius="0dp"
            android:bottomRightRadius="0dp"
            android:topLeftRadius="5dp"
            android:topRightRadius="5dp" />
    </shape>
</inset>

I'm also setting the dropdown background resource to the following layout which also uses a negative inset at the top in order to blend with the background of the ACTV:

<?xml version="1.0" encoding="utf-8"?>
<inset android:insetTop="-1dp"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <shape>
        <corners
            android:bottomLeftRadius="5dp"
            android:bottomRightRadius="5dp"
            android:topLeftRadius="0dp"
            android:topRightRadius="0dp" />
        <solid android:color="@color/white" />
        <stroke
            android:width="1dp"
            android:color="@color/btn_bg" />
    </shape>
</inset>

Despite my attempt, the stroke line at the top of the dropdown background is still shown, what am I missing here? I know that the inset of the background of the ACTV is working correctly because it loads before the dropdown and I can see the bottom stroke line missing.

Here's the fragment's code that sets up the ACTV:

private fun setUpPlacesAutocompleteTextView() {
        val onPlaceSelected: ((String) -> Unit?) = { placeId ->
            viewModel.storePlaceLocation(placeId, requireContext())
            navigateToMainFragment()
        }
        val suggestions: ArrayList<AutocompletePrediction> = arrayListOf()
        PlacesAutoCompleteSuggestionsAdapter(
            requireContext(),
            R.layout.place_search_dropdown_item,
            suggestions,
            onPlaceSelected
        )
            .also { adapter ->
                binding?.autoCompleteTxt?.setAdapter(adapter)
            }

        setAutoCompleteTextViewDropdownBg()
        binding?.autoCompleteTxt?.doOnTextChanged { query, _, _, _ ->
            if (query!!.length >= 3) {
                // delete previous suggestion entries
                suggestions.clear()

                // Create a new token for the autocomplete session. Pass this to FindAutocompletePredictionsRequest,
                // and once again when the user makes a selection (for example when calling fetchPlace()).
                val token = AutocompleteSessionToken.newInstance()

                // Use the builder to create a FindAutocompletePredictionsRequest.
                val request =
                    FindAutocompletePredictionsRequest.builder()
                        .setTypeFilter(TypeFilter.CITIES)
                        .setSessionToken(token)
                        .setQuery(query.toString())
                        .build()

                val placesClient = Places.createClient(requireContext())
                placesClient.findAutocompletePredictions(request).addOnSuccessListener { response ->
                    for (prediction in response.autocompletePredictions) {
                        // add the item to the arrayList
                        suggestions.add(prediction)
                    }
                    // notify the adapter that the data has changed
                    (binding?.autoCompleteTxt?.adapter as PlacesAutoCompleteSuggestionsAdapter).notifyDataSetChanged()
                    setAutoCompleteTextViewBgOnClick()
                }.addOnFailureListener { exception ->
                    if (exception is ApiException) {
                        Log.e(TAG, "Place not found: " + exception.statusCode)
                    }
                }
            }
        }
    }

private fun setAutoCompleteTextViewBgOnClick() {
    binding?.autoCompleteTxt?.setBackgroundResource(R.drawable.search_view_bg_clicked)
}

private fun resetAutoCompleteTextViewBg() {
    binding?.autoCompleteTxt?.setBackgroundResource(R.drawable.search_view_bg)
}

private fun setAutoCompleteTextViewDropdownBg() {
    binding?.autoCompleteTxt?.setDropDownBackgroundDrawable(
        ResourcesCompat.getDrawable(resources, R.drawable.search_view_dropdown_bg, null)
    )
}

Here's the current state of the layout:

curr_state



from Using insets on an AutoCompleteTextView's dropdown background

No comments:

Post a Comment