Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoCompleteTextView: detecting when dropdown is dismissed and item is NOT selected

I need to know when the user taps outside of the AutoCompleteTextView dropdown in order to dismiss it (i.e. they dismiss the popup with selecting an item in the list). I've setup the setOnDismissListener() as shown here:

    mAutoView.setOnDismissListener(new AutoCompleteTextView.OnDismissListener() {
        @Override
        public void onDismiss() {
            CharSequence msg = "isPerformingCompletion = " + mAutoView.isPerformingCompletion() +
                    "   Item selected at = " + mAutoView.getListSelection();
            Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
        }
    });

And an OnItemClickListener like this:

 private AdapterView.OnItemClickListener mAutocompleteClickListener
        = new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        // get selected item and pass it to result callback
    }
};

The onDismiss event fires before the onItemClick event, and unfortunately, neither the "isPerformingCompletion()" nor the "getListSelection()" methods return a value until the onItemClick event fires.

Can anyone suggest an approach to detecting a dismiss without a list selection?

like image 797
John Ward Avatar asked Nov 22 '25 09:11

John Ward


2 Answers

Below piece of code will detect that if user dismiss the dropdown of autocompletetextview, Then onDismiss it will check the selected input using Geocode API, If it is having a result then selected input is valid otherwise input is not valid, So In that case, Entered text will be vanish in bit seconds.

mAutoView.setOnDismissListener {
    try {
        val fromLocationName = Geocoder(context).getFromLocationName(mAutoView.getText().toString(), 1)
        if (fromLocationName != null && fromLocationName.isNotEmpty()) {
            Log.d(TAG, "Address valid")
        } else {
            mAutoView.setText("")
            Log.d(TAG, "Address not valid")
        }
    } catch (e: Exception) {
        mAutoView.setText("")
        Log.d(TAG, "Address not valid with Exception")
    }
}

Means you need a kind of validator in OnDismiss, That will check the input text is valid or not, Based on validation you can indicate to user that entered input is valid or not.

like image 75
Rahul Avatar answered Nov 24 '25 01:11

Rahul


AutoCompleteTextView: detecting when dropdown is dismissed and item is NOT selected

If I understood you well, you need to catch the event of dismissing the draopDown by touching outside of it instead of choosing an item.

For some reason, the setOnDismissListener doesn't work for me. I couldn't find a clue without touching the inner ListPopupWindow which get called on either event (item click or touch outside).

The event is differentiated by registering an inner OnItemClickListener listener to set the tag of the view to some value that indicates that BY_ITEM_CLICK ; if the tag is anything else; it will be considered a touch outside event.

Here is a custom AutoCompleteTextView that can be used for that:

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.View
import android.widget.AdapterView
import android.widget.AutoCompleteTextView
import android.widget.ListPopupWindow
import androidx.appcompat.widget.AppCompatAutoCompleteTextView


class OnDismissAutoCompleteTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : AppCompatAutoCompleteTextView(context, attrs), AdapterView.OnItemClickListener {

    interface OnMenuDismissListener {
        fun onTouchOutside()
        fun onItemClick()
        fun onBackPressed()
    }

    companion object {

        // set the tag to this value when an item is clicked to dismiss the menu
        const val BY_ITEM_CLICK = "BY_ITEM_CLICK"

        // set the tag to this value when the back is pressed to dismiss the menu
    }

    init {
        super.setOnItemClickListener(this)
    }

    var onMenuDismissListener: OnMenuDismissListener? = null

    private var consumerListener: AdapterView.OnItemClickListener? = null


    @SuppressLint("DiscouragedPrivateApi")
    private fun getPopup(): ListPopupWindow? {
        try {
            val field = AutoCompleteTextView::class.java.getDeclaredField("mPopup")
            field.isAccessible = true
            return field.get(this) as ListPopupWindow
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        return null
    }

    private fun setupDismissListener() {
        getPopup()?.let {
            it.setOnDismissListener {

                when (tag) {
                    BY_ITEM_CLICK -> onMenuDismissListener?.onItemClick() // Menu dismissal Event of clicking on the menu item

                    BY_BACK_PRESSED -> onMenuDismissListener?.onBackPressed()

                    else -> onMenuDismissListener?.onTouchOutside() // Menu dismissal Event of touching outside the menu
                }
                // reset the tag for the next dismissal
                tag = null
            }

        }
    }

    override fun onPreDraw(): Boolean {
        // Registering the mPopup window OnDismissListener
        setupDismissListener()
        return super.onPreDraw()
    }

    override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
        tag = BY_ITEM_CLICK
        consumerListener?.onItemClick(p0, p1, p2, p3)
    }

    override fun setOnItemClickListener(l: AdapterView.OnItemClickListener?) {
        // DO NOT CALL SUPER HERE
//        super.setOnItemClickListener(l)
        consumerListener = l
    }

    override fun onKeyPreIme(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing)
            tag = BY_BACK_PRESSED
        return super.onKeyPreIme(keyCode, event)
    }

}

Usage:

myAutoCompleteTV.onMenuDismissListener = object :
    OnDismissAutoCompleteTextView.OnMenuDismissListener {
    override fun onTouchOutside() {
        // Menu dismiss is due to touch outside event
        Toast.makeText(context, "touch outside", Toast.LENGTH_SHORT).show()
    }

    override fun onItemClick() {
        // Menu dismiss is due to clicking on an item
        Toast.makeText(context, "item clicked", Toast.LENGTH_SHORT).show()
    }

    override fun onBackPressed() {
        Toast.makeText(context, "back pressed", Toast.LENGTH_SHORT).show()
    }
}
like image 24
Zain Avatar answered Nov 24 '25 00:11

Zain



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!