-->

Saving information from one fragment and dialog if

2019-01-29 03:22发布

问题:

I have a screen where the user presses a button to bring up a DialogFragment with an EditText. They enter their information, and press Ok. If they press the button again, I've made it so the EditText will display the information they had just put in, and everything works fine.

However, if they enter the information, press Ok, and then use the options menu to go look at another screen/fragment, the information will not be displayed in the EditText once they press the button again.

How can I save the information even if the user navigates away from that screen for a moment? I think it would require a newInstance method in the fragment class, and then have the hosting activity call newInstance instead of the constructor. But I'm unsure of how to implement it. Any help appreciated. Thanks!

SingleFragmentActivity:

public abstract class SingleFragmentActivity extends FragmentActivity
{
    protected abstract Fragment createFragment();

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

        if (fragment == null)
        {
            fragment = createFragment();
            fm.beginTransaction()
                .add(R.id.fragmentContainer, fragment)
                .commit();
        }
    }
}

Relevant code from hosting activity, AdviceActivity:

public class AdviceActivity extends SingleFragmentActivity
{
    @Override
    protected Fragment createFragment()
    {
        return new AdviceFragment();
    }
}

Relevant code from the hosting fragment, AdviceFragment:

private boolean isTextButtonFirstClick = true;

...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
    mTextButton = (Button) v.findViewById(R.id.textButton);
    mTextButton.setOnClickListener(new View.OnClickListener()
    {
        public void onClick(View v)
        {
            if (isTextButtonFirstClick)
            {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                InputTextFragment dialog = InputTextFragment.newInstance("", isTextButtonFirstClick);
                dialog.setTargetFragment(AdviceFragment.this, REQUEST_TEXT);
                dialog.show(fm, DIALOG_TEXT);
                isTextButtonFirstClick = false;
            }
            else
            {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                InputTextFragment dialog = InputTextFragment.newInstance(mAdvice.getText(), isTextButtonFirstClick);
                dialog.setTargetFragment(AdviceFragment.this, REQUEST_TEXT);
                dialog.show(fm, DIALOG_TEXT);
            }
        }
    });
...
}

Relevant code from DialogFragment InputTextFragment:

public static InputTextFragment newInstance(String text, boolean isTextButtonFirstClick)
{
    Bundle args = new Bundle();
    args.putSerializable(EXTRA_TEXTBUTTON_FIRSTCLICK, isTextButtonFirstClick);
    args.putSerializable(EXTRA_TEXT, text);

    InputTextFragment fragment = new InputTextFragment();
    fragment.setArguments(args);

    return fragment;
}

...

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
    ...

    boolean isTextButtonFirstClick = getArguments().getBoolean(EXTRA_TEXTBUTTON_FIRSTCLICK);

    final EditText editText = (EditText) v.findViewById(R.id.dialogInputEditText);
    if (!isTextButtonFirstClick)
    {
        editText.setText(text);
        editText.setSelection(text.length());
    }

    ...
}

XML activity_fragment:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</RelativeLayout>

回答1:

It sounds as if the boolean isTextButtonFirstClick simply is mistakenly set to false.

This would make sense of a new instance of Advice Fragment is created during the navigation.

Try

  • Setting setRetainInstance(true) in onCreate of Advice Fragment in order to keep the boolean.

  • If R.id.fragmentContainer is a fragment tag in XML, then change it to a LinearLayout or RelativeLayout (you are adding a fragment on top anyways, and I suspect the findFragmentById to always return null)

  • Change the code in your Activity to:

    FragmentManager fm = getSupportFragmentManager();
    Fragment fragment = fm.findFragmentByTag("singleFragment");
    
    if (fragment == null)
    {
        fragment = createFragment();
    }
    
       fm.beginTransaction()
            .replace(R.id.fragmentContainer, fragment,  "singleFragment" )
            .commit();
    

Generally when programmatically adding fragments, you use tags, whereas when defining the fragment in XML letting the Android framework handle the lifecycle, you can find it by id.

Looks to me as if you are in a in-between solution, trying to do both, which will not work as intended.

Hope this can help and sorry in advance if there are formatting issues. The answer has been written from a phone :-)

Edit:

If you only wanted to save simple types of info (such as the boolean), I would point you to one of my old answers here: Saving textview in a fragment when rotating screen

Your last comment revealed that you have some complex information (text, photos, videos etc) that you want to persist.

Making all that Parcelable will be a huge pain, so here goes my second advice:

  1. Add EventBus to your project https://github.com/greenrobot/EventBus
  2. Create a holder class for the information, such as

    public class AdviceHolder {
        private boolean isTextButtonFirstClick;
        private String text;
        private BitMap image;
        ... 
    
       // setter and getter methods (if using eclipse alt+shit+s 'create setter and getters from fields') 
    }
    
  3. Now when starting AdviceActivity, you prepare a new AdviceHolder

    public class AdviceActivity extends SingleFragmentActivity
    {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            // make a new AdviceHolder if none existed
            AdviceHolder holder = new AdviceHolder();
            AdviceHolder existingHolder = EventBus.getDefault().getStickyEvent(AdviceHolder.class);
            if (existingHolder == null) {        
                 EventBus.getDefault().postSticky(holder);
            }
    
            super.onCreate(savedInstanceState);
        }
    
        @Override
        protected Fragment createFragment()
        {
            return new AdviceFragment();
        }
    }
    

This step will make a AdviceHolder object available anywhere in your code. Think of it as a global repository.

This means that no matter how you move between Activites or Fragments, they all have access to AdviceHolder and can edit it.

So for example in you AdviceFragment:

private AdviceHolder holder;

...
@Override
public void onCreate (Bundle savedInstanceState) {
    AdviceHolder holder = (AdviceHolder)EventBus.getDefault().getStickyEvent(AdviceHolder.class);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
    // read isTextButtonFirstClick from holder
    boolean isTextButtonFirstClick = holder.isTextButtonFirstClick();

    mTextButton = (Button) v.findViewById(R.id.textButton);
    mTextButton.setOnClickListener(new View.OnClickListener()
    {
        public void onClick(View v)
        {
            if (isTextButtonFirstClick)
            {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                InputTextFragment dialog = InputTextFragment.newInstance("", isTextButtonFirstClick);
                dialog.setTargetFragment(AdviceFragment.this, REQUEST_TEXT);
                dialog.show(fm, DIALOG_TEXT);

                // update isTextButtonFirstClick in holder
                holder.setTextButtonFirstClick(false);
                EventBus.getDefault().postStickyEvent(holder);
            }
            else
            {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                InputTextFragment dialog = InputTextFragment.newInstance(mAdvice.getText(), isTextButtonFirstClick);
                dialog.setTargetFragment(AdviceFragment.this, REQUEST_TEXT);
                dialog.show(fm, DIALOG_TEXT);
            }
        }
    });
...
}

When you at some point are done filling the AdviceHolder (and sent it to a server or whatever the plan is), remove it from EventBus to enable the creation of a new holder in AdviceActivity.

EventBus.getDefault().removeStickyEvent(AdviceHolder.class);

For other examples about EventBus have a look at http://www.stevenmarkford.com/passing-objects-between-android-activities/

This is all a lot of information, hope it is not too confusing.