Android Adventures - A NumberPicker DialogFragment Project
Written by Mike James   
Thursday, 14 August 2014
Article Index
Android Adventures - A NumberPicker DialogFragment Project
Adding the Number Pickers
Trying it out

The Button onClick Done Event

Now that we have hooked up the Activity to the DialogFragment we can return to the button's onClick event handler and write code that allow it to send the current value on the NumberPickers to the Activity and dismiss the dialog. 

We need to get the current value as displayed on the NumberPickers. We could have chosen to update the current value every time the made a change. In this case all we need is to update the current value when the Done button is selected. A simple function to get that value from the NumberPickers is:

private int getValue(){
 int value=0;
 int mult=1;
 for(int i=0;i<numDials;i++){
  value+=numPickers[i].getValue()*mult;
  mult*=10;
 }
 return value;
}

This gets the value of each NumberPicker in turn and multiplies by the appropriate power of ten.

The Onclick handler is:

okButton.setOnClickListener(
  new View.OnClickListener(){
    @Override
    public void onClick(View view) {
     currentValue=getValue();
     if (mListener != null) {
      mListener.onDone(currentValue);
     };
     dismiss();
   }
});

That's all we need everything is now ready to be used.

Trying it out

To try the dialog out all we have to do is add a button and a ViewText to the Activity's layout. 

The button just creates an instance of the dialog and adds it to the FragmentManager  

Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(
   new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    NumberDialog myDiag =
           NumberDialog.newInstance(3, 123);
    myDiag.show(getFragmentManager(), "Diag");
   }
});

This, of course has to be added to the end of the onCreate method. Notice that this creates a dialog with three NumberPickers set to 123. 

We also need to implement the dialog's interface:

public class MyActivity extends Activity
 implements NumberDialog.OnNumberDialogDoneListene{

And right clicking on the interface name and selecting generate methods produces an empty onDone method which simply displays the value returned by the dialog:

@Override
public void onDone(int value) {
 TextView tv=
       (TextView) findViewById(R.id.textView);
 tv.setText(Integer.toString(value));
}

Now if you run the program you will see the three NumberPickers set to the intial value. If you change them and select Done you will see the value appear in the ViewText:

 

dialog3

 

However there are a number of shortcomings. The first is that if you dismiss the dialog by clicking on the app the value is lost and nothing is updated. You can implement a cancel button and it is up to you to decide on the correct behavior when the dialog is dismissed by "other means".

Persisting the current value

A more important problem is that the current value  isn't persisted when the DialogFragment is destroyed and recreated. If you start to enter a value by spinning the NumberPickers and then rotate the device you will see that the UI is correctly recreated but the value displayed will return to the original initial value.

We need to persist the current value by saving it as part of the onSaveInstance method:

@Override
public void onSaveInstanceState(Bundle outState){
 super.onSaveInstanceState(outState);
 outState.putInt("CurrentValue",getValue());
}

This automatically saves the setting of the NumberPickers when the dialog is being destroyed by the system.

What about restoring it?

There are lots of places you could restore the value - in any method that receives a  savedInstanceState bundle. 

In this case a reasonably logical place to do the job is in the onCreate because this is where the intial values are set. We can let the intial values be set and then override any that we consider have changed since the first creation of the dialog;

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 if (getArguments() != null) {
  numDials = getArguments().getInt(ARG_numDials);
  currentValue =
          getArguments().getInt(ARG_initValue);
  numPickers = new NumberPicker[numDials];
}

 if (savedInstanceState!=null){
  currentValue=savedInstanceState.
                    getInt("CurrentValue");
 }
}

Notice the way that the CurrentValue is first set to the initial value when the dialog is created and then this is overwritten by the updated value if there is a savedInstanceBundle. 

Now the current value is persistent and you can rotate the device as much as you like and the NumberPickers will not change. 

Tasks For The Reader

There are a lot of missing facilities in this dialog. 

If you would like to try your hand at extending the project here are some suggestions in rough order of difficulty:

  1. Add a parameter that allows the user to set the title of the dialog.
  2. Add a cancel button.
  3. Add a check that the user isn't specifying too many NumberPickers. 
  4. Allow the user to specify the max/min values for the NumberPickers and the multiplier to be applied.
  5. Work out how to style the dialog and allow the user to set the style.

Listing

For completeness the full listing of the DialogFragment is:


public class NumberDialog extends DialogFragment{
 private static final String
                 ARG_numDials = "numDials";
 private static final String
                 ARG_initValue = "initValue";
 private int numDials;
 private int currentValue;

 private NumberPicker[] numPickers;
 private OnNumberDialogDoneListener mListener;

 public static NumberDialog newInstance(
               int numDials, int initValue) {
  NumberDialog numdialog = new NumberDialog();
  Bundle args = new Bundle();
  args.putInt(ARG_numDials, numDials);
  args.putInt(ARG_initValue, initValue);
  numdialog.setArguments(args);
  return numdialog;
 }

 public NumberDialog() {
  // Required empty public constructor
 }

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  if (getArguments() != null) {
   numDials = getArguments().getInt(ARG_numDials);
   currentValue =
            getArguments().getInt(ARG_initValue);
   numPickers = new NumberPicker[numDials];
}
   if (savedInstanceState!=null){
    currentValue=savedInstanceState.
                        getInt("CurrentValue");
}
 }

 private int getDigit(int d, int i) {
  String temp = Integer.toString(d);
  if (temp.length() <= i) return 0;
  int r = Character.getNumericValue(
        temp.charAt(temp.length() - i - 1));
  return r;
 }

 private int getValue(){
  int value=0;
  int mult=1;
  for(int i=0;i<numDials;i++){
   value+=numPickers[i].getValue()*mult;
   mult*=10;
  }
 return value;
 }

 
 @Override
 public View onCreateView(
        LayoutInflater inflater,
        ViewGroup container,
        Bundle savedInstanceState) {
  LinearLayout linLayoutH =
             new LinearLayout(getActivity());
  LinearLayout.LayoutParams params =
     new LinearLayout.LayoutParams(
       LinearLayout.LayoutParams.WRAP_CONTENT,            LinearLayout.LayoutParams.WRAP_CONTENT);
  linLayoutH.setLayoutParams(params);
  for (int i = numDials - 1; i >= 0; i--) {
   numPickers[i] = new NumberPicker(getActivity());
   numPickers[i].setMaxValue(9);
   numPickers[i].setMinValue(0);
   numPickers[i].setValue(
                     getDigit(currentValue, i));
   linLayoutH.addView(numPickers[i]);
  }
  LinearLayout linLayoutV =
                new LinearLayout(getActivity());
  linLayoutV.setOrientation(LinearLayout.VERTICAL);
  linLayoutV.addView(linLayoutH);
  Button okButton = new Button(getActivity());
  okButton.setOnClickListener(
    new View.OnClickListener(){
      @Override
      public void onClick(View view) {
       currentValue=getValue();
       if (mListener != null) {
         mListener.onDone(currentValue);
       };
       dismiss();
      }
    });

  params.gravity = Gravity.CENTER_HORIZONTAL;
  okButton.setLayoutParams(params);
  okButton.setText("Done");
  linLayoutV.addView(okButton);
  return linLayoutV;
 }

 @Override
 public void onAttach(Activity activity) {
  super.onAttach(activity);
  try {
        mListener =
            (OnNumberDialogDoneListener) activity;
  } catch (ClassCastException e) {
    throw
      new ClassCastException(activity.toString()
   + " must implement OnNumberDialogDoneListener");
  }
 }
 
 @Override
 public void onDetach() {
  super.onDetach();
  mListener = null;
 }

 @Override
 public void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putInt("CurrentValue",getValue());
 }

 public interface OnNumberDialogDoneListener {
  public void onDone(int value);
 }
}

 

 

You can download the code for the programs from the CodeBin (note you have to register first).

 

Android Adventures - Mastering Fragments & Dialogs

coverfrag

Contents

This book is currently being revised. Chapter 5 and later refer to an earlier version of Android Studio - revisit for updates.

  1. Introducing Fragments
  2. Fragments and Android Studio XML
  3. Static Fragments
  4. Dynamic Fragments (Coming soon)

  5. Fragment And Activity Working Together
  6. Managing Fragments
  7. Custom dialogs using DialogFragment
  8. Dialog Classes In DialogFragment
  9. A NumberPicker DialogFragment Project
  10. ViewPager

If you are interested in creating custom template also see:

Custom Projects In Android Studio

Androidgears

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter,FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.

 

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info

 



Last Updated ( Saturday, 25 July 2015 )