Fragment basic usage

Time:2021-12-1

1. Fragment overview

1.1 INTRODUCTION

Fragment is a UI fragment that can be embedded in activities, which can make the program make more reasonable and full use of the space of the large screen. The original intention is to adapt to the tablet computer with large screen. It can be regarded as a small activity, also known as activity fragment.

Using fragment, the screen can be divided into several pieces, and then grouped for a modular management. Fragment cannot be used alone. It needs to be nested in an activity, and its life cycle is also affected by the life cycle of the host activity

The official definition is as follows:

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

From the official definition:

  • Fragment depends on activity and cannot exist independently
  • An activity can have multiple fragments
  • A fragment can be reused by multiple activities
  • Fragment has its own life cycle and can receive input events
  • Fragments can be dynamically added or removed while the activity is running

Advantages of fragment:

  • Modularity: we don’t have to write all the code in the activity, but in their own fragments.
  • Reusability: multiple activities can reuse a fragment.
  • Adaptability: different layouts can be easily realized according to the screen size and screen direction of the hardware, so that the user experience is better.

1.2 fragment basic life cycle

image

The general life cycle of fragment is shown in the figure above:

  • Onattach(): called when a fragment is associated with an activity. You can get the activity reference through this method, and you can also get the parameters through getarguments().
  • Oncreate(): called when a fragment is created
  • Onactivitycreated(): called when the activity completes oncreate()
  • Onstart(): called when the fragment is visible.
  • Onresume(): called when the fragment is visible and interactive
  • Onpause(): called when the fragment is not interactive but visible.
  • Onstop(): called when the fragment is not visible.
  • Ondestroyview(): called when the UI of the fragment is removed from the view structure.
  • Ondestroy(): called when the fragment is destroyed.
  • Ondetach(): called when fragment and activity are disassociated.

The fragment life cycle goes through: run, pause, stop and destroy.

  • Running status: when the fragment is visible, the associated activity is running, and it is also running
  • Pause state: when the activity enters the pause state, the associated visible fragments will enter the pause state
  • Stop state: the activity enters the stop state, the associated fragments will enter the stop state, or the fragments can be removed from the activity through the remove (), replace () method of FragmentTransaction, but if the addToBackStack () method is invoked before the transaction is submitted, the fragments of this time will also enter the stop state.
  • Destruction status: when the activity is destroyed, the associated fragments enter the destruction status. Or call the remove() and replace() methods of fragmenttransaction to remove the fragments from the activity, but if the addtobackstack() method is not called before the transaction is committed, the fragments will also enter the destruction state.

When introducing the specific use of fragment, first introduce several core classes of fragment

  • Fragment: the base class of fragment. Any fragment created needs to inherit this class
  • Fragment Manager: manages and maintains fragments. It is an abstract class, and the specific implementation class is fragmentmanagerimpl.
  • Fragment transaction: all operations such as adding and deleting fragments need to be carried out in transaction mode. It is an abstract class, and the concrete implementation class is backstackrecord

Extension subclass:

  • Dialog boxes: dialogfragment
  • Lists: listfragment
  • Option settings: preferencefragment
  • WebView interface: webviewfragment

Note: it is not recommended to use the fragment under android.app for fragment development, but Android: support.v4.app, because the support library is constantly updated.

2. Fragment usage

There are two ways to use fragment: static loading and dynamic loading

2.1 static loading

The process of static loading is as follows:

  • XML layout file defining fragment
  • Customize the fragment class, inherit the fragment class or its subclasses, and implement the oncreate () method. In the method, load the layout file through inflator.inflate, and then return to its view
  • In the layout file corresponding to the activity that needs to load the fragmentThe name attribute of is set to the fully qualified class name, that is, the package name. Fragment
  • Finally, call setcontentview() in the activity to load the layout file

Once a static load is added, it cannot be deleted at run time

Example:

  • Define the fragment layout and create a new left_ Fragment.xml and right_ Fragment.xml file
  • Customize the Fragment class, inherit Fragment or its subclass, rewrite onCreateView (), invoke the inflater.inflate () method in the method, load the Fragment layout file, and then return the loaded view object.
public class LeftFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment, container,false);
        return view;
    }

}
public class RigthFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }
}
  • Add the fragment tag in the layout file corresponding to the activity that needs to load the fragment
  • In Activity’s onCreate () method, you call setContentView () to load the layout file.

2.2 dynamic loading fragment

The process of dynamically loading fragments is as follows:

  • Get the fragmentmanager object through getsupportfragmentmanager()
  • Get the fragmenttransaction object through FM. Begntransaction()
  • Call the add () method or the reply () method to load the fragment;
  • Finally, the commit () method is used to commit the transaction.

Simple example:

  • Like static loading, first define the layout and class of fragment, modify the main layout file, and do not specifyThe name attribute of the tag.
  • Implement fragment call
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        replaceFragment(new RigthFragment());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                replaceFragment(new AnotherRightFragment());
                break;
            default:
                break;
        }
    }

    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();   //  Start a transaction
        transaction.replace(R.id.right_layout, fragment);
        transaction.commit();
    }
}

2.3 precautions for use

  • The oncreateview() method of fragment returns the UI layout of fragment. It should be noted that the third parameter of inflate() is false, because in the internal implementation of fragment, the layout will be added to the container. If it is set to true, the addition will be repeated twice, and the following exception will be thrown:

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

  • If you want to pass in parameters when creating a fragment, you must add them through setarguments (bundle bundle). It is not recommended to add a constructor with parameters for the fragment, because adding them through setarguments () can preserve these data when the fragment is killed and restored by the system due to memory constraints

The parameters passed in can be obtained through getarguments () in onattach () of fragment. If you want to get an activity object, it is not recommended to call getactivity(), but to forcibly convert the context object into an activity object in onattach()

Example:

public class Fragment1 extends Fragment{
    private static String ARG_PARAM = "param_key";
    private String mParam;
    private Activity mActivity;
    public void onAttach(Context context) {
        mActivity = (Activity) context;
        mParam = getArguments().getString(ARG_PARAM);  // Get parameters
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_1, container, false);
        TextView view = root.findViewById(R.id.text);
        view.setText(mParam);
        return root;
    }
    public static Fragment1 newInstance(String str) {
        Fragment1 frag = new Fragment1();
        Bundle bundle = new Bundle();
        bundle.putString(ARG_PARAM, str);
        fragment.setArguments(bundle);   // Set parameters
        return fragment;
    }
}
  • In dynamic loading Fragment, the FragmentTransaction class provides methods to complete the operation of adding and deletions. After completion, the FragmentTransaction.commit () method is submitted for modification.

    • Transaction. Add(): add a fragment to the activity
    • Transaction. Remove(): remove a fragment from the activity. If the removed fragment is not added to the fallback stack, the fragment instance will be destroyed
    • Transaction. Replace(): replace the current with another fragment, which is actually a combination of remove() and add()
    • Transaction. Hide(): hides the current fragment. It is only invisible and will not be destroyed
    • Transaction. Show(): displays previously hidden fragments
    • Detach (): the view will be removed from the UI. Unlike remove (), the fragment status is still maintained by the fragmentmanager
    • Attach (): rebuild the view, attach it to the UI and display it.

The commit method must be called before Activity.onSaveInstance ().

The commit () operation is asynchronous. It is internally added to the processing queue through mmanager. Enqueueaction(). The corresponding synchronization method is commitnow(), and there will be checkstateloss() operation inside commit(). If the developer uses it improperly (for example, the commit() operation is after onsaveinstancestate()), an exception may be thrown, while the commitallowingstateloss() method will not throw an exception version of the commit() method, but try to use commit() instead of commitallowingstateloss() 。

  • Fragmentmanager has a fallback stack, which is similar to the task stack of activity. If the statement is added, the transaction will be added to the fallback stack. When the user clicks the return button, the transaction will be fallback (if the transaction is add (frag1), the fallback operation is remove (frag1)); If the statement is not added, the user will directly destroy the activity by clicking the return button.

  • Fragment common exception

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)

at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)

at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)

at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Cause: commit () is called after onSaveInstanceState ().

Since onSaveInstanceState () is called before onPause (), onStop () is called. Onrestoreinstancestate() is after onstart() and before onresume(). Therefore, the schemes to avoid this exception are:

  • Do not put fragment transactions in callbacks of asynchronous threads
  • Use commitallowingstateloss() when you have to

3. Linkage between fragment and activity

image

The complete life cycle of fragment and activity is shown in the figure above

  1. When fragment is added in oncreate() of activity
  • Activity super.oncreate execution completed
  • Fragment onAttach
  • Fragment onCreate
  • Fragment onCreateView
  • Fragment onViewCreated
  • Activity. Super. Onstart() is in execution
  • Fragment onActivityCreated
  • Fragment onViewStateRestored
  • Fragment onStart()
  • Activity super.onstart execution completed
  • Activity super.onPostCreate()
  • Activity super.onResume()
  • Activity super. Onpostresume() is in execution
  • Fragment onResume()
  • Activity super. Onposresume() completed
  • Activity onAttachedToWindow()
  • Activity onCreateOptionsMenu()
  • Fragment onCreateOptionsMenu()
  • Activity onPrepareOptionsMenu()
  • Fragment onPrepareOptionsMenu()
  • Activity onWindowFocusChanged()
  1. Pause lifecycle
  • Activity super. Onpause() is in execution
  • Fragment.onPause()
  • Activity super. Onpause() execution completed
  • Activity super. Onsaveinstancestate() is in execution
  • Fragment onSaveInstanceState()
  • Activity super. Onsaveinstancestate() completed execution
  • Activity super. Onstop() is in execution
  • Fragment onStop()
  • Activity super. Onstop() finished executing
  1. Life cycle of destruction
  • Activity super. Ondestroy() is in execution
  • Fragment onDestroyView()
  • Fragment onDestroy()
  • Fragment onDetach()
  • Activity super. Ondestroy() completed
  1. Restart life cycle
  • Activity super.onRestart()
  • Activity super. Onstart() is in execution
  • Fragment onStart()
  • Activity super. Onstart() completed
  • Activity super.onResume()
  • Activity super. Onpostresume() is in execution
  • Fragment onResume()
  • Activity super. Onposresume() completed
  • Activity onwindowfocuschanged() completed execution

3.1 fallback stack

Similarly, the Android system maintains a task stack for the activity. We can also maintain a fallback stack through the activity to save the changes of each fragment transaction. If you add the fragment task to the fallback stack, when the user clicks the back button, he will see the last saved fragment.

Once the fragment completely pops up from the back stack, the user clicks the back button again to exit the current activity

Add a fragment transaction to the fallback stack:

FragmentTransaction.addToBackStack(String)

Simple example:

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout, fragment);
    transaction.addToBackStack(null);   // Add to fallback stack
    transaction.commit();
}

Replace is a combination of remove and add, and if the transaction is not added to the fallback stack, the previous fragment instance will be destroyed. Obviously, we call transaction. Addtobackstack (null); The current transaction is added to the fallback stack, so the fragmentone instance will not be destroyed, but the view hierarchy will still be destroyed, that is, ondestoryview and oncreateview will be called

If you do not want the view redrawn, you can hide the original fragment:

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.hide(this);
    transaction.add(R.id.right_layout, fragment);
    transaction.addToBackStack(null);   // Add to fallback stack
    transaction.commit();
}

4. Fragment communicates with activity

image

The communication between fragment and activity is shown in the figure above:

  • If the activity contains references to fragments managed by itself, you can directly access the public methods of all fragments through references
  • If no fragment reference is saved in the activity, it doesn’t matter. Each fragment has a unique tag or ID. you can get any fragment instance through getfragmentmanager. Findfragmentbytag() or findfragmentbyid(), and then operate
  • In the fragment, you can get the instance of the currently bound activity through getactivity, and then operate.

remarks:

  • If you need a context in the fragment, you can use getactivity(). If the context needs to exist after the activity is destroyed, you can use getactivity. Getapplicationcontext();

Considering the reuse of fragment and reducing the coupling with activity, the fragment operation should be determined by its manager activity.

4.1 transferring data to fragment

Step flow:

  • Create a bundle packet in the activity, call setarguments() of the fragment instance, and pass the bundle packet to the fragment
  • Fragment calls getarguments () to get the bundle object, and then parses it

Simple example:

//Create a fragment object and pass the value through the bundle object (in the oncreate method)
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("key", values);
fragment.setArguments(bundle);
//(in oncreateview method in fragment class)
Bundle bundle = this.getArguments();
    if (bundle != null)
    {
        String str = bundle.getString("key");
    }
    TextView textView = new TextView(getActivity());
    Textview.settext ("enjoyment from top to bottom")// It's an elevator. Don't get me wrong

4.2 transferring data to activity

Step flow:

  • Define an internal callback interface in the fragment, and then let the activity containing the fragment implement the callback interface
  • Fragment transmits data through the callback interface

Simple example:

  • First, define an interface in the fragment (define abstract methods and what type parameters to pass)
/*Interface*/  
public interface Mylistener{
    public void thanks(String code);
}
  • The interface is defined in the fragment class
private Mylistener listener;
  • In the onattach method, force the defined interface to the activity type
@Override
    public void onAttach(Activity activity) {
        // TODO Auto-generated method stub
        listener=(Mylistener) activity;
        super.onAttach(activity);
    }
  • The activity only needs to implement the interface and override the method
@Override
    public void thanks(String code) {
        // TODO Auto-generated method stub
        Toast. Maketext (this, "received fragment message: -" + code + "--, you're welcome", toast. Length_short). Show();;
    }

In addition to interface callbacks, you can also use eventbus for interactive communication.

5. Communication between fragments

5.1 setArguments()

Example:

  • Create a new function in fragment B: newinstance() receives the passed parameters
public static Fragment2 newInstance(String text) {
        Fragment2 fragment = new Fragment2();
        Bundle args = new Bundle();
        args.putString("param", text);
        fragment.setArguments(args);
        return fragment;
    }
  • Get parameters in oncreateview of fragment B
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view =  inflater.inflate(R.layout.fragment2, container, false);
        if (getArguments() != null) {
            String mParam1 = getArguments().getString("param");
            TextView tv =  (TextView)view.findViewById(R.id.textview);
            tv.setText(mParam1);
        }
        return view;
    }
  • In Fragment A, when you call Fragment B, you get the instance through the newInstance function and pass the parameters:
public class Fragment1 extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);
        Button btn = (Button)view.findViewById(R.id.load_fragment2_btn);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(final View view) {
                Fragment2 fragment2 = fragment2.newinstance ("parameter passed from fragment1");
 
                FragmentTransaction transaction = getFragmentManager().beginTransaction();
                transaction.add(R.id.main_layout, fragment2);
                transaction.addToBackStack(null);
                transaction.commit();
            }
        });
        return view;
    }
}

5.2 fragment interaction with different containers of activity

There are three ways to solve this situation:

Method 1: operate directly in the activity

Directly find the instance of the corresponding control in the activity, and then manipulate it directly

Method 2: operate directly in fragment

Here are two questions: how to get the reference of your own control? How do I get references to other fragment page controls?

  • First, get the reference of your own control

Can be obtained in oncreateview()

public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment1, container, false);
    listView = (ListView)rootView.findViewById(R.id.list);// Get the control reference in your view. Method 1
    return rootView;
}

In oncreateview(), the view has not been created, so if you use the getview() method here, it will return null

Another method is to get the callback in onactivitycreated(), and its callback will be executed after oncreate()

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
 
    listView = (ListView) getView().findViewById(R.id.list);// Get the control reference in your view, method 2
}
  • Get other fragment page control reference methods

Get the activity resource. After the activity is created, it must be placed in the onactivitycreated() callback function

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
 
    mFragment2_ tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);// The only way to get control references in other fragments!!!
 
}

The general implementation example is as follows:

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
 
    mFragment2_ tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);// The only way to get control references in other fragments!!!
    listView = (ListView) getView().findViewById(R.id.list);// Get the control reference in your view, method 2
 
    ArrayAdapter arrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, mStrings);
    listView.setAdapter(arrayAdapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView> parent, View view, int position, long id) {
            String str = mStrings[position];
            mFragment2_tv.setText(str);
       }
    });
}

Method 3: operate in respective fragments

Method 2 operates fragment B in fragment A, which violates the idea of module separation and should be separated through activity

In the activity, you can get the fragment instance directly through fragmentmanager. Findfragmentbyid()

Example:

Set textview function in fragment2

public class Fragment2 extends Fragment {
    private TextView mTv;
    …………
    public void setText(String text) {
        mTv.setText(text);
    }
}

Define the processing method in fragment1

  • Define interfaces and variables
private titleSelectInterface mSelectInterface;
 
public interface titleSelectInterface{
    public void onTitleSelect(String title);
}
  • Interface variable assignment

The interface is used by the activity, and the interface variable is assigned a value in the activity. When the fragment is associated with the activity, it needs to be forced

public void onAttach(Activity activity) {
    super.onAttach(activity);
 
    try {
        mSelectInterface = (titleSelectInterface) activity;
    } catch (Exception e) {
        throw new ClassCastException(activity.toString() + "must implement OnArticleSelectedListener");
    }
}
  • Call interface variable
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
 
    listView = (ListView) getView().findViewById(R.id.list);// Get the control reference in your view, method 2
    ArrayAdapter arrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, mStrings);
    listView.setAdapter(arrayAdapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView> parent, View view, int position, long id) {
            String str = mStrings[position];
            mSelectInterface.onTitleSelect(str);
        }
    });
}
  • Implement interface in activity
public class MainActivity extends FragmentActivity implements Fragment1.titleSelectInterface {
 
    ……
    
    @Override
    public void onTitleSelect(String title) {
        FragmentManager manager = getSupportFragmentManager();
        Fragment2 fragment2 = (Fragment2)manager.findFragmentById(R.id.fragment2);
        fragment2.setText(title);
    }
}