Retrieving Value from EditText in DialogFragment - android

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.

Related

How do I go about setting up a user interface that involves tags?

Here is what I am aiming for:
I am unsure if I am doing this correctly. There are probably better,more efficient, and cleaner ways to do it, but I need to know how.
This layout was designed in xml and inflated via an inflater. The produced view was then placed into an AlertDialog. Thus, this is seen as an AlertDialog by the user.
My concern is with the tags section at the bottom. I want this to work like how Tumblr tags work. Type a string, hit the button, and a button with that tag name will show up in the blank section below it.
Now, if you click on those buttons (with their respective tag names), they will disappear from the frame.
I have several concerns.
I have trouble implementing listeners. If the AddTag button creates more buttons in the (currently invisible, but present) LinearLayout, then what about the created buttons? How do those buttons implement onClick listeners that will remove themselves from the LinearLayout if they were created in some inner method defined from the AddTag button's onClick method?
I am afraid about having to declare some of these views as FINAL in order to reference them in button methods and inner classes. I am now stuck because of this.
Do I have to define my own layout for the tag buttons? You see, a LinearLayout displays things one after the other, yes? I want to try to recreate how some social networking sites do it. Fill the layout with buttons from top to bottom, left to right. If there is no room left in the current row, go to the next one and add the tag button there. It's basically a dynamic LinearLayout that has autowrapping.
If there are any better ways of implementing this, please let me know a general step by step of what to do. I have not learned Fragments yet, but I think it may be VERY applicable here. Also, should I be creating a class that extends ViewGroup, inflating the XML there, and adding helper methods to handle things? I suppose from a DialogFragment I could then addView(the class I just created) and work from there?
Here is my current code by the way. I am stuck and stumped.
/**
* Opens a view for the user to define their new action and add it to the
* dictionary.
*
* #param view
*/
public void defineNewAction(View view) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
View viewToSet = inflater.inflate(
R.layout.define_new_action_window_layout,
null);
final EditText newActionName = (EditText) viewToSet
.findViewById(R.id.set_action_name);
final RadioGroup priorityGroup = (RadioGroup) viewToSet
.findViewById(R.id.radiogroup_set_priority);
final EditText goalTimeHours = (EditText) viewToSet
.findViewById(R.id.set_goal_time_hours);
final EditText goalTimeMinutes = (EditText) viewToSet
.findViewById(R.id.set_goal_time_minutes);
final EditText addTagsInput = (EditText) viewToSet
.findViewById(R.id.add_tags_input);
Button addTagButton = (Button) viewToSet.findViewById(R.id.btn_add_tags);
final ArrayList<String> tags = new ArrayList<String>();
final LinearLayout currentTagsLayout = (LinearLayout) viewToSet
.findViewById(R.id.current_tags);
addTagButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String tag = addTagsInput.getText().toString();
tags.add(tag);
Button newTag = new Button(builder.getContext());
int tagId = tag.hashCode();
if (tagId < 0)
tagId *= -1;
newTag.setId(tagId);
newTag.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Button toRemove = (Button) currentTagsLayout.findViewById(tagId);
currentTagsLayout.removeView(toRemove);
}
});
currentTagsLayout.addView(newTag);
}
});
builder.setTitle("Define your action.");
builder.setView(viewToSet);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
String name = newActionName.getText().toString();
int priority = priorityGroup.getCheckedRadioButtonId();
int goalHours = Integer
.parseInt(goalTimeHours.getText().toString());
int goalMinutes = Integer.parseInt(goalTimeMinutes.getText()
.toString());
}
});
builder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
I have trouble implementing listeners
There's no trouble. For the functionality you are trying to achieve, you can keep adding buttons and setting OnClickListeners on them. You don't even need to give them an id, or track them in any way. The following code inside your OnClickListener will do:
newTag.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use the View given to you
currentTagsLayout.removeView(v);
}
});
I am afraid about having to declare some of these views as FINAL
This is how Java works. I haven't noticed any crippling effects of this. You can also declare your variables as global to not have to define them as final. But I don't see why declaring them as final is an issue. Could you provide an example where this is a problem?
Do I have to define my own layout for the tag buttons?
This is something you will have to deal with yourself. It's a design decision. If you need auto-wrapping support, you can look at Android Flow Layout: Link. It's an extended LinearLayout that supports auto-wrap of its contents.
I have not learned Fragments yet, but I think it may be VERY
applicable here
I don't see why they would be.
Note/Aside: Some kind of a check here would be better:
String tag = "";
if (!addTagsInput.getText().toString().equals("")) {
tag = addTagsInput.getText().toString();
} else {
// handle empty string
}

How do I get edittext value from a custom AlertDialog where retrieval of values is not in the onClick?

What I am trying to do is to create a custom dialog that overrides an AlertDialog.
What it is supposed to do is get some text (at least 2 strings) and then for each of those strings it is supposed to be able to get more information, but I want to do this in custom dialogs.
So what is supposed to happen is a user can enter 2 people in an activity screen, and then for the first person, you get a custom dialog and that person can enter three words, and then it jumps to the next custom dialog (exact same layout I am inflating) and the second person can enter some words.
This is my xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LinLay_Enter_Words"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/TextView_AddPlayerWord_Instruction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/help_text_size"
android:textStyle="bold"></TextView>
<EditText
android:id="#+id/EditText_Word1"
android:layout_height="wrap_content"
android:maxLength="20"
android:layout_width="match_parent"
android:maxLines="1"></EditText>
<EditText
android:id="#+id/EditText_Word2"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:maxLines="1"></EditText>
<EditText
android:id="#+id/EditText_Word3"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:maxLines="1"></EditText>
</LinearLayout>
And this is part of the code:
protected Dialog onCreateDialog(int id) {
switch (id) {
case NOUN_INPUT_DIALOG_ID:
Dialog returnedDialog = initWordDialog();
return(returnedDialog);
}
return null;
}
It calls initWordDialog():
private Dialog initWordDialog() {
LayoutInflater inflater = LayoutInflater.from(this); //(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View dialogLayout = inflater.inflate(R.layout.word_entry_dialog, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
...
TextView v1 = (TextView) dialogLayout.findViewById(R.id.TextView_AddPlayerWord_Instruction);
...
v1.setText("SomeText");
builder.setView(dialogLayout);
builder.setTitle(R.string.enter_word_title);
builder.setPositiveButton("Next", onNextSubmit);
AlertDialog wordBuilderDialog = builder.create();
return wordBuilderDialog;
}
I think what I am trying to find has been discussed to some degree here:
Value of EditText in Custom Dialog
Android - Custom Dialog - Can't get text from EditText
How to add two edit text fields in an alert dialog
The problem, I believe, lies here, where all of the examples everyone has their onClick in the same function as their onCreate. My stuff was a bit more complicated and I wanted to separate out the functions; however, as a result, I am now unable to access any of the EditText variables.
Here is my onClick implementation:
private DialogInterface.OnClickListener onNextSubmit = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (setPlayerWords()) {
...
}
};
The part that matters is I don't even get to the part where I'm accessing the edittexts until setPlayerWords is called, and this is where it is failing:
public boolean setPlayerWords() {
PMGamePlay pmObj = (PMGamePlay) getApplicationContext();
String[] playerWords = new String[pmObj.numberOfWordsPlayersGetToInput()];
//LayoutInflater inflater = LayoutInflater.from(this);
//View dialogLayout2 = inflater.inflate(R.layout.word_entry_dialog, null);
//setContentView(R.layout.word_entry_dialog);
final LinearLayout myLayout = (LinearLayout) findViewById(R.id.LinLay_Enter_Words);
final EditText w0 = (EditText) myLayout.findViewById(R.id.EditText_Word1);
final EditText w1 = (EditText) myLayout.findViewById(R.id.EditText_Word2);
final EditText w2 = (EditText) myLayout.findViewById(R.id.EditText_Word3);
String test = w0.getText().toString();
playerWords[0] = w0.getText().toString();
playerWords[1] = w1.getText().toString();
playerWords[2] = w2.getText().toString();
...
return true;
}
I initially tried re-inflating, but that seemed to reset and while the edittexts would not be null, they were reset to have "" in their values.
Then I tried to setContentView on my xml file, but that still gave me a null value.
Now, I just try and simply access the linearlayout, and that also returns a null value. If I just try to access the edittexts by their id directly without first going through its parent linearlayout, it also returns a null value.
At this point, I'm not sure what to do other than to cram everything that I have in these separate functions into the same single onclick, but I really don't want to do that. Is there nothing else I can do to access these edittexts?
Any help would be greatly appreciated!
Have you tried using the long version of inflate inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) ? I know that if you don't use this method there can be some issues with it grabbing layout characteristics, so might be causing the issue. For the viewgroup you should pick the parrent view for the alert and usually want attachToRoof = false;

ParseInt Exception

I am creating a small calc app with EditText views and Im running into an runtime exception when the user leaves an EditText view empty causing the ParseInt to try and Parse nothing. Ive read that I need to 'Try' and 'Catch' this error before it occurs, but Im unsure of where and how to do this!
Any advice is much appreciated!
Here is my code:
public class HandlerExamples extends Activity implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.testButton);
button.setOnClickListener(this);
public void onClick(View v) {
String a,b,t;
double vis;
EditText txtbox1 = (EditText)findViewById(R.id.A);
EditText txtbox2 = (EditText)findViewById(R.id.B);
EditText txtbox3 = (EditText)findViewById(R.id.t);
TextView tv = (TextView) findViewById(R.id.Answer);
a = txtbox1.getText().toString();
b = txtbox2.getText().toString();
t = txtbox3.getText().toString();
vis = ((Integer.parseInt(a)*1) + (Integer.parseInt(b)*2)) / (Double.parseDouble(t));
tv.setText(double.toString(vis));
}
}
Thanks so much!
public void onClick(View v) {
int id = v.getId();
switch(id){
case R.id.xx:
//do things xx click
break;
case R.id.yy:
//do things yy click
break;
}
}
you can get the view id to know whick widget was clicked.
Changwei Yao defined one way you can do this, but here's the way most Android programmers would do this (programmatically), since it's a little easier to read and figure out what your widgets are doing:
But first, remove the implements OnClickListener from your Activity, as it's not needed.
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// what you want your button to do when clicked
}
}
editText.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// what you want your EditText to do when clicked
// (such as editText.setText(""))
}
}
Another way to do the same thing is to define android:onClick="insert_method_name_here" for the widgets that you want perform an action when clicked. In your case, in your main.xml (since that's what you're using in your Activity), you could write something like...
<Button android:id="#+id/testButton"
(other attributes you wish to apply to the button)
android:onClick="buttonAction" />
<EditText
(other attributes)
android:onClick="textAction" />
And then, in your Activity, you define the methods buttonAction(View v) and textAction(View v). Note that these methods must be public void, and must take the sole argument View v.
(One advantage of the XML method is that you don't necessarily have to define an android:id attribute for these widgets, unless you need to be able to manipulate them or extract information from them in your code (which means you will need to define an android:id for your EditText since you'll likely want the user's input))
If you only need to exclude the empty text field then hotveryspicy's solution is probably the quickest. For a secure solution: catching the NumberFormatException will filter anything that can not be converted to an integer.
int vis;
try {
vis = Integer.parseInt(a);
}
catch(NumberFormatException nfe) {
Log.e(TAG,"trying to convert:"+a+" to integer failed");
vis = 0;
}

How do I create an Android Spinner as a popup?

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.

Updating a textview inside a custom dialog via a button

So, my current issue is that I can't find an elegant way to update a dialog box when a button is pressed. I can achieve functionally the same result by dismiss() and show(), but that is ugly.
Lets say this dialog has 3 buttons, for selling widgets that the player has. Sell All, Sell 10, and Sell X (amount entered with a EditText). I'd like for the dialog to persist if the player pushes Sell 10, but also to update it's textviews with the new count of widgets.
Pertinent part of the XML layout of the custom dialog:
<LinearLayout android:id="#+id/linearLayout3" android:layout_height="wrap_content" android:layout_width="match_parent">
<TextView android:id="#+id/sell10Text" android:layout_width="wrap_content" android:text="TextView" android:layout_height="wrap_content" android:layout_weight="2"></TextView>
<Button android:text="Sell 10" android:enabled="false" android:layout_width="wrap_content" android:id="#+id/sell10Button" android:layout_height="wrap_content" android:layout_weight="1"></Button>
</LinearLayout>
Pertinent part of the dialog creation:
final Dialog alert = new Dialog(this);
alert.setTitle("Sell how many "+(masterRes.get(currentResIndex).getName())+"?");
alert.setContentView(R.layout.selldialog);
TextView tvsellAll = (TextView) alert.findViewById(R.id.sellAllText);
TextView tvsell10 = (TextView) alert.findViewById(R.id.sell10Text);
//etc etc more handles, including buttons
tvsellAll.setText("Sell All ("+String.valueOf(masterRes.get(currentResIndex).getHeld())+") - $"+String.valueOf(calcCost(masterRes.get(currentResIndex).getHeld())));
tvsell10.setText("Sell 10 - $"+String.valueOf(calcCost(10)));
// etc etc more setTexts
btnsell10.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
if (v.isEnabled()) {
int y=masterRes.get(currentResIndex).getHeld();
masterRes.get(currentResIndex).setHeld(y-10);
held -= 10;
money += (calcCost(10));
updateScreen();
alert.tvsellAll.setText("Sell All ("+String.valueOf(masterRes.get(currentResIndex).getHeld())+") - $"+String.valueOf(calcCost(masterRes.get(currentResIndex).getHeld())));
alert.tvsell10.setText("Sell 10 - $"+String.valueOf(calcCost(10)));
alert.tvsellAmt.setText("Sell Amount (0-"+String.valueOf(masterRes.get(currentResIndex).getHeld())+")");
}
}
});
// etc etc other button handlers, alert.show() at the end
Now obviously the setTexts within the button can't resolve, as they can't see the alert I created, they just see OnClickListener.
I tried handling this like I did with my main activity's updater updateScreen(), which is a Runnable, that is a long list of setTexts and/or invalidates, and is runOnUiThread(updateScreen). Works great for the base activity.
I did some copypasta and tried to make a updateSellScreen(), get it to hook into the custom dialog's textviews, but it can't resolve the alert class... I'm kind of lost now.
Is this even possible without trashing everything and just creating a custom view (which I am very averse to trying to tackle this fresh into Android programming...)
Declare your TextViews as final. You'll still be able to set their texts, it just means you won't be able to reassign the variable references. Don't do alert.tv as the TextView is not an instance variable of your dialog, but rather of the method with which you are creating your dialog. This is the easy way. You could also declare your TextViews as instance variables of your Activity and then update them through a handler.
alert.setTitle("Sell how many "+(masterRes.get(currentResIndex).getName())+"?");
alert.setContentView(R.layout.selldialog);
final TextView tvsellAll = (TextView) alert.findViewById(R.id.sellAllText);
final TextView tvsell10 = (TextView) alert.findViewById(R.id.sell10Text);
//etc etc more handles, including buttons
tvsellAll.setText("Sell All ("+String.valueOf(masterRes.get(currentResIndex).getHeld())+") - $"+String.valueOf(calcCost(masterRes.get(currentResIndex).getHeld())));
tvsell10.setText("Sell 10 - $"+String.valueOf(calcCost(10)));
// etc etc more setTexts
btnsell10.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
if (v.isEnabled()) {
int y=masterRes.get(currentResIndex).getHeld();
masterRes.get(currentResIndex).setHeld(y-10);
held -= 10;
money += (calcCost(10));
updateScreen();
tvsellAll.setText("Sell All ("+String.valueOf(masterRes.get(currentResIndex).getHeld())+") - $"+String.valueOf(calcCost(masterRes.get(currentResIndex).getHeld())));
tvsell10.setText("Sell 10 - $"+String.valueOf(calcCost(10)));
tvsellAmt.setText("Sell Amount (0-"+String.valueOf(masterRes.get(currentResIndex).getHeld())+")");
}
}
});
In activity where you creates your dialog, you can declare private variables of dialog, textviews, etc, then they will be accessible anywhere in activity.
dialogA = new Dialog(myActivity.this, android.R.style.Theme_Dialog);
dialogA.setContentView(R.layout.myDialog);
// ...
tv1 = (TextView) dialogA.findViewById(R.id.textView1);
Button b1 = (Button) dialogA.findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String s1 = tv1.getText().toString();
Toast.makeText(myActivity.this, s1, Toast.LENGTH_SHORT).show();
dialogA.cancel();
}
});
dialogA.show();

Categories

Resources