Android Spinner - Differentiate manually and programmatically selection - android

I am looking for a way to differentiate selection type in OnItemSelectedListener of Spinner between manual selection by tapping and by setSelection(pos)
i tried by this, but no success
binding.spinner.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>,
view: View?,
position: Int,
id: Long
) {
if (adapterView.isPressed) {
Toast.makeText(this#SplashActivity, "Manually", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#SplashActivity, "Programmatically", Toast.LENGTH_SHORT)
.show()
}
}
For reference, similar functionality is already provided by Checkbox, and it is working perfectly for checkboxes
binding.checkbox.setOnCheckedChangeListener { compoundButton, bool ->
if (compoundButton.isPressed) {
//manually
Toast.makeText(this, "Manually", Toast.LENGTH_SHORT).show()
} else {
//Programmatically
Toast.makeText(this, "Programmatically", Toast.LENGTH_SHORT).show()
}
}

Related

Make delete key on custom in-app keyboard delete text from other edit text

I've made a custom in-app keyboard (by following this tutorial). But I'd like it to delete text from a different edit text when the delete key is pressed. Is this possible to achieve?
This is my onClick code in my keyboard activity file:
override fun onClick(v: View) {
if (v.id == R.id.button_delete) {
// Delete key is pressed.
} else {
// Regular key is pressed.
val value = keyValues[v.id]
inputConnection?.commitText(value, 1)
}
}
Let me know if there's anything else you'd like to know :)
I eventually solved this by putting a character e.g: 'd' into the edittext like so:
override fun onClick(v: View) {
if (v.id == R.id.button_delete) {
inputConnection?.commitText("d", 1)
} else {
val value = keyValues[v.id]
inputConnection?.commitText(value, 1)
}
}
Then in the activity that controlls the edittext's layout, e.g: for me it was PasscodeActivity, put a TextWatcher that detects when the text inside of the edittext has changed.
private fun setEditTextListener() {
val inputEl = findViewById<EditText>(resources.getIdentifier("inputEl", "id", packageName))
inputEl.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(textBeforeChangedStr: CharSequence?, startNum: Int, countNum: Int, afterNum: Int) {
...
}
override fun onTextChanged(textAfterChangedStr: CharSequence?, startNum: Int, beforeNum: Int, countNum: Int) {
...
}
override fun afterTextChanged(editableEl: Editable?) {
if (inputEl.text.toString() == "d") { // The 'd' character is put into the edittext when the delete key is pressed.
clearOtherEditText()
}
...
}
})
}
Then in the function clearOtherEditText() I put in some code to get rid of the text in a different edittext like so:
val otherInputEl = findViewById<EditText>(resources.getIdentifier("otherInput", "id", packageName))
otherInputEl.text.clear()
Hope this helps someone else :)
try below solution
override fun onClick(v: View) {
if (v.id == R.id.button_delete) {
// Delete key is pressed.
**inputConnection.deleteSurroundingText(1, 0)**
} else {
// Regular key is pressed.
val value = keyValues[v.id]
inputConnection?.commitText(value, 1)
}
}

Last item in the recyclerview doesn't work as expected

I have a few views on my item layout which is used to display items in a recyclerView in Kotlin. Some views are to be shown/hidden based on the conditions. Every item in the recyclerview shows the views (button, editText) correctly except the last item. Even though the last item doesn't meet the criteria to show the views, that is already hidden in the XML, it shows those views. I have checked my code but I couldn't make out the reason why it's happening.
Following code is within the onBindViewHolder of my ItemListAdapter.kt class.
if (model.category == "Food") {
Log.d("CheckTag", "item is ${model.item_title} and the category is ${model.category}")
holder.binding.tvDeliveryType.visibility = View.VISIBLE
holder.binding.tvDeliveryType.text = "Same Day Delivery"
mFireStore.collection(Constants.CART_ITEMS)
.whereEqualTo(Constants.USER_ID, FirestoreClass().getCurrentUserID())
.whereEqualTo(Constants.PRODUCT_ID, model.product_id).get()
.addOnSuccessListener { document ->
if (document.documents.size > 0) {
holder.binding.llGoToCart.visibility = View.VISIBLE
} else {
holder.binding.llAddToCart.visibility = View.VISIBLE
}
}.addOnFailureListener { e ->
}
holder.binding.btnGoToCart.setOnClickListener {
context.startActivity(Intent(context, CartListActivity::class.java))
}
holder.binding.btnAddToCart.setOnClickListener {
if (holder.binding.etQuantity.text.toString().isNotEmpty()) {
if (holder.binding.etQuantity.text.toString().toInt() > 0) {
holder.binding.spnUom.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
uom = parent?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
val addToCart = Cart(
FirestoreClass().getCurrentUserID(),
model.user_id,
model.product_id,
model.title,
model.price,
model.image,
holder.binding.etQuantity.text.toString(),
uom = uom
)
FirestoreClass().addToCart(context, addToCart)
Toast.makeText(
context, "Item added to the cart", Toast.LENGTH_SHORT
).show()
holder.binding.llAddToCart.visibility = View.GONE
holder.binding.llGoToCart.visibility = View.VISIBLE
} else {
Toast.makeText(
context, "Please enter a valid quantity", Toast.LENGTH_SHORT
).show()
}
} else {
Toast.makeText(
context, "Please enter quantity required", Toast.LENGTH_SHORT
).show()
}
}
} else {
holder.binding.tvDeliveryType.visibility = View.GONE
}
It was due to the else part which was missing in the code.

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?
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.
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
const val BY_BACK_PRESSED = "BY_BACK_PRESSED"
}
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
if (consumerListener != null) {
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()
}
}

Spinner - show hint when adapter is empty

I know there have been several questions that dealt with the problem how to add the "Select one..." hint for the Spinner before the first selection is made. But that's not my case.
What I need is to display the hint only when the SpinnerAdapter is empty. By default in such case, nothing happens on click (but that is not the major problem), and most of all, the spinner doesn't display any text, so it looks like this, which obviously doesn't feel right:
Any idea how to simply handle this problem? I've come up with 2 possible solutions, but I don't like any of them very much:
If the SpinnerAdapter is empty, hide the Spinner from the layout and display a TextView with the same background as the Spinner instead.
Implement a custom SpinnerAdapter whose getCount() returns 1 instead of 0 if the internal list is empty, and at the same time, have its getView() return a TextView with the required "Empty" message, possibly grey-coloured. But that would require specific checking if the selected item is not the "Empty" one.
You can use this SpinnerWithHintAdapter class below
class SpinnerWithHintAdapter(context: Context, resource: Int = android.R.layout.simple_spinner_dropdown_item) :
ArrayAdapter<Any>(context, resource) {
override fun isEnabled(position: Int): Boolean {
return position != 0
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return (super.getDropDownView(position, convertView, parent) as TextView).apply {
if (position == 0) {
// Set the hint text color gray
setTextColor(Color.GRAY)
} else {
setTextColor(Color.BLACK)
}
}
}
fun attachTo(spinner: Spinner, itemSelectedCallback: ((Any?) -> Unit)? = null) {
spinner.apply {
adapter = this#SpinnerWithHintAdapter
itemSelectedCallback?.let {
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedItem = parent?.getItemAtPosition(position)
// If user change the default selection
// First item is disable and it is used for hint
if (position > 0) {
it(selectedItem)
}
}
}
}
}
}
}
How to use? Let's assume I have data class called City
data class City(
val id: Int,
val cityName: String,
val provinceId: Int
) {
/**
* By overriding toString function, you will show the dropdown text correctly
*/
override fun toString(): String {
return cityName
}
}
In the activity, initiate the adapter, add hint(first item), add main items, and finally attach it to your spinner.
SpinnerWithHintAdapter(this#MyActivity)
.apply {
// add hint
add("City")
// add your main items
for (city in cityList) add(city)
// attach this adapter to your spinner
attachTo(yourSpinner) { selectedItem -> // optional item selected listener
selectedItem?.apply {
if (selectedItem is City) {
// do what you want with the selected item
}
}
}
}

Multiple spinners and onItemSelected

I have two spinners that trigger the onItemSelected event. The question is How can I know which one triggered such event ? So far I tried:
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
Log.d("form","onitemselected");
switch (view.getId()) {
case R.id.region_spinner:
Region r = (Region)sregions.getSelectedItem();
Log.d("form","regionid:" + r.id);
break;
case R.id.state_spinner:
Log.d("form","state id:");
break;
}
But only the first Log is displayed, so there's no match in the switch.
use:
switch(parent.getId()) {
...
}
instead is what you need.
The view in your parameter is the actual 'row' (i.e. the clicked child of spinner item), and the parent is the actual 'spinner' that you are after.
Use below code if you have multiple spinners in one activity and you are using onItemSelected override method
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (parent!!.id == R.id.spinner_1) {
// first spinner selected
} else if (parent!!.id == R.id.spinner_2) {
// second spinner selected
}
}
either you can use switch case
Spinner is a subclass of AdapterView. The parent object passed into the method is the spinner in which the item was selected.
At first in onCreate :
Spinner cit_for_bus, bus_number;
cit_for_bus = (Spinner) findViewById(R.id.cit_for_bus);
bus_number = (Spinner) findViewById(R.id.bus_number);
cit_for_bus.setOnItemSelectedListener(this);
bus_number.setOnItemSelectedListener(this);
don't forget to use :
you have to bind/tie spinners to onItemSelected
cit_for_bus.setOnItemSelectedListener(this);
bus_number.setOnItemSelectedListener(this);
and use implement for class :
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener
outSide of onCreate use this :
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (adapterView.getId() == R.id.cit_for_bus) {
Toast.makeText(getApplicationContext(), adapterView.getId() + "/ " + adapterView.getCount() + "/" + adapterView.getSelectedItem(), Toast.LENGTH_LONG).show();
} else if (adapterView.getId() == R.id.bus_number) {
Toast.makeText(getApplicationContext(), adapterView.getId() + "/ " + adapterView.getCount() + "/" + adapterView.getSelectedItem(), Toast.LENGTH_LONG).show();
}
}
If you import your XML on Kotlin you can use it as so:
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id:
Long) {
when (parent)
firstSpinner -> {
// first spinner selection
}
secondSpinner -> {
// second spinner selected
}
}
}

Categories

Resources