Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Spinner control still display prompt information even if the event setOnItemSelectedListener is after setSelection?

Tags:

android

I write the event setOnItemSelectedListener of Spinner after spinnerRanger.setSelection.

I think Toast.makeText(...) will not launch when I run the APP for the first time, but prompt information is still displayed, why?

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.cleanup_delete_fragment_old, container, false);

    mView = rootView;
    mContext = rootView.getContext();

    InitValueOfControls();
    return rootView;
}

private void InitValueOfControls() {
    spinnerRanger = (Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);
    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));
    spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Toast.makeText(mContext, "My "+position, Toast.LENGTH_LONG).show();
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });
}

To Trevor Carothers:

If I insert a DoLongTimeOperation() before Selection listener event, will it still display the toast?

private void InitValueOfControls() {
    spinnerRanger = (Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);
    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));

    DoLongTimeOperation();

    spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Toast.makeText(mContext, "My " + position, Toast.LENGTH_LONG).show();
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });
}

Code A:

private void InitValueOfControls() {
    spinnerRanger = (Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);
    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));

    DoLongTimeOperation();

    spinnerRanger.post(new Runnable() {
        @Override
        public void run() {
            spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    Toast.makeText(mContext, "My "+position, Toast.LENGTH_LONG).show();
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {
                }
            });
        }
    });
}

Code B

private void InitValueOfControls() {
    spinnerRanger = (Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);
    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));
    spinnerRanger.post(new Runnable() {
        @Override
        public void run() {
            spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                // ...
            }
        }
    });
}

Code C

private void InitValueOfControls() {
    spinnerRanger = (Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);
    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));
    spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        // ...
    });
}
like image 996
HelloCW Avatar asked Jul 17 '15 08:07

HelloCW


1 Answers

I just ran this code with my own basic ArrayAdapter and it displayed the toast immediately when the View was created and setSelection() was called before the listener was set(as you have it above).

This is definitely unintuitive, since you are setting the listener AFTER the selection has been set. The only way to get to the bottom of it is to take a deep dive into the source code. After rummaging around I found out that they are posting this Runnable to the main thread to notify the listener of selection changes. This is the order of events:

  1. Spinner View inflated
  2. Adapter(data sources) set
  3. Selection set and SelectionNotifier Runnable posted
  4. Selection listener is set on Spinner
  5. SelectionNotifier processed on mainthread
  6. SelectionNotifier calls onItemSelected() and Toast is displayed

Post a Runnable to set your selection listener, so that it gets processed after SelectionNotifier for the first pass-through:

private void InitValueOfControls()
{
    spinnerRanger=(Spinner)mView.findViewById(R.id.spinner);
    PublicParFun.FillRangeSpinner(mView, spinnerRanger);

    spinnerRanger.setSelection(PublicParFun.GetIndexOfRangeDeleteOld(mContext));
    spinnerRanger.post(new Runnable() {
    @Override
        public void run() {
            spinnerRanger.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {...}
        }
    });
}

The new order of events is:

  1. Spinner View inflated
  2. Adapter(data sources) set
  3. Selection set and SelectionNotifier Runnable posted
  4. Runnable to set Selection listener on Spinner is posted
  5. SelectionNotifier processed on mainthread
  6. SelectionNotifier has no listeners to notify so it does nothing. NO TOAST displayed!
  7. Our Runnable to set Selection listener on Spinner is processed on main thread and selection listener is set
like image 163
Trevor Carothers Avatar answered Oct 16 '22 15:10

Trevor Carothers