I would like to implement dialog such this one
User hits the button "Icon", this dialog opens, user chooses appropriate icon by pressing on it, then dialog closes and returns to me id of this icon.
How could I do it?
Thanks.
You want to create an alertDialog with a custom grid in it. In my answer I assume that OP wanted to know how to create such a dialog in general. Therefore I am using a normal style. If you want to use a dark style, create a custom styling and use it in your AlertDialog.Builder:
AlertDialog.Builder builder = new AlertDialog.Builder(context, <YourCustomStyle>);
Result:
Create the layout for a single entry. Since OP is only showing icons, I only use an ImageView. If the icon should for example have text under it, you simply could create a TextView below it and fill it with your text.
view_icon_chooser_entry.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image_choose_icon_entry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:contentDescription="#null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
Create an adapter that can deal with the data and create the layout. In my example I am extending from a BaseAdapter since this is quite easy to do. A little bit more modern would be to use a RecyclerView and create your own custom adapter for it.
AlertDialogImageAdapter.java:
public class AlertDialogImageAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
AlertDialogImageAdapter(Context context) {
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return iconList.length;
}
#Override
public Object getItem(int position) {
return iconList[position];
}
#Override
public long getItemId(int position) {
return position;
}
#SuppressLint("InflateParams")
#NonNull
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
AlertDialogViewHolder alertDialogViewHolder;
if (convertView == null) {
// This is an alertDialog, therefore it has no root
convertView = layoutInflater.inflate(R.layout.view_icon_chooser_entry, null);
DisplayMetrics metrics = convertView.getResources().getDisplayMetrics();
int screenWidth = metrics.widthPixels;
convertView.setLayoutParams(new GridView.LayoutParams(screenWidth / 6, screenWidth / 6));
alertDialogViewHolder = new AlertDialogViewHolder();
alertDialogViewHolder.icon = convertView.findViewById(R.id.image_choose_icon_entry);
convertView.setTag(alertDialogViewHolder);
} else {
alertDialogViewHolder = (AlertDialogViewHolder) convertView.getTag();
}
alertDialogViewHolder.icon.setAdjustViewBounds(true);
alertDialogViewHolder.icon.setScaleType(ImageView.ScaleType.CENTER_CROP);
alertDialogViewHolder.icon.setPadding(8, 8, 8, 8);
alertDialogViewHolder.icon.setImageResource(iconList[position]);
return convertView;
}
// This is your source for your icons, fill it with your own
private Integer[] iconList = {
android.R.drawable.ic_media_play, android.R.drawable.ic_media_pause,
android.R.drawable.ic_delete, android.R.drawable.ic_btn_speak_now,
android.R.drawable.ic_media_previous, android.R.drawable.ic_media_next,
android.R.drawable.ic_menu_my_calendar, android.R.drawable.ic_menu_agenda,
android.R.drawable.ic_media_play, android.R.drawable.ic_media_pause,
android.R.drawable.ic_delete, android.R.drawable.ic_btn_speak_now,
android.R.drawable.ic_media_previous, android.R.drawable.ic_media_next,
android.R.drawable.ic_menu_my_calendar, android.R.drawable.ic_menu_agenda,
android.R.drawable.ic_media_play, android.R.drawable.ic_media_pause,
android.R.drawable.ic_delete, android.R.drawable.ic_btn_speak_now,
android.R.drawable.ic_media_previous, android.R.drawable.ic_media_next,
android.R.drawable.ic_menu_my_calendar, android.R.drawable.ic_menu_agenda
};
private class AlertDialogViewHolder {
ImageView icon;
}
}
Then, place a method to create a new AlertDialog with your custom AlertDialogImageAdapterand use a grid for the layout. You can change how many columns you have with setNumColumns(4).
Put this method where you want to show the alert dialog and simply call it:
private void showAlertDialog(Context context) {
GridView gridView = new GridView(context);
gridView.setAdapter(new AlertDialogImageAdapter(context));
gridView.setNumColumns(4);
gridView.setGravity(Gravity.CENTER);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO: Implement
Toast.makeText(view.getContext(), "Clicked position is: " + position, Toast.LENGTH_LONG).show();
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setView(gridView);
builder.setTitle(R.string.title_chose_icon);
builder.show();
}
I will suggest you to "imitate" the dialog and not using the android one.
For that, you can create a second layout with the dark grey background and all your clickable icons inside. When you will call the dialog, set the dimming to the main layout and put the one with the icons on the top.
I am using this in my app. I will provide you some code in 10min.
Have a look at the fragments section in the API Demo app. There are some dialogs you can use
Related
In my Android app I have an EditText. When the EditText is focused, a PopupWindow is shown, which contains a ListView. The user can continue typing in the EditText when the PopupWindow is on screen, and in the mean time he/she should be able to click an item on the ListView to dismiss the PopupWindow. (Depending on which ListView item is clicked, in addition to dismissing the PopupWindow something else should also happen, but it's irrelevant here.)
The problem is, although the "continue typing" part works, the "click an item" part doesn't, because the OnItemClickListener of the ListView never gets called, and I can't figure out why.
Here's my activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ddd"
android:focusableInTouchMode="true"
tools:context="com.example.popuptest3.MainActivity" >
<EditText
android:id="#+id/edtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="phone" />
</RelativeLayout>
My popup_window.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff" >
<ListView
android:id="#+id/lstItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="60dp" />
<Button
android:id="#+id/btnDismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Dismiss" />
</RelativeLayout>
My list_item.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp" >
<TextView
android:id="#+id/txtItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Item Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
In my MainActivity class I have the following preparation code:
private PopupWindow popup = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prepareControls();
}
private void prepareControls() {
final EditText edtPhone = (EditText)findViewById(R.id.edtPhone);
edtPhone.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
openPopup(edtPhone);
else
closePopup();
}
});
}
That is, I open the PopupWindow when edtPhone gets focus, and close the PopupWindow when edtPhone loses focus. Then I have:
private void openPopup(View parent) {
closePopup();
// Inflate the view of the PopupWindow.
LayoutInflater layoutInflater = LayoutInflater.from(this);
View view = layoutInflater.inflate(R.layout.popup_window,
new LinearLayout(this), false);
final Activity activity = this;
// Prepare the ListView.
ListView lstItems = (ListView)view.findViewById(R.id.lstItems);
lstItems.setAdapter(new ListAdapter(this));
lstItems.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
hideKeyboardAndClearFocus(activity);
}
});
// Prepare the Button.
Button btnDismiss = (Button)view.findViewById(R.id.btnDismiss);
btnDismiss.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
hideKeyboardAndClearFocus(activity);
}
});
// Create and show the PopupWindow.
popup = new PopupWindow(view, 400, 300, false);
popup.showAtLocation(parent, Gravity.LEFT | Gravity.TOP, 0, 60);
}
The btnDismiss button is not part of my plan; I add it here just for demonstration purposes. Finally I have:
private void closePopup() {
if (popup != null) {
popup.dismiss();
popup = null;
}
}
private static void hideKeyboardAndClearFocus(Activity activity) {
InputMethodManager manager = (InputMethodManager)activity.
getSystemService(Activity.INPUT_METHOD_SERVICE);
manager.hideSoftInputFromWindow(
activity.getCurrentFocus().getWindowToken(), 0);
activity.getWindow().getDecorView().clearFocus();
}
I think this doesn't require explanation, and the ListAdapter class is quite standard so I omit it.
Now, when I click on edtPhone, it gets focus, the soft keyboard shows up, and the PopupWindow opens. I can type something into edtPhone, and when I click on btnDismiss, edtPhone loses focus, the soft keyboard is dismissed, and the PopupWindow closes, all as expected. But if instead of btnDismiss I click on an item on lstItems, nothing happens. Using Log I can see that OnItemClickListener is not called. The funny thing is, the ListView can be scrolled well, it just cannot be clicked.
I know this post but that solution doesn't work for my purpose. If I change this line:
popup = new PopupWindow(view, 400, 300, false);
to the following:
popup = new PopupWindow(view, 400, 300, true);
the PopupWindow becomes focusable and the ListView becomes clickable, alright, but then I cannot continue typing into edtPhone. In fact, if there are other controls on the activity, they all become unreachable, as if the PopupWindow is "modal".
I also tried android:focusable="false", no luck. And I'm not doing auto-complete so AutoCompleteTextView isn't what I need.
So why is OnItemClickListener not called on non-focusable PopupWindow? Am I doing anything wrong?
set the popupwindow to non-focusable
popupwindow.setFocusable(false);
popupwindow.setTouchable(true);
popupwindow.setOutsideTouchable(true);
extend the ListView and override the following methods
#Override
public boolean hasFocus() {
return true;
}
#Override
public boolean isFocused() {
return true;
}
#Override
public boolean hasWindowFocus() {
return true;
}
This is how AutoCompleteTextView implements the popup for suggestions.
If you need to select the list item using DPAD keys or other navigation keys , then you need to pass the key events from the EditText to the ListView.
I solved this problem by just setting an OnClickListener on convertview, which is a parameter of adapter's getView method. I then calls onItemClick method of the listview's onItemClickListener.
No need to make popupWindow focusable. Infact, just using ListPopupWindow is easier since its only inside getView() adapter method that we are adding some few lines of code
ListPopupWindow popupWindow = new ListPopupWindow(mContext);
MentionsAdapter adapter = new MentionsAdapter(mContext, suggestions);
popupWindow.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String username = mSuggestions.get(position);
String mentionCompletion = username.substring(mCurrentQuery.length()).concat(" ");
mEditor.getText().insert(mEditor.getSelectionEnd(), mentionCompletion);
hideSuggestions();
}
});
popupWindow.setAdapter(adapter);
MentionsAdapter getView method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(,R.layout.item_user,parent,false);
}
// this is the key point of workaround
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.getListView().getOnItemClickListener()
.onItemClick(listView, v, position, getItemId(position));
}
});
return convertView;
}
public void onClick(View arg0) {
final Dialog dialog = new Dialog(Hero.this);
ListView list = new ListView(Hero.this);
final ArrayList<Spell> spells = new ArrayList<Spell>();
for (int i = 0; i < MainActivity.charSpells.size(); i++){
if (MainActivity.charSpells.get(i).getType() == Spell.SPELL){
spells.add(MainActivity.charSpells.get(i));
}
}
list.setAdapter(new ArrayAdapter<Spell>(Hero.this, R.layout.row, spells));
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(list);
dialog.setCanceledOnTouchOutside(true);
dialog.show();
list.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
spell1.setText(spells.get(arg2).getSc());
MainActivity.spells[0] = spells.get(arg2);
dialog.dismiss();
}
});
}
I made this Dialog which contain a ListView, but the items can ony be selected when you click on the text and not, like it should be, on the whole row.
Can anyone find my mistake?
EDIT:
OK, solved it, I used a builder to make the dialog:
https://stackoverflow.com/a/6157258/2468567
If that's happening, I'm assuming your TextView resource must be set to wrap_content for both height/width, which would mean the only touchable area is where the actual text is.
Try changing their values in the XML to fill_parent (or a fixed size or whatever you're doing). Then adjust the android:gravity to adjust where the text is placed within the View.
Recap:
android:layout_width="fill_parent"
android:layout_height="fill_parent
android:gravity="center"
Try to make the width bigger then the screen on the TextView
list.xml
android:layout_width="1000dp"
I'm going through Google's Android Developer page on Dialogs, specifically this section. However, instead of creating the DialogFragment's message programmatically, I made a pre-set layout named layout_newpayperiod.xml with the following elements:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Spinner
android:id="#+id/spinner_payperiod"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:padding="8dp"
android:entries="#array/pay_periods"
/>
<EditText
android:id="#+id/edittext_savepercent"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:padding="8dp"
android:inputType="number"
android:hint="Percent to Save"
/>
<EditText
android:id="#+id/edittext_payment"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:padding="8dp"
android:inputType="numberDecimal"
android:hint="Total Payment"
/>
</LinearLayout>
When I call the DialogFragment it shows up as normal, with the Spinner having the proper values. I filled in the entries and hit "OK", but when I try to retrieve the values from the Spinner and two EditText fields, the app forces close with a NumberFormatException: Invalid double "". I get the feeling I'm not retrieving the Views properly. Can anyone help me figure this out please? Thanks!
public class StartPayperiodDialogFragment extends DialogFragment {
/* The activity that creates an instance of this dialog fragment must
* implement this interface in order to receive event callbacks.
* Each method passees the DialogFragment in case the host needs to query it.
*/
public interface StartPayperiodDialogListener{
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
StartPayperiodDialogListener listener;
// Override the Fragment.onAttach() method to instantiate the StartPayperiodDialogListener
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try{
// Instantiate the NoticeDialogListener so we can send events to the host
listener = (StartPayperiodDialogListener) activity;
}catch(ClassCastException e){
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString() + " must implement StartPayperiodDialogListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState){
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View transactionLayout = View.inflate(getActivity(), R.layout.layout_newpayperiod, null);
builder.setView(transactionLayout)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Send the positive button event back to the calling activity
listener.onDialogPositiveClick(StartPayperiodDialogFragment.this);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Send the negative button event back to the calling activity
listener.onDialogNegativeClick(StartPayperiodDialogFragment.this);
}
});
return builder.create();
}
}
In MainActivity.class, the callback method:
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
// User pressed OK, so we need to grab the values from the
// dialog's fields and apply them to the Views in the Main
// Activity
View transactionLayout = View.inflate(this, R.layout.layout_newpayperiod, null);
// Start with the payment amount
EditText paymentEt = (EditText) transactionLayout.findViewById(R.id.edittext_payment);
TextView paymentTv = (TextView) findViewById(R.id.text_paycheck);
paymentTv.setText(moneyFormat.format(Double.parseDouble(paymentEt.getText().toString())));
// Next, the percent to save
EditText savingsEt = (EditText) transactionLayout.findViewById(R.id.edittext_savepercent);
TextView savingsTv = (TextView) findViewById(R.id.text_savings);
savingsTv.setText(savingsEt.getText().toString() + "%");
// Then, the pay period
Spinner periodSp = (Spinner) transactionLayout.findViewById(R.id.spinner_payperiod);
TextView periodTv = (TextView) findViewById(R.id.text_payperiod);
periodTv.setText(periodSp.getSelectedItem().toString());
// Finally, let's update the daily allowance amount and clear
// the adapter
adapter.clear();
adapter.notifyDataSetChanged();
TextView allowanceTv = (TextView) findViewById(R.id.text_allowance);
Double allowanceValue;
switch(periodSp.getSelectedItemPosition()){
case(0): // Daily
allowanceValue = Double.parseDouble(paymentTv.getText().toString());
break;
case(1): // Weekly
allowanceValue = Double.parseDouble(paymentTv.getText().toString()) / 7;
break;
case(2): // 2 Weeks
allowanceValue = Double.parseDouble(paymentTv.getText().toString()) / 14;
break;
case(3): // 30 Days
allowanceValue = Double.parseDouble(paymentTv.getText().toString()) / 30;
break;
default: // Debugging purposes only
allowanceValue = 42.0;
break;
}
allowanceTv.setText(Double.toString(allowanceValue));
}
Try this:
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
// User pressed OK, so we need to grab the values from the
// dialog's fields and apply them to the Views in the Main
// Activity
// Start with the payment amount
Dialog dialogView = dialog.getDialog();
EditText paymentEt = (EditText) dialogView.findViewById(R.id.edittext_payment);
... etc. (Retrieve any other views from the dialog by querying the dialogView in the same way.)
Your inflate code "inflates" a brand new version of that view. You want to access the one that was created in the dialog.
I think that this line View transactionLayout = View.inflate(this, R.layout.layout_newpayperiod, null); messes everything. Maybe it's not messing, but you're getting address of freshly created layout and assign it to transactionLayout reference. Then you're getting Views from that layout EditText paymentEt = (EditText) transactionLayout.findViewById(R.id.edittext_payment); which are certainly uninitialisted. It has value empty string value -> "";
I think you should use findViewById to get reference to your EditText's as you do with your TextView's. But as you are in your MainActivity which layout is probably not a parent view to your R.layout.layout_newpayperiod, you must find a way to do that properly.
You've got your DialogFragment as parameter in this onDialogPositiveClickcallback method. So you can obtain it's View and the layout you're looking for - that contains your EditText's
Sorry for editing this post so many times.
The problem is, no matter where or how I call for this layout's components, they always return null.
setView(inflater.inflate(R.layout.search_layout, null))
This works fine. It displays the layout inside the Dialog, yet, the children are always returned as null by findViewById(R.id.some_search_layout_children).
I've tried cleaning my project multiple times, tried to implement another class for my Dialog, called findViewById() as a member of my main Activity, inside the initSearch() method, and inside an anonymous implementation of OnClickListener for the Dialog, but all with the same result. I've also tried breaking the children out into independent Views and programmatically calling them:
TextView text = (TextView) findResourceById(R.id.new_independant_textview);
But, again, the same result.
This is the relevant code:
public class Xyz extends Activity {
public void onCreate(...) { // some listener will trigger initSearch() }
private void initSearch() {
AlertDialog.Builder searchDialog = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
searchDialog.setTitle("Search Photos");
searchDialog.setMessage("Specify tag and value...");
// R.layout.search_dialog is my custom layour, it displays fine, it works.
searchDialog.setView(inflater.inflate(R.layout.search_dialog, null));
EditText tagText = (EdiText) findViewById(R.id.tagField); // WILL RETURN NULL
searchDialog.setPositiveButton( ... ) ...
searchDialog.show();
}
This line:
EditText text = (EditText) findViewById(R.id.tagField);
always returns null, no matter how or where it's called – globally, local final, etc. – it just returns null.
Here is the XML of my custom Dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/search_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/tagText"
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="#string/tag" />
<EditText
android:id="#+id/tagField"
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
<TextView
android:id="#+id/valueText"
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="#string/value" />
<EditText
android:id="#+id/valueField"
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</LinearLayout>
This is my R.java file:
public static final class id {
public static final int action_settings=0x7f0a0011;
public static final int add_album=0x7f0a0001;
public static final int add_photo=0x7f0a000d;
public static final int albums_list=0x7f0a0003;
public static final int delete_album=0x7f0a000b;
public static final int exit_finder=0x7f0a000f;
public static final int new_directory=0x7f0a000e;
public static final int open_album=0x7f0a000a;
public static final int photos_grid=0x7f0a0000;
public static final int rename_album=0x7f0a000c;
public static final int search_dialog=0x7f0a0004;
public static final int search_icon=0x7f0a0002;
public static final int splash_rutgers=0x7f0a0009;
public static final int tagField=0x7f0a0006; // problematic
public static final int tagText=0x7f0a0005; / problematic
public static final int terminate_app=0x7f0a0010;
public static final int valueField=0x7f0a0008; // problematic
public static final int valueText=0x7f0a0007; // problematic
}
Calling findViewById() will search for views within your Activity's layout and not your dialog's view. You need to call findViewById() on the specific View that you set as your dialog's layout.
Try this
private void initSearch() {
AlertDialog.Builder searchDialog = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
searchDialog.setTitle("Search Photos");
searchDialog.setMessage("Specify tag and value...");
// R.layout.search_dialog is my custom layour, it displays fine, it works.
View dialogView = inflater.inflate(R.layout.search_dialog, null);
searchDialog.setView(dialogView);
EditText tagText = (EdiText) dialogView.findViewById(R.id.tagField);
searchDialog.setPositiveButton( ... ) ...
AlertDialog myAlert = searchDialog.create(); //returns an AlertDialog from a Builder.
myAlert.show();
}
Notice how I'm inflating the view and storing it in a View named dialogView. Then, to find your EditText named tagField, I'm using dialogView.findViewById(R.id.tagField);
The TextView with id text123 has to be declared inside the Layout you set with setContentView
Your problem is you are trying to do .show() on a AlertDialog Builder, not the AlertDialog itself.
Try the following code:
public class Xyz extends Activity {
public void onCreate(...) { // some listener will trigger initSearch() }
private void initSearch() {
AlertDialog.Builder searchDialog = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
searchDialog.setTitle("Search Photos");
searchDialog.setMessage("Specify tag and value...");
// R.layout.search_dialog is my custom layour, it displays fine, it works.
searchDialog.setView(inflater.inflate(R.layout.search_dialog, null));
EditText tagText = (EdiText) findViewById(R.id.tagField); // WILL RETURN NULL
searchDialog.setPositiveButton( ... ) ...
AlertDialog myAlert = searchDialog.create(); //returns an AlertDialog from a Builder.
myAlert.show();
}
I had the exact same problem while programming a multilingual app.
Eventually I found out that I forgot to update labels in some layout's activity [xml] files.
I had 3 of them per activity:
<activity_name>.xml
<activity_name>.xml(land)
<activity_name>.xml(iw)
I updated only the first and forgot to update the other as follows:
All three had the a single TextView with the id of:
<TextView
android:id="#+id/loading_textbox"
.../>
Then I've changed the name of the id of that text view - only in the first file - to:
<TextView
android:id="#+id/status_textbox"
.../>
And, of-course, in the Activity's Java code (that uses all three...):
TextView tv = findViewByID(R.id.status_textbox);
This worked for the regular (English <activity_name>.xml) version.
But when I went the the IW language (Hebrew <activity_name>xml(iw)) tv got null value and I even got some crushes.
When I changed the text view id of the other files to "#+id/status_textbox" everything worked like a charm...
So, simply make sure all your IDs are updated and accounted for in all your layouts and languages.
This solved my problem anyway..
I want to bring up a spinner dialog when the user taps a menu item to allow the user to select an item.
Do I need a separate dialog for this or can I use Spinner directly? I see this link, mentions a MODE_DIALOG option but it doesn't seem to be defined anymore. AlertDialog may be OK but all the options say "clicking on an item in the list will not dismiss the dialog" which is what I want. Any suggestion?
Ideally, the code would be similar to the case where the spinner is shown on the screen:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity,
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
myspinner.setAdapter(adapter);
// myspinner.showAsDialog() <-- what i want
You can use an alert dialog
AlertDialog.Builder b = new Builder(this);
b.setTitle("Example");
String[] types = {"By Zip", "By Category"};
b.setItems(types, new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
switch(which){
case 0:
onZipRequested();
break;
case 1:
onCategoryRequested();
break;
}
}
});
b.show();
This will close the dialog when one of them is pressed like you are wanting.
In xml there is option
android:spinnerMode="dialog"
use this for Dialog mode
Try this:
Spinner popupSpinner = new Spinner(context, Spinner.MODE_DIALOG);
See this link for more details.
MODE_DIALOG and MODE_DROPDOWN are defined in API 11 (Honeycomb). MODE_DIALOG describes the usual behaviour in previous platform versions.
Adding a small attribute as android:spinnerMode="dialog" would show the spinner contents in a pop-up.
You can create your own custom Dialog. It's fairly easy. If you want to dismiss it with a selection in the spinner, then add an OnItemClickListener and add
int n = mSpinner.getSelectedItemPosition();
mReadyListener.ready(n);
SpinnerDialog.this.dismiss();
as in the OnClickListener for the OK button. There's one caveat, though, and it's that the onclick listener does not fire if you reselect the default option. You need the OK button also.
Start with the layout:
res/layout/spinner_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/dialog_label"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:hint="Please select an option"
/>
<Spinner
android:id="#+id/dialog_spinner"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
/>
<Button
android:id="#+id/dialogOK"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:text="OK"
android:layout_below="#id/dialog_spinner"
/>
<Button
android:id="#+id/dialogCancel"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:text="Cancel"
android:layout_below="#id/dialog_spinner"
android:layout_toRightOf="#id/dialogOK"
/>
</RelativeLayout>
Then, create the class:
src/your/package/SpinnerDialog.java:
public class SpinnerDialog extends Dialog {
private ArrayList<String> mList;
private Context mContext;
private Spinner mSpinner;
public interface DialogListener {
public void ready(int n);
public void cancelled();
}
private DialogListener mReadyListener;
public SpinnerDialog(Context context, ArrayList<String> list, DialogListener readyListener) {
super(context);
mReadyListener = readyListener;
mContext = context;
mList = new ArrayList<String>();
mList = list;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spinner_dialog);
mSpinner = (Spinner) findViewById (R.id.dialog_spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String> (mContext, android.R.layout.simple_spinner_dropdown_item, mList);
mSpinner.setAdapter(adapter);
Button buttonOK = (Button) findViewById(R.id.dialogOK);
Button buttonCancel = (Button) findViewById(R.id.dialogCancel);
buttonOK.setOnClickListener(new android.view.View.OnClickListener(){
public void onClick(View v) {
int n = mSpinner.getSelectedItemPosition();
mReadyListener.ready(n);
SpinnerDialog.this.dismiss();
}
});
buttonCancel.setOnClickListener(new android.view.View.OnClickListener(){
public void onClick(View v) {
mReadyListener.cancelled();
SpinnerDialog.this.dismiss();
}
});
}
}
Finally, use it as:
mSpinnerDialog = new SpinnerDialog(this, mTimers, new SpinnerDialog.DialogListener() {
public void cancelled() {
// do your code here
}
public void ready(int n) {
// do your code here
}
});
You can use a spinner and set the spinnerMode to dialog, and set the layout_width and layout_height to 0, so that the main view does not show, only the dialog (dropdown view). Call performClick in the button click listener.
mButtonAdd.setOnClickListener(view -> {
spinnerAddToList.performClick();
});
Layout:
<Spinner
android:id="#+id/spinnerAddToList"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:prompt="#string/select_from_list"
android:theme="#style/ThemeOverlay.AppCompat.Light"
android:spinnerMode="dialog"/>
The advantage of this is you can customize your spinner any way you want.
See my answer here to customize spinner:
Overriding dropdown list style for Spinner in Dialog mode
Here is an Spinner subclass which overrides performClick() to show a dialog instead of a dropdown. No XML required. Give it a try, let me know if it works for you.
public class DialogSpinner extends Spinner {
public DialogSpinner(Context context) {
super(context);
}
#Override
public boolean performClick() {
new AlertDialog.Builder(getContext()).setAdapter((ListAdapter) getAdapter(),
new DialogInterface.OnClickListener() {
#Override public void onClick(DialogInterface dialog, int which) {
setSelection(which);
dialog.dismiss();
}
}).create().show();
return true;
}
}
For more information read this article: How To Make Android Spinner Options Popup In A Dialog
This is from the Android SDK source code.
As you can see you have a special constructor to create a Spinner with the specified mode you wanna use.
Hope it will help you :)
/**
* Construct a new spinner with the given context's theme, the supplied attribute set,
* and default style. <code>mode</code> may be one of {#link #MODE_DIALOG} or
* {#link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
*
* #param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* #param attrs The attributes of the XML tag that is inflating the view.
* #param defStyle The default style to apply to this view. If 0, no style
* will be applied (beyond what is included in the theme). This may
* either be an attribute resource, whose value will be retrieved
* from the current theme, or an explicit style resource.
* #param mode Constant describing how the user will select choices from the spinner.
*
* #see #MODE_DIALOG
* #see #MODE_DROPDOWN
*/
public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
super(context, attrs, defStyle);
If you want to show it as a full screen popup, then you don't even need an xml layout. Here's how do do it in Kotlin.
val inputArray: Array<String> = arrayOf("Item 1","Item 2")
val alt_bld = AlertDialog.Builder(context);
alt_bld.setTitle("Items:")
alt_bld.setSingleChoiceItems(inputArray, -1) { dialog, which ->
if(which == 0){
//Item 1 Selected
}
else if(which == 1){
//Item 2 Selected
}
dialog.dismiss();
}
val alert11 = alt_bld.create()
alert11.show()
Here is a Kotlin version based on the accepted answer.
I'm using this dialog from an adapter, every time a button is clicked.
yourButton.setOnClickListener {
showDialog(it /*here I pass additional arguments*/)
}
In order to prevent double clicks I immediately disable the button, and re-enable after the action is executed / cancelled.
private fun showDialog(view: View /*additional parameters*/) {
view.isEnabled = false
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.your_dialog_title)
val options = arrayOf("Option A", "Option B")
builder.setItems(options) { dialog, which ->
dialog.dismiss()
when (which) {
/* execute here your actions */
0 -> context.toast("Selected option A")
1 -> context.toast("Selected option B")
}
view.isEnabled = true
}
builder.setOnCancelListener {
view.isEnabled = true
}
builder.show()
}
You can use this instead of a context variable if you are using it from an Activity.