Multiple spinners and onItemSelected - android

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
}
}
}

Related

Android Spinner - Differentiate manually and programmatically selection

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()
}
}

How to display a drawable according to a condition entered by the user in Android studio(Java)

I have a list of items displayed in Recycler view and they are sorted according to their priority (1,2,3) entered by the user with 1 on top and 3 at the bottom. I want the items to be accompanied by a drawable in red(for priority 1), yellow(for priority 2) and green(for priority 3) depending on the input from the user. May I
please know how to display the drawable in such a case.
picture 1
Picture 2
You can do this in onBindViewHolder method of the adapter or create a bind method in your view holder and then call it from onBindViewHolder
For the latter, in your view holder create bind method
In Kotlin
class TaskViewHolder(private val taskItemBinding: TaskItemBinding) :
RecyclerView.ViewHolder(taskItemBinding.root) {
fun bind(task: Task) {
taskItemBinding.apply {
priorityIndicator.setImageDrawable(
ContextCompat.getDrawable(
priorityIndicator.context,
when (task.priority) {
1 -> R.drawable.indicator_red
2 -> R.drawable.indicator_yellow
3 -> R.drawable.indicator_green
}
)
)
}
}
}
Now, call it from onBindViewHolder
override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
val task = getItem(position)
holder.bind(task)
}
In Java
public class TaskViewHolder extends RecyclerView.ViewHolder {
ImageView priorityIndicator;
public TaskViewHolder(#NonNull View itemView) {
super(itemView);
priorityIndicator = itemView.findViewById(R.id.priorityIndicator);
}
private void bind(Task task) {
int drawableId;
switch (task.priority) {
case 1: drawableId = R.drawable.indicator_red;
break;
case 2: drawableId = R.drawable.indicator_yellow;
break;
default: drawableId = R.drawable.indicator_green;
}
priorityIndicator.setImageDrawable(
ContextCompat.getDrawable(priorityIndicator.getContext(), drawableId)
);
}
}
Now call it from onBindViewHolder
#Override
public void onBindViewHolder(#NonNull TaskViewHolder holder, int position) {
holder.bind(taskList.get(position));
}
So the color of the layout is always the same right? Red for 1st index, yellow for 2nd, and green for 3rd?
On your RecyclerView adapter you can pass a position parameter at onBindViewHolder and add condition based on the position. For example:
override fun onBindViewHolder(holder: listHolder, position: Int) {
holder.bind(listItem[position], position)
}
class ListHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
fun bind(item: YourItem, position) {
when(position){
0 -> binding.imageView.setImageResource(R.drawable.redDrawable)
1 -> binding.imageView.setImageResource(R.drawable.yellowDrawable)
2 -> binding.imageView.setImageResource(R.drawable.greenDrawable)
}
}
}

Android Spinner Make Item Clickable but not Selectable

Is it possible to have an item in my spinner which is clickable but not selectable?
The scenario here is that i have a Category spinner and i want the user to input his category himself. So i want the "add Item" selection to appear at the end of the Spinner's list and make it clickable only. Can someone help ?
You can do one thing, in listener if you got last position by default select 0 position and do you click action whatever you want.
yourSpinner?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
// if position == n, open dialog here
// and then
yourSpinner?.setSelectablePosition(0);
}
}
you can do like
txtTimeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(position == last){
txtTimeSpinner.setSelection(0);
}else {
//your code here for selection
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
and array first position is as default like ("Select Time")

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
}
}
}
}

Categories

Resources