Reputation: 940
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?
Upvotes: 3
Views: 1873
Reputation: 40878
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()
}
}
Upvotes: 1
Reputation: 641
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.
Upvotes: 2