I therefore initially used buttons to popup a dialog to fill-in some information before updating the cursor. While being functional, I was still seeking for a solution that would not clutter the user interface.
I then came with a custom spinner that looks very like the normal ones, except that this spinner provides an additional button when the list is displayed to add a new element.
Clicking the add button close the current spinner dialog and start an activity with a dialog theme. A dialog could be used instead of an activity, but using an activity make it possible to Toast some message to the user when data are invalid before closing the dialog.
Once the data is validated and inserted into the data base, the dialog is closed. Then, the cursor associated to the spinner is updated and the newly added entry is selected.
That covers the intended behavior, so let's look at the code behind. The method used extends the Spinner class and override the performClick method that create an AlertDialog with a custom one displaying our custom dialog. The custom dialog could either be another AlertDialog with buttons added, but I decided to extends Dialog instead to have more flexibilities with the interface for more features.
So here goes the relevant code for implementing the spinner.
public class SpinnerCustomDialog extends Spinner implements DialogInterface.OnClickListener { protected OnAddListener mListener = null; public interface OnAddListener { void onAdd(Spinner inSpinner); } @Override public boolean performClick() { //boolean handled = super.performClick(); //TODO how to avoid this skip ? boolean handled = false; if (!handled) { handled = true; Context context = getContext(); final DropDownAdapter adapter = new DropDownAdapter(getAdapter()); SpinnerDialog lDialog = new SpinnerDialog(context, getSelectedItemPosition(), adapter, this); if (mPrompt != null) { lDialog.setTitle(mPrompt); } lDialog.show(); } return handled; } @Override public void onClick(DialogInterface dialog, int which) { if(which == DialogInterface.BUTTON_POSITIVE) { if(mListener!=null) mListener.onAdd(this); } else { setSelection(which); } dialog.dismiss(); } public void onAddReturn(int resultCode, Intent data) { if(resultCode == Activity.RESULT_OK) { //Update selection to the newly added item ((CursorAdapter)getAdapter()).getCursor().requery(); int lPosition = AdapterUtil.getItemPositionById(((CursorAdapter)getAdapter()), data.getLongExtra("id", -1)); if(lPosition >= 0) this.setSelection(lPosition); } } }
To make it work, I had to paste some more methods from Spinner as even with the extend, some protected members were not visible. I still wonder why ....
The performClick simply create a Dialog and provide an adapter. The SimpleCursorAdapter is wrapped by the DropDownAdapter as done by the normal spinner to trigger call to getDropDownView instead of getView.
The dialog also need to receive a DialogInterface.OnClickListener to handle the item selection and button triggering.
This is done by the onClick method that is also defined in Spinner, but that we need to override to handle the button. The type of view clicked can be identified by the integer receive. An item will receive a positive value associated to his position, while a button will be negative.
When the add button is clicked, the event is forwarded to a method defined by the user through the OnAddListener. Here, the intended action is to start a new activity. This is done with a short piece of code within the parent activity:
mSpinner.setOnAddListener(new SpinnerCustomDialog.OnAddListener(){ public void onAdd(Spinner mInSpinner) { Intent lIntent = new Intent(WidgetTest.this, AddNewStringDataDialog.class); lIntent.putExtra("category", "Spinner1"); startActivityForResult(lIntent,REQUEST_ADD_NEW); } });
Here, the activity AddNewStringDataDialog is started to fill in the required date for a new table row associated to this spinner. When this dialog closes, the position of the newly inserted item need to be retrieved. This is done by calling the onAddReturn of the custom spinner in the onActivityResult of the calling activity as follow:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode) { case REQUEST_ADD_NEW: mSpinner.onAddReturn(resultCode, data); break; }
The definition of the spinner dialog is quite straight forward and is as follow:
public class SpinnerDialog extends Dialog implements OnItemClickListener, View.OnClickListener { DialogInterface.OnClickListener mListener; SpinnerDialog(Context inContex, int inPosition, ListAdapter inSpinnerAdapter, DialogInterface.OnClickListener inListener) { super(inContex); this.setContentView(R.layout.dbspinner_dialog); mListener = inListener; ListView lList = (ListView) this.findViewById(R.id.list); lList.setAdapter(inSpinnerAdapter); lList.setOnItemClickListener(this); lList.setChoiceMode( ListView.CHOICE_MODE_SINGLE ); lList.setSelection(inPosition); Button lButton = (Button) this.findViewById(R.id.add_btn); lButton.setOnClickListener(this); } public void onItemClick(AdapterView inParent, View inView, int inPosition, long inId) { if(mListener != null) mListener.onClick(this, inPosition); } public void onClick(View inView) { if(mListener != null) mListener.onClick(this, DialogInterface.BUTTON_POSITIVE); } }
The tricky part was to make its width expand fully. This was done by placing the button in an extra layout.
can you explain how to do this in android honeycomb 3.2 and with a real dropdown, instead of an "alert" ?
ReplyDeleteAnthraxium-64, I'm not sure what you mean by "real dropdown". I guess you mean the standard spinner widget. I you look into the performClick() of android.widget.spinner you'll realized that the "real spinner" is starting an alert dialog. Hence, this method just change the created alert dialog.
ReplyDeleteokay, but i mean, you can choose between the dropdown style and the alert style ;)
ReplyDeletei want to know how i can create a custom alert dialog, i already had to use a custom arrayadapter for my spinner
I wasn't aware of the dropdown style introduce in API 11. One would need to look into the code of the new spinner to see exactly what is going on. It seems however that the sources are still not available.
ReplyDeletecan you show how you add your SpinnerCustomDialog to your xml layout file....or just post your source code
ReplyDeleteTo use this widget in your application layout, you simply use something like this:
ReplyDelete(jfdupuis.util.widget.SpinnerCustomDialog android:id="@+id/dev_spinner" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:prompt="@string/my_prompt"
android:drawSelectorOnTop="true"/)
Where jfdupuis.util.widget is the package in which I decided to define this widget. Of course, replace ( and ) with < and >.
This comment has been removed by the author.
ReplyDelete