This article is about the use of Fragment, most of which are relatively basic knowledge points. The reason why I write it out is that I have taken a lot of detours and met many pits when using fragment in my work. Just because there are so many details that I haven’t mastered. Here to share, but also to facilitate their own review.
1. Summary of fragments
Fragment is a controller object to which an activity can delegate tasks, usually to manage the user interface. The managed user interface can be the entire screen or a small portion of the screen.
Fragment and support libraries
Fragments that Google introduced in API11 (to meet the flexibility requirements of tablet UI design) are minimally compatible with API4. To make your app compatible with older devices, Android provides development support libraries, The fragments (android. Support. The v4. App. Fragments), FragmentActivity (android. Support. The v4. App. Fragments – Activity)
Fragment library for the operating system built-in version
Developers usually prefer to use the fragment version of the support library rather than the built-in version of the system, because Google updates the support library every year to release new features and fix bugs. If we want to reflect these benefits, we just need to upgrade the support library version of the project. The purpose of Google’s support library is to make it easier for developers to experience fragments in versions that do not support the API.
2. Hosting UI Fragments
We all know that a fragment is managed by an activity. While hosting a fragment, an activity must handle two things:
- Manage the fragment lifecycle
- Place the fragment in the layout
2.1 Fragment life cycle
Fragment life cycle | Fragment vs. Activity lifecycle comparison |
---|---|
|
|
The Fragment life cycle is very similar to the activity life cycle methods. It also has methods that can stop, pause, and run transitions. It also has methods that can override and complete tasks at critical points. Most people know that the fragment’s onAttach(), onCreat(), and onCreatView() lifecycle methods are executed during the Activity’s onCreat(). As you should know from the source code, these three methods are called in setContentView() in onCreate().
Life cycle methods are important in development because a fragment works on behalf of an activity, and its state reflects the state of the activity. Obviously, a fragment needs a corresponding life cycle method to handle the activity. The key difference between the fragment and activity lifecycle methods is that the fragment lifecycle method is invoked by the activity, not the operating system, which doesn’t care about the fragment that the activity uses to manage views.
2.2 Two ways of trusteeship
There are two ways to host an activity fragment:
- Add a Fragment to the activity layout
- Add in the activity via code
The first method is relatively simple, binding the fragment directly through Activityu’s layout, but it is not flexible enough to switch between other fragment views during the fragment life cycle, so we won’t cover it here.
The second way: although we want to add fragments to the hosted activity through code, we first need to define the layout container, that is, the location of the frgament. The layout file looks like this:
<?xml version="1.0" encoding="utf-8"? >
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>Copy the code
Create fragments and implement lifecycle methods
package com.haife.album_master.activities;
import android.os.Bundle;
import android.support.v4.app.Fragment;
/**
* A simple {@link Fragment} subclass.
*/
public class BlankFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); }}Copy the code
There are a few things to note about this code:
- Fragment life-cycle methods are public methods. Unlike activities, Activity life-cycle methods are protected methods that can be called by managed activities
- Like an activity, a fragment has a Bundle that stores and retrieves state. As with the activity’s onSaveInstance() method, we can also override this method in the fragment
- Attempts to create a fragment are not created in the fragment.oncreat () method. Although we have configured instances of the fragment in the method, the creation and configuration of the view is done in another lifecycle method, onCreatView()
package com.haife.album_master.activities;
import android.os.Bundle;
import android.support.v4.app.Fragment;
/**
* A simple {@link Fragment} subclass.
*/
public class BlankFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
textView.setText(R.string.hello_blank_fragment);
returntextView; }}Copy the code
Add fragments to FragmentManager
The FragmentManager class manages the Fragment queue and the rollback of the fragment.
graph LR
Activity-->FragmentManager
FragmentManager-->|fragmentQueue |Fragment
FragmentManager-->|Back up the stack | FragmentTransactionCopy the code
To add a fragment to an activity programmatically, we simply call the FragmentManager in the Activity, first retrieving the FragmentManager.
FragmentManager fm = getSupportFragmentManager();Copy the code
Fragment transaction
When we get the FragmentManager in our activity, we need to put the Fragment under its management. The code is as follows:
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = newBlankFragment(); fm.beginTransaction() .add(R.id.fragment_container, fragment) .commit(); }}}Copy the code
add(…) Methods are at the heart of the whole transaction. In addition to adding fragments, a fragment transaction can be used to remove, attach, separate, and replace fragments in a fragment queue. This is the key to using fragments to assemble and reassemble user interfaces at runtime. The FragmentManager manages the rollback of fragment transactions.
FragmentManager. BeginTransaction () method is to create and return FragmentTransaction examples, thus a FragmentTransaction queue can be obtained.
Now let’s go back to summarizing the code above. First, the FragmenbManager requests the Fragment instance with the resource Id. If the fragment is not empty, the FragmentManager returns it directly. Many people ask why a fragment is in a queue? You haven’t added a fragment yet, but it’s actually quite simple. As we mentioned earlier, the activity lifecycle changes when the device rotates. When the activity is destroyed, the FragmentManager of the activity stores the Fragment queue. When an activity executes the onCreat() method of its life cycle, the FragmentManager will preferentially go back to the saved Fragment queue and then rebuild to the original state, so you can see why. If the fragment is empty, add 0 directly to the fragment queue.
Abstract Activity class
As you can see, the implementation code above is almost generic, but the only downside is that when adding a fragment to the FragmentManager, the fragment instantiated can only be a BlankFragment, for the sake of application flexibility. So let’s go ahead and optimize. We can define an abstract method as the parent of the activity, and then subclasses will implement that method to return the fragment instance.
To differentiate, we create a BaseFragmentActivity
public class BaseFragmentActivity extends FragmentActivity {
protect abstrct Fragment creatFragment(a);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) { fragment = creatFragment(); fm.beginTransaction() .add(R.id.fragment_container, fragment) .commit(); }}}Copy the code
MainActivity is modified as follows
public class MainActivity extends BaseFragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected Fragment creatFragment(a){
retrun new BlankFragment(a); }}Copy the code
Now it seems that our MainActivity is not concise and clean. Using more abstract classes in actual development will greatly save your development time and improve efficiency.
3. Use Fragment argument
One of the most common things you need in development is to pass values between activities and Fragmenr. For example, if we want to jump from one fragment to a new target activity (with parameters), then the target fragment is responsible for receiving that value. The implementation code is as follows:
The target activity
public class TargetActivity extends BaseFragmentActivity {
public static final String EXTRA_UUID = "com.haife.album_master.activities.targetId"
public static Intent newIntent(Context ctx,String targetId){
Intent intent = new Intent(ctx,TargetActivity.class);
intent.putExtra(EXTRA_UUID,targetId);
returnintent; }}Copy the code
The jump in
@Override public void onClick(a){ Intent intent = TargetActivity.newInstance(getActivity(),id); startActivity(intent); }Copy the code
At this point, the parameter ID is safely stored in TargetActivity, whereas the parameter is retrieved and the Fragment class is used.
Fragment To obtain extra information
There are two ways for fragments to retrieve data in intents. One is simple and straightforward, and the other is complex and flexible, involving fragments arguement. Fragment: getActivity() : getActivity() : getActivity() : getActivity() : getActivity() : getActivity() : getActivity() I’m using it to get the value of String ID.
public class TargetFragment extends Fragment {
public void oncreat(Bundle saveInstanceState){ String id = getActivity().getIntent().getStringExtra(TargetAtivity.EXTRA_UUID); }}Copy the code
After a few lines of code, we can retrieve information from the intent of the hosted activity. However, this way our TargetFragment cannot be reused. Currently, it can only be used for TargetActivity. The fragment can store the value in a TargetFragment somewhere other than in the private space of the TargetAvcivity. This way, the fragment can retrieve the extra data it needs without relying on the intent specified in the activity’s intent. Fragment argument is the fragment argument.
fragment argument
Each Fragment has a Bundle object that contains key-value pairs. We can do this in an intent that adds “extra” to an activity, where each key corresponds to an argument. How to append arguement to a fragment? We need to call fragment.setargument (Bundle). Note that this method must be called after the Fragment is created. A common practice for Android developers is to execute a static newInstance() on a fragment while creating an instance of the fragment and a Bundle object, and then append the argument to the fragment in the Bundle. The code is as follows:
package com.haife.album_master.activities;
import android.os.Bundle;
import android.support.v4.app.Fragment;
/**
* A simple {@link Fragment} subclass.
*/
public class TargetFragment extends Fragment {
private static final String ARG_TARGET_ID = "arg_target_id";
public static BlankFragment newInstance(String id){
Bundle bundle = new Bundle();
bundle.putString(ARG_TARGET_ID,id);
BlankFragment blankFragment = new BlankFragment();
blankFragment.setArguments(bundle);
returnblankFragment; }}Copy the code
Now, you need to create the fragments of time, the activity to invoke BlankFragment. NewInstance (String) method, rather than directly to the one above to invoke a constructor, the activity can pass any parameters to the newInstance () method, The value of extra in the activity is passed here according to the actual development needs:
public class TargetActivity extends BaseFragmentActivity {
private static final String EXTRA_UUID = "com.haife.album_master.activities.targetId"
@Override
protected Fragment creatFragment(){
String id = getIntent().getStringExtra(EXTRA_UUID);
return TargetFragment.newInstance(id); }}Copy the code
The EXTRA_ID is changed to private because it is not used by any other class. Note that both an activity and a fragment need not and cannot remain independent at the same time. An activity needs to know the internal details of a fragment.
For argument
Above we have attached argument to TargetFragment. How do we get its argument?
public class TargetFragment extends Fragment {
private static final String ARG_TARGET_ID = "arg_target_id";
private static final String DEFAULT_VALUE = "default_value";
@Override
public void onCreate(Bundle saveInstanceState){ String id = getArguments().getString(ARG_TARGET_ID, DEFAULT_VALUE); }}Copy the code
So here’s the code. It’s a little bit less, but it’s pretty generic. If there is a RecycleView in the frament, how can we refresh it in the fragment when the data source changes?
public class BlankFragment extends Fragment {
@Override
public void onResume(a) {
super.onResume();
updateUI();
}
private void updateUI(a) {
if (mAdapter == null) {
mAdapter = new MyAdapter(userList);
recycle.setAdapter(mAdapter);
}else{ mAdapter.notifyDataSetChanger(); }}}Copy the code
To explain why we chose to override the onResum() method to refresh the list instead of onStart(), we are not sure if the host activity will be stopped when another Activbity comes before the BlankFragment’s host activity. If the previous activity is transparent, refreshing the list in the onStart() method is not effective, and in general, the safest way to ensure that the fragment view is refreshed is in the onResum() method.
Fragment argument is the fragment argument. Why use it? Why don’t we just create an instance variable inside the fragment? When the operating system rebuilds a Fragment, or the user leaves the application, or the system reclaims memory, or the application configuration changes, all the instance variables are no longer there. Of course, some people say that you can save it in the instance state and store it in the onSaveInstanceState(Bundle) method. That’s fine, too, if you can remember it when you look back at the code
4. Obtain the returned result using the fragment
Fragments and activity, there are fragments. StartActivityForResult (…). And fragments. OnActivityResult (…). “Is similar to the Activity method of the same name. However, the handling of returning results from a fragment is a little different. A fragment can receive the results from an activity and cannot hold them by itself. Although Fragment has its own startActivityForResult(…) And your onActivityResult (…). But there is no setResult(…) Method, instead, we can make the activity return the result value. The specific code is as follows:
public class BlankFragment extends Fragment {...public void returnResult(a){
getActivity().setResult(Activity.Result_OK,null); }...Copy the code
5. At the end
Last but not least, it’s important to use fragments properly when designing apps, but we shouldn’t abuse them. The purpose of Google’s fragment design is to encapsulate reusable components. Components here refer to the components of the screen. It is good to use 2-3 fragments at most on a single screen. In fact, WHEN choosing between activities or UI fragments to manage the user interface, I think there is no need to think too much. Forget about activities when you can implement them using fragments.