fragment-How to listen for fallback events in Fragment and how to save Fragment status

Source: Internet
Author: User
Tags hosting

first, how to monitor the fallback event in fragment1. Explanation of the problem

It is very easy to listen for fallback events in activity, because it is good to rewrite the onbackpressed () function directly, but when you want to listen for fallback events in fragment, take it for granted that you want to rewrite the onbackpressed () method, At this point you will find: there is no onbackpressed () method in fragment to rewrite. How can this be broken!

Think, in the previous example, we add into fragment1,fragment2,fragment3,fragment4 in a fragment_container of activity, and when we click Back to rewind the stack, will be transaction back to the stack of fragment operations out of the stack! So, where did these fallback events come from, fragment?

First, the fallback event is always sent to the activity! It is dealt with by the activity itself after being sent to the activity. For example, it will fragment the contents of the stack back to the stack this operation.
Second: You have to know: fragment is just a control in the activity, although we may have made him as large as the activity to cover the entire page, looks like the activity looks no different, but he is a control! How does the system distribute a fallback event to a control? This is of course not possible.

2. Solutions

Now that it's clear, fragment is just a control, and fallback events can only be intercepted in activity. Then we can figure it out.
First, we can write our own onbackpressed () method in the fragment class to handle the callback event.
You can then use the callback to pass the fragment instance of the fallback event to the activity.
Finally, after getting the fragment instance, you can call the Onbackpressed () method of the fragment instance in the activity's Onbackpress () method.
In this way, we intercepted the fallback event in the fragment.

3. Example

Below, let's look at the effect in one example.
As follows:

As you can see from below, when you click the Back button in Fragment3, the return event is captured and the TextView on the Fragment3 is displayed as "Ragment3 snaps to the fallback event!" "But I only capture it once, and when the second click exits the default action: Transaction out of the stack.

Here's a look at the specific implementation process:
About the mainactivity layout and the addition of fragment is no longer spoken, the following directly from the callback start
1. Define the Onbackpress () function and the processing in FRAGMENT3:

public class Fragment3 extends Fragment {      private Boolean mhandledpress = false;      TextView TV;      ....... @Override public      void onactivitycreated (Bundle savedinstancestate) {          super.onactivitycreated ( Savedinstancestate);          TV = (TextView) GetView (). Findviewbyid (R.ID.FRAGMENT3_TV);      }        public Boolean onbackpressed () {          if (!mhandledpress) {              tv.settext ("Fragment3 \ nthe" Snap to the fallback event! ");              Mhandledpress = true;              return true;          }          return false;      }  }  

The above code, no difficulty, is defined as a onbackpressed () function, which returns a Boolean value, meaning, if the return event is processed to return true, if you do not handle the return false, let the upper layer processing.
The variable mhandledpress is used to specify that it is processed only once, and onbackpressed () will return false after processing one time.
2, define the callback function in Fragment3, pass the reference of own instance
(1), define an interface to be used as a callback, and the corresponding variable:

protected Backhandlerinterface backhandlerinterface;  Public interface Backhandlerinterface {public      void setselectedfragment (Fragment3 backhandledfragment);  }  

Notice that the FRAGMENT3 is passed in the callback! Because we're going to call the onbackpressed () function we wrote in Fragment3 when the main activity is handling onbackpress (), we're going to pass in the Fragment3 instance.
(2), then assign a value to the Backhandlerinterface variable
As with the previous article, we want to force the activity to implement this interface, so we use casts to assign values. In the previous article, we made the cast in the Onattach () function, the code is as follows:

public void Onattach (activity activity) {      Super.onattach (activity);      try{          backhandlerinterface = (backhandlerinterface) getactivity ();      } catch (Exception e) {          throw new classcastexception ("Hosting activity must implement Backhandlerinterface");  }

In fact, the Onattach () callback has already bound fragment and activity together, so as long as the life flow in the Onattach () after any one life cycle, we can use getactivity to get activity instances, To make the cast, so here we're going to change places and do it in the OnCreate () function:

public void OnCreate (Bundle savedinstancestate) {      super.oncreate (savedinstancestate);      if (! ( Getactivity () instanceof Backhandlerinterface) {          throw new classcastexception ("Hosting activity must implement Backhandlerinterface ");      } else {          Backhandlerinterface = (backhandlerinterface) getactivity ();      }  }  

This throws an exception and does not use try...catch ... To do, but directly using instanceof to determine whether the current activity is not backhandlerinterface instance, that is, if the backhandlerinterface has been derived, if not directly thrown exception, If the derivation is derived, the cast is assigned a value.
(3), in the appropriate position to pass their own instance through the callback. The code is as follows:

The code for this setting Fragment3 instance, as long as the FRAGMENT3 instance has been produced in the life cycle can be set, that can be placed in the life cycle after OnCreate () function, that is OnCreate (), Oncreateview (), Onactivitycreated (), OnStart (); Although I have tested it, it is feasible to put any of these functions, but after onactivitycreated () the activity is finally OnCreate () executed, So it is the most insured place in onactivitycreated () or OnStart (). So here's the OnStart (), the code is as follows:

public void OnStart () {      super.onstart ();      Backhandlerinterface.setselectedfragment (this);  }  

So the complete code logic is this:

public class Fragment3 extends Fragment {      //define callback functions and variables      protected backhandlerinterface backhandlerinterface;      Public interface Backhandlerinterface {public          void setselectedfragment (Fragment3 backhandledfragment);      }           @Override public      void OnCreate (Bundle savedinstancestate) {          super.oncreate (savedinstancestate);          The callback function assigns a value          if (!) ( Getactivity ()  instanceof backhandlerinterface) {              throw new classcastexception ("Hosting activity must Implement Backhandlerinterface ");          } else {              Backhandlerinterface = (backhandlerinterface) getactivity ();          }      }        @Override public      void OnStart () {          super.onstart ();          Send an instance of yourself          backhandlerinterface.setselectedfragment (this);}  }  

3, in the mainactivity, the fallback interception, the code is as follows:

public class Mainactivity extends Fragmentactivity implements Fragment3.backhandlerinterface {      private Fragment3 selectedfragment;      ....... @Override public      void Setselectedfragment (Fragment3 backhandledfragment) {          this.selectedfragment = backhandledfragment;      }        @Override public      void onbackpressed () {          if (selectedfragment = = NULL | |!selectedfragment.onbackpressed ()) {              super.onbackpressed ();}}}      

(1), first, the mainactivity implementation of the Fragment3.backhandlerinterface interface
Implement the Setselectedfragment () function here, with the following code:

public class Mainactivity extends Fragmentactivity implements Fragment3.backhandlerinterface {      private Fragment3 selectedfragment;      ....... @Override public      void Setselectedfragment (Fragment3 backhandledfragment) {          this.selectedfragment = backhandledfragment;      }  }  

(2), and then the fallback interception in the onbackpressed () callback

public void onbackpressed () {      if (selectedfragment = = NULL | |!selectedfragment.onbackpressed ()) {          Super.onbackpressed ();      }  }  

Note here that the logic is in Call super.onbackpressed (), assuming that selectedfragment.onbackpressed () returns false, that is, onbackpressed () in Fragment3 Returns false, which means that the default action is not taken until the fallback event is stopped.

second, how to save the fragment state after performing the replace operation

First of all, we first explain a phenomenon, we first look at the following demo:

The process is like this:
1, first in the edittext of Fragment1 first a few words
2. Then if you call Addfragment () to add Fragment2, and then when you return from Fragment2, you find that there are some words.
3, but if we add Fragment2 by calling replace (), we will find that when we return, the words are gone!

This illustrates a problem where the view of the fragment added by the call Addfragment is saved to the views tree (Viewtree), where the state of each control is saved. But if you call replace () to add fragment, as we mentioned earlier, replace () is implemented by emptying all fragment views in the same container from Viewtree! Then add the specified fragment. Since the repalce operation will completely empty all previous views, when using transaction fallback, it is only necessary to rebuild each fragment view, so it is back from the replace operation, all the controls are rebuilt, and all the previous user input is gone.

Here, you first understand a problem, repalce () operation, will empty the same container all fragment views! Note the word: Please empty is fragment's view! Instances of fragment are not destroyed! Because instances of fragment are managed by Fragmentmanager. When the fragment view is destroyed, the fragment instance is not destroyed. The two of them are not simultaneous, that is, the variable defined in fragment, the value given in the last run is always there. When the fragment instance will be destroyed, of course it will be destroyed when it is not used. When will not be used, that is, it is impossible to return to this operation, it will be destroyed.
In the example above, Fragment1 is destroyed by Fragment2 's repalce operation, but when the replace operation is performed, the operation is added to the fallback stack, and Fragmentmanager knows that The user may also use fragment1 again by fallback, so the instance of Fragment1 is preserved. Conversely, if, in the execution of the repalce operation, did not add to the fallback stack, that Fragmentmanager must also know that the user can not go back to the last Fragment1 interface, So its fragment instance will be cleared at the same time as the Fragment1 view is cleared.

Speaking so much, now if we want to use repace operation, while saving the state of the previous fragment interface, what should I do?

method One: control state save and restore

As we mentioned above, when we clear the fragment view, if we add the operation to the fallback stack at the same time, then its view is cleared from the Viewtree, but its instance is saved in Fragmentmanager, and its variables will be saved until the next time. But the view will be rebuilt when it comes back.
The first method, we can use a variable to save the EditText current string, before replace the value in the EditText in this variable, when the return to create the view again, the Edittxt assignment is not good.
The code is as follows:

public class Fragment1 extends Fragment {private String meditstr;      Private EditText EditText; @Override public View Oncreateview (layoutinflater inflater, ViewGroup container, Bundle Savedinstancesta          TE) {View Rootview = inflater.inflate (R.layout.fragment1, container, false);          EditText = (editText) Rootview.findviewbyid (R.id.fragment1_edittext);          Edittext.settext (MEDITSTR);      return rootview; } @Override public void onactivitycreated (Bundle savedinstancestate) {super.onactivitycreated (Savedi          Nstancestate);          Button Btnreplace = (button) GetView (). Findviewbyid (R.id.fragment1_repalce);  Btnreplace.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v)                                    {meditstr = Edittext.gettext (). toString ();          ............ }          });  ............ }   }

The code above is always two steps:
First step: Save the state before Repalce

Meditstr = Edittext.gettext (). toString ();  

Step two: Restore state at creation time

Edittext.settext (MEDITSTR);  

This can work, but what if we have a lot of controls? What's so complicated about that? This will not be a good idea. Because the initialization and assignment of many variables will make the code look ugly and difficult to understand.

method Two: Simply add the ID value to the control

In real-time also encountered a workaround, that is, to add an ID to the EditText control, as long as the EditText control to add the ID, do not need those above the replace the value before the save is created when the restore, its contents will be saved. It is not known whether other controls can also save the user's input value by adding an ID value, namely:

<edittext      android:id= "@+id/fragment1_edittext"      android:layout_width= "Match_parent"      android: layout_height= "Match_parent"      android:gravity= "Top|left"      android:background= "#ffffff"      android:hint = "Here is EditText, enter the text here."/>  

method Three: Save the Fragmentview view

Method One and method two feelings are still too reliable solution, since the variables in fragment will be saved, then we directly save the fragment view directly into the variable, the system in the use of Oncreateview () to create a view, we directly return to the saved view.
Based on the idea above, we do this in code:
1. Create a variable to save the fragment view:

Private View Rootview;  

2, then see the realization of Oncreateview

Public View Oncreateview (layoutinflater inflater, ViewGroup container,          Bundle savedinstancestate) {      return Getpersistentview (Inflater, container, savedinstancestate, r.layout.fragment1);  }  

Can be seen, compared to the previous direct return inflater.inflate (R.layout.fragment1, container,false); Rebuild the view, here is a Getpersistentview () function returned, Here's a look at the implementation of this function:

Public View Getpersistentview (layoutinflater inflater, ViewGroup container, Bundle savedinstancestate, int layout) {      if (Rootview = = null) {          //inflate the layout for this fragment          Rootview = inflater.inflate (Layout, container , false);      } else {(          (viewgroup) rootview.getparent ()). Removeview (Rootview);      }      return rootview;  }  

This code is returned to Rootview. That is, when the rootview==null, that is, the first time the creation, the use of inflater.inflate () to create the initial state of the view, when the next time to enter the interface, such as the following through the fallback operation into the Fragment1, This time the Rootview is no longer empty. However, the view returned in Oncreateview () is to be added to the viewtree. And here the Rootview view in the last has been added to the inside, a view instance can not be add two times, otherwise it will be the following error! So, in this case, if Rootview already exists in Viewtree, remove it from Viewtree first.

OK, here is finished, the source code will be given below. Let's take a look at the final:

fragment-How to listen for fallback events in Fragment and how to save Fragment status

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.