Fragment throw IllegalStateException after device orientation change - android

I have a listfragment in an activity, a dialogfragment will open on pressing on a row for the further options, now I rotate the device and choose a option from dialogfragment, its throws an IllegalStateException .... Fragment is not attached.
choiceDialog
.setOnClickListDialogClickListener(new StandardListDialogFragment.OnClickListener<String>() {
#Override
public void onClick(DialogInterface dialog, int which,
String value) {
Intent intent = new Intent(this.getActivity(),
DutyEditor.class);
startActivityForResult(intent, 0);
dialog.dismiss();
}
});

This happens because the Fragment is destroyed and re-created with a FragmentManager whenever orientation changes.
You should stick to these rules when working with Fragments
Avoid setters when using Fragments.
Never hold a field reference to a Fragment in an Activity.
You can hold a field reference to an Activity on a Fragment after onAttach() and before onDetach(), but I find it better to call getActivity() and check for null every time I need it for short operations.
The best option is to call startActivityForResult() in the DialogFragment itself.
But whenever you really need to deliver the DialogFragment click events to the Activity, use an Interface. For example.
public final class SomeDialogFragment extends DialogFragment {
/**
* Callbacks of {#link SomeDialogFragment}
*/
public interface SomeDialogFragmentCallbacks {
/**
* Called when user pressed some button
*/
void onSomeButtonClick();
}
#Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
// Make sure the Activity can receive callbacks
if (!(activity instanceof SomeDialogFragmentCallbacks)) {
throw new RuntimeException("Should be attached only to SomeDialogFragmentCallbacks");
}
}
// now whenever a button is clicked
#Override
public void onClick(DialogInterface dialog, int which) {
final SomeDialogFragmentCallbacks callbacks = (SomeDialogFragmentCallbacks) getActivity();
if (callbacks != null) {
callbacks.onSomeButtonClick();
}
}
}
And all Activities that use this DialogFragment should implelent the callbacks method.
public final class SomeActivity extends Activity implements SomeDialogFragmentCallbacks {
#Override
public void onSomeButtonClick() {
// Handle some DialogFragment button click here
}
}

Related

How can I confirm exit from the individual fragment?

I have been able to confirm exit before leaving fragment. However my activity has six cards each of which calls a fragment when clicked. The confirm exit applies to all fragments in this activity. How can I apply to individual fragment say to the Garden Mapping card which calls its own fragment(It is called GpsFragment). I need to confirm exit before leaving this Fragment(GpsFragment)
This is my main menu
This is my code in the main activity
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 1) {
new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Closing EzyAgric")
.setMessage("Are you sure you want to close EzyAgric?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setNegativeButton("No", null)
.show();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Define an interface to comunicate with your activity and fragment
public abstract class BackPressFragment extends BaseFragment {
public abstract boolean processBackPress();
}
YourFragment extends BackPressFragment {
.....
}
And process key event in your activity, pass event through the interface.
Hi you need to send the onBackPressed() event to the fragment. Best way to do that is via an interface.
Create an Interface like this:
public interface IOnBackPressListener {
void onBackPressed();
}
Now implement your interface in the fragment like this:
public class MyFragment extends Fragment implements IOnBackPressListener {
public MyFragment(){
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onBackPressed() {
Log.e("Fragment","On Back pressed");
// Alert code comes here
}
}
Now in your activity write the following code in onBackPressed()
#Override
public void onBackPressed() {
IOnBackPressListener fragment=((IOnBackPressListener)
getSupportFragmentManager().findFragmentById(R.id.container));
if(fragment!=null)
fragment.onBackPressed();
}

android prevent fragment leave on back pressed

I have a ViewPager inside a FragmentActivity. For each page I have a Fragment with an EditText.
I want to prevent the user leaving the screen if the EditText value has changed, showing a warning Dialog asking if he/she really wants to leave the screen.
For that I need to check the EditText value when the back Button is pressed.
I have only found how to do it but using the onBackPressed() method in the container FragmentActivity.
The problem is that I don't know how to access the EditText inside the Fragment from the parent FragmentActivity.
Is it not possible just to do it in the Fragment? I've tried the method onStop and onDetach but they are called after leaving the Fragment, so I cannot stop it.
Any ideas?
Try this in your parent Activity:
#Override
public void onBackPressed() {
super.onBackPressed();
YourFragment fragment = (YourFragment) getSupportFragmentManager().getFragments().get(viewPager.getCurrentItem());
}
Then you have access to your Fragment so you can add a parameter in your fragment textHasChanged (for example) and update it when the text changes.
You may create interface which has method onBackPressed(your fragment should implement this interface), then override method onBackPressed in your activity, then find your fragment by tag or id -> cast to this interface and call this method.
So you need BackPressed() event in fragment for you problem to check if editext is values is changed or not?
So you have create you own OnBackpressed() interface.
Create a interface OnBackPressedListener
public interface OnBackPressedListener {
public void doBack();
}
Create a new class name BaseBackPressedListener
public class BaseBackPressedListener implements OnBackPressedListener {
private final FragmentActivity activity;
public BaseBackPressedListener(FragmentActivity activity) {
this.activity = activity;
}
#Override
public void doBack() {
activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
//activity.getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Create a instance of your newly created class BaseBackPressedListener in your top base Activity where fragment is added
protected OnBackPressedListener onBackPressedListener;
Create a method in your top base activity to set newly created instance of new class above
public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
this.onBackPressedListener = onBackPressedListener;
}
you will have overide onBackPressed() method in you top base activity modify it like this
#Override
public void onBackPressed() {
if (onBackPressedListener != null)
onBackPressedListener.doBack();
else
super.onBackPressed();
}
And lastly you have add this in you fragment onCreateView()
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Activity activity = getActivity();
((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
//your other stuff here
return view;
}
Now you can check on back pressed in your top base activity weather editext value change our not by modifying this methods
#Override
public void doBack() {
if(!IsEditTextChanged)
activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
//activity.getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
IsEditTextChanged is global variable set its values on textchanged
mEditText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if(s.length() != 0)
IsEditTextChanged = true;
// your desired logic here?
}
});
Fragment BackPressed thread
You could create a Boolean value in the FragmentActivity and keep it updated with the EditText updated. In this way you can check the String value instead the EditText (In fact the fragment could not be loaded in the Pager).
For example:
1) create an interface in order to declare the input protocol
public interface InputInterface {
void setEditTextNotEmpty(Boolean notEmpty);
}
2) implement this interface in your FragmentActivity
public class MainActivity implements InputInterface {
private Boolean editTextNotEmpty;
...
#Override
void setEditTextNotEmpty(Boolean changed){
editTextNotEmpty=changed;
}
#Override
public void onBackPressed() {
... your custom code....
}
3) You somehow notify the Fragment when it's visualized as for in this post: How to determine when Fragment becomes visible in ViewPager
When the fragment is visualized, you update the activity boolean if necessary.
4) keep your MainActivity updated when you update the edit text:
yourEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
if (getActivity instanceof InputInterface){
Boolean notEmpty = !TextUtils.isEmpty(s.toString);
((InputInterface)getActivity).setEditTextNotEmpty(notEmpty);
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
I hope it helped.

Android Studio - Can a fragment be modified after it's created?

I have an Activity that has a section where a Fragment is shown. A button is clicked on the Fragment and a new Activity launches with some buttons. Depending on which button is clicked, that activity closes and the Fragment should be updated accordingly but I'm having trouble accessing the views on the Fragment to edit them.
It seems that in the onResume() of the Fragment, I'm unable to access the view using getView(). I've also tried passing in the view from the Activity that the Fragment lives in, but that also returned null.
So I'm wondering if Fragments are static after onCreateView() is called?
SecondActivity calling back into MainActivity:
#Override
protected void onResume() {
super.onResume();
rvAdapter.setOnItemClickListener(new RVAdapter.MyClickListener() {
#Override
public void onItemClick(View v, int position) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.putExtra("key", value);
startActivity(intent);
}
});
}
MainActivity handling the call back and calling a function in the Fragment:
#Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
String value = intent.getStringExtra("key");
if (value != null) {
((ExampleFragment) exampleFragment).updateFragment(value);
}
}
Fragment attempting to update itself:
public void updateFragment(String value) {
TextView textView = (TextView) getView().findViewById(R.id.profile_text_view);
if (value != null) {
textView.setText(value);
}
}
And the result would be NPE on getView() call in the Fragment. I've tried passing in the view from MainActivity via: exampleFragment.getView() but also no luck.

How to change the layout of DialogFragment when the screen is rotated in Android

I have a custom DialogFragment, which contains two DatePicker widgets (from and to, using layout/custom_date_picker).
I need change the layout of dialogFragment when the screen is rotated to landscape (using layout-land/custom_date_picker).
The configuration of the Activity is :
<activity
android:name=".activities.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTop"/>
The implemented DialogFragment source is:
public class DatePickerDialog extends DialogFragment
{
public static final String START_SEARCH_DATE = "startSearchDate";
public static final String END_SEARCH_DATE = "endSearchDate";
private CustomDatePicker dpStartDate;
private CustomDatePicker dpEndDate;
private AlertDialog d;
public interface DatePickerDialogListener
{
void onFinishDatePickDialog(String fromDate, String toDate);
}
public DatePickerDialog()
{
}
// Use this instance of the interface to deliver action events
DatePickerDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the DatePickerDialogListener
#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
mListener = (DatePickerDialogListener) activity;
} catch (ClassCastException e)
{
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString() + " must implement DatePickerDialogListener");
}
}
public static DatePickerDialog newInstance(String startSearchDate, String endSearchDate)
{
DatePickerDialog frag = new DatePickerDialog();
Bundle args = new Bundle();
args.putString(START_SEARCH_DATE, startSearchDate);
args.putString(END_SEARCH_DATE, endSearchDate);
frag.setArguments(args);
return frag;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
View customView = getActivity().getLayoutInflater().inflate(R.layout.custom_date_picker, null);
// Define your date pickers
dpStartDate = (CustomDatePicker) customView.findViewById(R.id.dpStartDate);
dpEndDate = (CustomDatePicker) customView.findViewById(R.id.dpEndDate);
String startSearchDate = getArguments().getString(START_SEARCH_DATE);
String endSearchDate = getArguments().getString(END_SEARCH_DATE);
dpStartDate.updateDate(startSearchDate);
dpEndDate.updateDate(endSearchDate);
d = new AlertDialog.Builder(getActivity())
.setView(customView)
.setTitle(getString(R.string.datepicker_hint))
.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
}
}) //Set to null. We override the onclick
.setNegativeButton(getString(R.string.cancel), null)
.create();
d.show();
d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
boolean b = DateUtils.largerThanEndDate(dpStartDate, dpEndDate);
if (b)
{
ToastUtils.showShortText(getActivity(), getString(R.string.date_error));
} else
{
mListener.onFinishDatePickDialog(dpStartDate.getDate(), dpEndDate.getDate());
dismiss();
}
}
});
return d;
}
}
Can anyone please guide me as to how this can be done ? Thanks in advance.
Edit:
Here the code with which I invoke the DialogFragment:
public void showDatePicker()
{
startSearchDate = dateModel.getStartDate();
endSearchDate = dateModel.getEndDate();
datePickerDialog = (DatePickerDialog)fragmentManager.findFragmentByTag(DATE_PICKER_DIALOG);
if (alertDialog == null)
{
alertDialog = DatePickerDialog.newInstance(startSearchDate, endSearchDate);
alertDialog.show(fragmentManager, DATE_PICKER_DIALOG);
}
}
I suppose you could consider using an if clause in your onCreate() as follows:
int rotation = getWindowManager().getDefaultDisplay().getRotation();
and then do
switch(rotation){
case Surface.ROTATION_0:
case Surface.ROTATION_180:
// show portrait
case Surface.ROTATION_90:
case Surface.ROTATION_270:
// show landscape
}
For portrait mode, show the two dialogs stacked. And for landscape mode, show them side-by-side.
EDIT:
Answering point-wise:
When the phone is rotated, the Activity is necessarily re-created.
This means that onCreate() will be called on rotation. Hence, the rotation state can be determined in onCreate() as shown above. In this case, make rotation a class member field so that it can be accessed throughout the class.
Indeed, as you correctly point out, the dialog can be shown from either onConfigurationChanged() or in onCreate() itself. For this, and for how to save the instance state, please see the attached links.
References:
1. How to properly retain a DialogFragment through rotation?.
2. Why won't Fragment retain state when screen is rotated?.

onCreateDialog is called after show?

I am trying to implement custom DialogFragment. But when I try to show it I am getting NullPointerException. Also as I have noticed onCreateDialog is never implictly called.
What is wrong with it. I have read official manual, and followed all steps in it DialogFragment
Here is my code for custom Dialog Fragment
public class UserInputDialogFragment extends DialogFragment {
InputDialogListener mListener;
private EditText mTextEdit;
public UserInputDialogFragment() {
super();
}
// Use this instance of the interface to deliver action events
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View mainView = inflater.inflate(R.layout.dialog_input, null);
builder.setView(mainView);
mTextEdit = (EditText) mainView.findViewById(R.id.user_input);
if (mTextEdit==null) {
Log.e("ERROR","Text edit is null");
}
// Add action buttons
builder.setPositiveButton(R.string.ok_btn, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick(UserInputDialogFragment.this,mTextEdit.getText().toString());
}
});
builder.setNegativeButton(R.string.cancel_bnt, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogNegativeClick(UserInputDialogFragment.this,mTextEdit.getText().toString());
UserInputDialogFragment.this.getDialog().cancel();
}
});
return builder.create();
}
public interface InputDialogListener {
public void onDialogPositiveClick(DialogFragment dialog, String userInput);
public void onDialogNegativeClick(DialogFragment dialog, String userInput);
}
public void showAndAddHint(FragmentManager manager,String tag,String hint) {
this.onCreateDialog(null);
mTextEdit.setHint(hint);
this.show(manager,tag);
}
#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
mListener = (InputDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement InputDialogListener");
}
}
}
And I am trying to show dialog this way.
UserInputDialogFragment userInputDialogFragment = new UserInputDialogFragment();
userInputDialogFragment.showAndAddHint(getFragmentManager(),"Please enter phone number",task.phoneNumber);
And here is NullPointerException mTextEdit is null.
public void showAndAddHint(FragmentManager manager,String tag,String hint) {
this.onCreateDialog(null);
mTextEdit.setHint(hint);
this.show(manager,tag);
}
The showAndAddHint method won't work as written. What you should do instead is:
1 - Set a member variable mHint = hint;
2 - Call show() exactly the way you're doing it now.
3 - Read the member variable mHint in on create dialog and use it to set the edit text hint.
Don't call onCreateDialog explicitly because the show method does that for you when needed.

Categories

Resources