Another option to watch for those events is to extend the Spinner
class and use one of its methods(performClick()
which will trigger its dialog/popup) followed by monitoring the focus of the window holding this custom Spinner
. This should provide you with the wanted closed event for all the possible finishing possibilities(for either the dialog or dropdown mode).
The custom Spinner
class:
public class CustomSpinner extends Spinner {
/**
* An interface which a client of this Spinner could use to receive
* open/closed events for this Spinner.
*/
public interface OnSpinnerEventsListener {
/**
* Callback triggered when the spinner was opened.
*/
void onSpinnerOpened(Spinner spinner);
/**
* Callback triggered when the spinner was closed.
*/
void onSpinnerClosed(Spinner spinner);
}
private OnSpinnerEventsListener mListener;
private boolean mOpenInitiated = false;
// implement the Spinner constructors that you need
@Override
public boolean performClick() {
// register that the Spinner was opened so we have a status
// indicator for when the container holding this Spinner may lose focus
mOpenInitiated = true;
if (mListener != null) {
mListener.onSpinnerOpened(this);
}
return super.performClick();
}
@Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasBeenOpened() && hasFocus) {
performClosedEvent();
}
}
/**
* Register the listener which will listen for events.
*/
public void setSpinnerEventsListener(
OnSpinnerEventsListener onSpinnerEventsListener) {
mListener = onSpinnerEventsListener;
}
/**
* Propagate the closed Spinner event to the listener from outside if needed.
*/
public void performClosedEvent() {
mOpenInitiated = false;
if (mListener != null) {
mListener.onSpinnerClosed(this);
}
}
/**
* A boolean flag indicating that the Spinner triggered an open event.
*
* @return true for opened Spinner
*/
public boolean hasBeenOpened() {
return mOpenInitiated;
}
}
based on @Luksprog wonderful solution,i just want to add a small change which will be very helpful in case someone is using the CustomSpinner inside a fragment.
instead of using the Activity.onWindowFocusChanged
function, we override the View.onWindowFocusChanged
function. thus the whole CustomSpinner class become
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;
public class CustomSpinner extends Spinner {
private static final String TAG = "CustomSpinner";
private OnSpinnerEventsListener mListener;
private boolean mOpenInitiated = false;
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinner(Context context, int mode) {
super(context, mode);
}
public CustomSpinner(Context context) {
super(context);
}
public interface OnSpinnerEventsListener {
void onSpinnerOpened();
void onSpinnerClosed();
}
@Override
public boolean performClick() {
// register that the Spinner was opened so we have a status
// indicator for the activity(which may lose focus for some other
// reasons)
mOpenInitiated = true;
if (mListener != null) {
mListener.onSpinnerOpened();
}
return super.performClick();
}
public void setSpinnerEventsListener(OnSpinnerEventsListener onSpinnerEventsListener) {
mListener = onSpinnerEventsListener;
}
/**
* Propagate the closed Spinner event to the listener from outside.
*/
public void performClosedEvent() {
mOpenInitiated = false;
if (mListener != null) {
mListener.onSpinnerClosed();
}
}
/**
* A boolean flag indicating that the Spinner triggered an open event.
*
* @return true for opened Spinner
*/
public boolean hasBeenOpened() {
return mOpenInitiated;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
android.util.Log.d(TAG, "onWindowFocusChanged");
super.onWindowFocusChanged(hasWindowFocus);
if (hasBeenOpened() && hasWindowFocus) {
android.util.Log.i(TAG, "closing popup");
performClosedEvent();
}
}
}
Hi friends I am struggling on this issue from last two days and finally I got following solution which done my job. I tried and it worked perfectly. Thanks
mSpinner.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
Toast.makeText(MapActivity.this,"down",Toast.LENGTH_LONG).show();
// Load your spinner here
}
return false;
}
});
After spending the day looking at all the solutions, here is my easy fix for detecting the opening and closing of Spinner and also how focusing outside spinner closes the spinner.
Step 1: Adding a addOnWindowFocusChangeListener to your Spinner in Fragment or Activity.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val spinner = spinner_view
val arrayAdapter = ArrayAdapter<RestoreManager.ConnectionType>(context!!, R.layout.layout_backup_spinner)
arrayAdapter.setDropDownViewResource(R.layout.spinner_item)
spinner?.let {
val spinnerAdapter = SpinnerAdapter(activity!!)
it.adapter = spinnerAdapter
it.setSelection(0)
it.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, position: Int, id: Long) {}
override fun onNothingSelected(parentView: AdapterView<*>) {}
}
it.viewTreeObserver?.addOnWindowFocusChangeListener { hasFocus -> //This updates the arrow icon up/down depending on Spinner opening/closing
spinnerAdapter .spinnerOpen = hasFocus
spinnerAdapter .notifyDataSetChanged()
}
}
}
addOnWindowFocusChangeListener is called every time the spinner opens or closes. It also get triggered when spinner is open and user tap outside the spinner to close the spinner. In this method you can update the UI of your SpinnerAdapter.
For my use case, i wanted to show the arrow icon up and down when the spinner opens and closes. So i set the flag spinnerAdapter.spinnerOpen
in my Spinner Adapter.
Step 2: In your SpinnerAdapter override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {}
is called every time spinner open or close. Here is the code in SpinnerAdapter:
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val spinView = if (convertView == null) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.layout_backup_spinner, null)
} else {
convertView
}
var arrowIcon = spinView.findViewById<ImageView>(R.id.arrow_icon)
if (spinnerOpen) arrowIcon.setImageResource(R.drawable.arrow_down)
else arrowIcon.setImageResource(R.drawable.arrow_up)
return spinView
}
There's no built in function but it's pretty easy to do with an OnTouchListener
and OnItemSelectedListener
.
abstract class OnOpenListener implements OnTouchListener, OnItemSelectedListener {
public OnOpenListener(Spinner spinner) {
spinner.setOnTouchListener(this);
spinner.setOnItemSelectedListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
onOpen();
}
return false;
}
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
onClose();
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
onClose();
}
abstract public void onOpen();
abstract public void onClose();
}
And then assign the appropriate listeners:
OnOpenListener onOpenListener = new OnOpenListener(mySpinner) {
@Override
public void onOpen() {
// spinner was opened
}
@Override
public void onClose() {
// spinner was closed
}
};
I think the best way to find when it got opened and closed is this way:
If it was closed, and now it calls "getDropDownView" in the adapter, it can be assumed that it got opened.
If "onItemSelected" or "onNothingSelected" are called, now it got closed.
EDIT: here's a sample code
public class MainActivity extends AppCompatActivity {
boolean isSpinnerClosed = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatSpinner spinner2 = (AppCompatSpinner) findViewById(R.id.spinner2);
List<String> list = new ArrayList<String>();
list.add("list 1");
list.add("list 2");
list.add("list 3");
Log.d("AppLog", "started");
//spinner2.setondi
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) {
@Override
public View getDropDownView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
if (isSpinnerClosed) {
Log.d("AppLog", "closed->open");
isSpinnerClosed = false;
}
return super.getDropDownView(position, convertView, parent);
}
};
spinner2.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(final AdapterView<?> adapterView, final View view, final int i, final long l) {
Log.d("AppLog", "onItemSelected");
if (!isSpinnerClosed) {
Log.d("AppLog", "open->closed");
isSpinnerClosed = true;
}
}
@Override
public void onNothingSelected(final AdapterView<?> adapterView) {
Log.d("AppLog", "onNothingSelected");
if (!isSpinnerClosed) {
Log.d("AppLog", "open->closed");
isSpinnerClosed = true;
}
}
});
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner2.setAdapter(dataAdapter);
}
@Override
public void onWindowFocusChanged(final boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && isSpinnerClosed) {
Log.d("AppLog", "open->closed");
isSpinnerClosed = true;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With