Search code examples
androidandroid-fragmentsandroid-fragmentactivityandroid-dialogfragment

How do I create a pop-up Android Fragment that takes in a text input before returning to my main activity?


For my app, I want my user to be able to press a "Change User" button, have a fragment pop-up, take in a text input, and then save to shared preferences upon exit, returning to the activity it was called from.

I would like my methodology to be similar to a TimePicker fragment I have already implemented, which I will post at the end of this.

I am currently using this tutorial as a guide: Dialog Fragment Tutorial

Here is my Dialog Fragment Class:

public class EditNameDialog extends DialogFragment {

    private EditText mEditText;

    public EditNameDialog() {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_edit_name, container);
        getDialog().setTitle("Hello");
        return view;
    }

    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        return true;

    }
}

Here is my MainActivity that calls it:

public class MainActivity extends Activity implements
        TimePickerFragment.FragmentCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Use the current time as the default values for the picker
        SharedPreferences sharedPref = this.getSharedPreferences("prediction",
                Context.MODE_PRIVATE);

        final Calendar c = Calendar.getInstance();
        int defaultHour = c.get(Calendar.HOUR_OF_DAY);
        int defaultMinute = c.get(Calendar.MINUTE);

        int hour = sharedPref.getInt("prediction_hour", defaultHour);
        int minute = sharedPref.getInt("prediction_min", defaultMinute);

        String timeString = Integer.toString(hour) + ":"
                + Integer.toString(minute);

        TextView predictionText = (TextView) findViewById(R.id.prediction_time);
        predictionText.setText(timeString);
    }

    public void showTimePickerDialog(View v) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getFragmentManager(), "timePicker");

    }

    public void showUserNameEdit(View c) {
        EditNameDialog newFragment = new EditNameDialog();
        newFragment.show(getSupportFragmentManager(), "editName");
    }

    @Override
    public void TimeUpdated(int hour, int minute) {
        Toast.makeText(this, "Hour: " + hour + " Minute: " + minute,
                Toast.LENGTH_SHORT).show();
        // finish();
        // startActivity(new Intent(this, MainActivity.class));

        String timeString = Integer.toString(hour) + ":"
                + Integer.toString(minute);
        TextView predictionText = (TextView) findViewById(R.id.prediction_time);
        predictionText.setText(timeString);
    }
}

I believe that this is where the problem lies. Either I call show() with getFragmentManager() and it says that show() only takes getSupportFragmentManager(), or I call it with getSupportFragmentManager(), and it says that Activity does not have this method. I know that I could extend my activity with FragmentActivity instead, but this creates other issues. I would like to call it like my TimePickerFragment, shown below:

public class TimePickerFragment extends DialogFragment implements
        TimePickerDialog.OnTimeSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        Context context = getActivity();
        SharedPreferences sharedPref = context.getSharedPreferences(
                "prediction", Context.MODE_PRIVATE);

        final Calendar c = Calendar.getInstance();
        int defaultHour = c.get(Calendar.HOUR_OF_DAY);
        int defaultMinute = c.get(Calendar.MINUTE);

        int hour = sharedPref.getInt("prediction_hour", defaultHour);
        int minute = sharedPref.getInt("prediction_min", defaultMinute);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        Context context = getActivity();
        SharedPreferences sharedPref = context.getSharedPreferences(
                "prediction", Context.MODE_PRIVATE);
        SharedPreferences.Editor editorh = sharedPref.edit();
        editorh.putInt("prediction_hour", hourOfDay);
        editorh.commit();
        SharedPreferences.Editor editorm = sharedPref.edit();
        editorm.putInt("prediction_min", minute);
        editorm.commit();
        mCallbacks.TimeUpdated(hourOfDay, minute);
    }

    /**
     * Interface
     */
    private FragmentCallbacks mCallbacks;

    public interface FragmentCallbacks {
        void TimeUpdated(int hour, int minute);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mCallbacks = (FragmentCallbacks) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(
                    "Activity must implement Fragment Two.");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

}

Solution

  • I suggest staying up-to-date with the latest support libraries, so that your app uses the latest in user interface designs (Material Design) and across all devices your app will retain the same look. So I suggest do as follows:

    For DialogFragments, you should pass arguments to start a new instance of your fragment, instead of using shared preferences. While shared preferences still works, it isn't a very ideal solution. Best practices for passing arguments to a dialog fragment are shown below with a method called newInstance(). The reason you don't pass the arguments to the constructor can be found here

    Use AppCompatDialog in onCreateDialog, and the use the imports shown below:

    import android.support.v4.app.DialogFragment;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatDialog;
    
    ...
    ...
    
    public class TimePickerFragment extends DialogFragment implements
        TimePickerDialog.OnTimeSetListener {
    
    public static TimePickerFragment newInstance(int hour, int min){
        TimePickerFragment frag = new TimePickerFragment();
        Bundle args = new Bundle();
        args.putInt("hour", hour);
        args.putInt("min", min);
        frag.setArguements(args);
        return frag;
    }
    
    @Override
    public AppCompatDialog onCreateDialog(Bundle savedInstanceState) {
    
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int defaultHour = c.get(Calendar.HOUR_OF_DAY);
        int defaultMinute = c.get(Calendar.MINUTE);
    
        int hour = getArguments().getInt("hour");
        int minute = getArguments().getInt("min");
    
        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }
    
        ...
        ...
    }
    

    And when you show it do the following:

    TimePickerFragment.newInstance(hour, min).show(getSupportFragmentManager(), "time_fragment");
    

    And in your activity should extend AppCompatActivity like follows:

    public class MainActivity extends AppCompatActivity implements
        TimePickerFragment.FragmentCallbacks {
    

    Then in your activity use getSupportFragmentManager() when calling show();

    If you don't have the latest support libraries downloaded with your app, add these dependencies to your gradle file:

    dependencies {
        compile 'com.android.support:appcompat-v7:22.1.1'
        compile 'com.android.support:support-v4:22.1.1'
        ...
    }