Preface:

Android Basics

Android Skill Tree – Fragment overview

Android Skill Tree – Animation summary

Android Skill Tree – View summary

Android Skill Tree – Activity summary

Android Skill Tree – View event system summary

Android Skill Tree – Summary of Android storage paths and I/O operations

Android Skill Tree – Multi-process related summary

Android skill Tree – Drawable summary

Android Skill Tree – Screen Adaptation summary


I haven’t written an article for a long time, no other reason than laziness.

In the recent APP development, the method of single Activity + multiple Fragment is used, which is different from the method of Activity in the previous basic interface. Therefore, I use a lot of Fragment. I think I have written a lot of related basic knowledge before, but I have never written Fragment. So I decided to make up a basic summary of fragments.

As usual, head first:

Let’s look at the basics of fragments in the order of the brain diagram.


Body:

1. The Fragment to add

We know that a Fragment is a “Fragment” added to an Activity. If I were to ask you now that your Activity wanted to display a Button, what would you do?

1. Add the configuration file in layout. XML<Button/>

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        />
</LinearLayout>
Copy the code

2. Add dynamically in code, like we added to a LinearLayout:

Button button = new Button(mActivity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
button.setLayoutParams(params);
container.addView(button);
Copy the code

So it’s easy to think of a Fragment as a simple View (but more like a “child Activity”) and add it the same way.

1. Add to layout.xml:

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
Copy the code

2. Add:

Fragment one = new FragmentOne(); / / custom fragments classes / / object must first obtain FragmentManager FragmentManager FragmentManager = getSupportFragmentManager (); / / open a FragmentTransaction transaction FragmentTransaction transaction = fragmentManager. BeginTransaction (); transaction.add(R.id.framelayout_view,one).commit();Copy the code

The ViewGourp of the ID added to R.I.D. ramelayout_view can be
or another .


2. Basic Fragment operations

You need to get the FragmentTransaction and FragmentManager when you see our dynamic code addition above.

2.1 FragmentManager related

1. GetFragmentManager () :

Gets the Fragment parent’s manager, but this method is now flagged as not recommended in the Activity.

 /**
     * Return the FragmentManager for interacting with fragments associated
     * with this activity.
     *
     * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
     */
    @Deprecated
    public FragmentManager getFragmentManager() {
        return mFragments.getFragmentManager();
    }
Copy the code

2. getSupportFragmentManager():

This method in the V4 package has the same effect as the previous method, but is recommended by Android (after all, it is compatible with all versions of Android).

3. getChildFragmentManager():

Can you add a Fragment to a child Activity? The answer is yes. The Fragment manager inside the Fragment needs to be retrieved using getChildFragmentManager().

2.2 FragmentTransaction related

We can see that there are various methods of adding and removing.

1. Attach /detach method:

  • Detach (Fragment Fragment) : Detaches the UI view of the specified Fragment
  • Attach (Fragment Fragment) : Reattach a Fragment (after the Fragment detach has been executed)
  1. When a Fragment is detach, the Fragment life cycle terminates after onDestroyView is executed. This means that the Fragment instance is not destroyed, but the UI is removed.
  2. After the Fragment is detach, attach will cause the Fragment to start onCreateView and continue onResume.
  3. Attach cannot be used alone like Add. If it is used alone, an exception will be thrown. The purpose of the detach method is to interface restore the Fragment after detach.

2. The add/remove methods:

I think these two are the most used, Add () and remove() add and remove fragments. Remove () is a bit more thorough than detach(). If it is not added to the rollback stack, remove() will continue the fragment lifecycle until onDetach(); If a rollback is added, only onDestoryView() is executed, and the Fragment object is still there.

Adding a fragment to an Activity stack is similar to adding the same ID to an Activity stack. When multiple activities are started, they are stacked on top of each other.

3. The replace method:

Replace = remove + add replace = remove + add replace = remove + add

Therefore, if you feel that there are too many fragments, which affects performance, you can use replace to switch each interface, so that there is only one Fragment at present. However, after each switch, the Fragment will be rebuilt, so if the interface has network request related, You will find that the interface again requests the network interface, which is unnecessary.

4. Hide/show method:

Literally, let a Fragment hide, let a Fragment show. You can understand that view. GONE and view.visible are set for Button. When there are multiple fragments or TAB switching, select a button and make the corresponding Fragment display according to the corresponding Fragment, while hiding the other fragments.

5. com MIT/commitAllowingStateLoss:

I suspect many people recognize this commitAllowingStateLoss mostly because their code has a flashback exception:

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

The system calls onSaveInstanceState() to save the state, data, etc., of your current Activity when you leave the Activity, until you return to the Activity (before onResume()) and execute the Fragment transaction. There are many tutorials on the web that tell you to commit your Activity with commitAllowingStateLoss(). It won’t throw an error, but if you submit your Activity after it has saved its state, it will crash unexpectedly at the Ativity. When restoring data, updates to the fragment submitted after the Activity saved the state will not be restored, causing the state to be lost.

Bonus: the 1.com MIT () method does not immediately perform the action contained in the transaction, but instead adds it to the UI thread queue. If you want to immediate execution, can immediately after a commit call FragmentManager executePendingTransactions () method. 2. The commit () method must be called before the state storage, otherwise it will throw an exception, if feel lost it doesn’t matter, You can call commitAllowingStateLoss(). But this method is not recommended unless you absolutely have to, as it can cover up a lot of mistakes.

6. AddToBackStack:

We can see that FragmentTransaction has a method that adds a rollback to the stack, but not an exit method: popBackStack. This is because this method is in the FragmentManager.

That is, the picture below:

AddToBackStack and popBackStack should be at the same level as add and remove. PopBackStack should also be a method under FragmentTransaction.

So popBackStack and FragmentTransaction are on the same level as each other, so popBackStack is actually FragmentTransaction. So addToBackStack adds our FragmentTransaction transactions (add,remove,replace, etc.) to the back stack (!!). Remember that the fragment is not added to the rollback stack, and popBackStack is the transaction that operates on the rollback stack.

Of course, the specific source code process analysis, detail is a lot of words, can be another special to write an article, so direct reference online others have written good articles:

Fragment stack management

1. Add the fragment to the rollback stack: Execute onDestoryView on the removed fragment without executing onDestory. The fragment instance is still there. Unadded fragment: Execute onDestoryView and onDestory to remove removed fragments completely


3. Obtain the Context in the Fragment

We can use the getActivity() and getContext() methods directly inside the Fragment code.

But sometimes the fetch is empty, so generally we use:

Class xxxFragment extends Fragment {

    private Context mContext;
    
    //'After the higher version, it's all called back to this method'@Override public void onAttach(Context context) { super.onAttach(context); mContext = context; } / /'This method will be called back when the API is lower than version 23.'@Override public void onAttach(Activity activity) { super.onAttach(activity); mContext = activity; }}Copy the code

4. The ViewPager fragments

ViewPager with fragments of time, the main use FragmentPagerAdapter and FragmentStatePagerAdapter these two Adapter. In fact, it is very simple to use (the general most simple writing) :

public class FragmentAdapter extends FragmentPagerAdapter{ private ArrayList<Fragment> list; Public Fragment_pager(FragmentManager FM,ArrayList< fragment > list) {super(FM); this.list=list; } // Set the fragment @override public fragment getItem(int position) {// TODO auto-generated method stubreturnlist.get(position); } // Set the number of fragment @override public intgetCount() {
        // TODO Auto-generated method stub
        returnlist.size(); }}Copy the code

Then the ViewPager. SetAdapter (XXXX);

But everyone will wonder why there are two Adapter: FragmentPagerAdapter and FragmentStatePagerAdapter, them we can see the difference between a specific source:

FragmentPagerAdapter source code:

public abstract class FragmentPagerAdapter extends PagerAdapter {


    //'Initializes create Item:'
    @NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        long itemId = this.getItemId(position);
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
        if(fragment ! = null) { //'behind using fragments by FragmentTransaction. Add the attach way,'
            //'I just redrew the UI, and the Fragment object is still there. '
            this.mCurTransaction.attach(fragment);
        } else {
        
            //'We know we just returned fragment using the getItem(Position) method'
            //'We can see that the first use of the fragment was added via FragmentTransaction. Add'
            fragment = this.getItem(position);
            this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
        }

        if(fragment ! = this.mCurrentPrimaryItem) { fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        returnfragment; } / /'destruction of the item:
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        if(this.mCurTransaction == null) { this.mCurTransaction = this.mFragmentManager.beginTransaction(); } / /'We can see that FragmentTransaction is simply detach the fragment. The view is gone, but the fragment object is still there.'this.mCurTransaction.detach((Fragment)object); }}Copy the code

We can see that fragments are not actually destroyed. The FragmentPageAdapter is good for fixed, small fragments, such as when used with a TabLayout.

FragmentStatePagerAdapter source code:

public abstract class FragmentStatePagerAdapter extends PagerAdapter {


    
    @NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        Fragment fragment;
        if (this.mFragments.size() > position) {
            fragment = (Fragment)this.mFragments.get(position);
            if(fragment ! = null) {returnfragment; }}if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        fragment = this.getItem(position);
        if (this.mSavedState.size() > position) {
            SavedState fss = (SavedState)this.mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }

        while(this.mFragments.size() <= position) {
            this.mFragments.add((Object)null);
        }

        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        this.mFragments.set(position, fragment);
        
        //'We can see that the fragments are added'
        this.mCurTransaction.add(container.getId(), fragment);
        return fragment;
    }
    
    
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        while(this.mSavedState.size() <= position) {
            this.mSavedState.add((Object)null);
        }

        this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);
        this.mFragments.set(position, (Object)null);
        
        
        //'You can see it's all removed by remove.'this.mCurTransaction.remove(fragment); }}Copy the code

So we know the truth of FragmentStatePagerAdapter take fragments objects are destroyed, so if fragments quantity many, use this will be better, because fragments there is too much, cause great influence to application performance, Remove the fragment.


5. Fragment without UI:

5.1 Using the Fragment to Preserve the Object to Be Restored

Call the setRetainInstance(true) method to preserve the fragment as follows:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true); . }Copy the code

For example, rotating the screen will not destroy existing fragments with the activity (but will destroy the fragment view); Instead, it persists (as long as the process doesn’t die) and is passed on to the new Activity as needed.

So let’s say that some objects can stay in the fragment, and when the Activity resumes, other objects can be retrieved from the fragment.

Take a look at some of the other authors:

How the Fragment calls setRetainInstance

5.2 Similar to RxPermission is used to handle callbacks

RxPermission has a Fragment for issuing permission callbacks. What does this mean?

We know native request permissions:

Int requestCode = 1; requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},requestCode); / / permission to processing result callback @ Override public void onRequestPermissionsResult (int requestCode, @ NonNull String [] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); }Copy the code

Do you think it is very troublesome to see the callback method?? And it’s not aesthetically pleasing.

RxPermission applies for permissions like this:

RxPermissions rxPermissions = new RxPermissions(this); RxPermissions. RequestEach (/ / request permissions Manifest. Permission. CAMERA, Manifest.permission.READ_PHONE_STATE) .subscribe(new Consumer<Permission>() { @Override public void Accept (@ IO. Reactivex. Annotations. NonNull Permission Permission) throws the Exception {/ / authority notification callback}});Copy the code

It just feels like one step. It’s great. But RxPermission only native permission to apply for the encapsulation of the system, the system originally callback function: onRequestPermissionsResult where to go???????

public class RxPermissionsFragment extends Fragment { ....... . . //'Request permission'@TargetApi(23) void requestPermissions(@NonNull String[] permissions) { this.requestPermissions(permissions, 42); } / /'Result callback after permission application'
    @TargetApi(23)
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 42) {
            boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];

            for(int i = 0; i < permissions.length; ++i) { shouldShowRequestPermissionRationale[i] = this.shouldShowRequestPermissionRationale(permissions[i]); } this.onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale); / /}}'Specific handling method after callback'
    void onRequestPermissionsResult(String[] permissions, int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
        int i = 0;

        for(int size = permissions.length; i < size; ++i) {
            this.log("onRequestPermissionsResult " + permissions[i]);
            PublishSubject<Permission> subject = (PublishSubject)this.mSubjects.get(permissions[i]);
            if (subject == null) {
                Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
                return;
            }

            this.mSubjects.remove(permissions[i]);
            boolean granted = grantResults[i] == 0;
            
            //'Subject actively calls onNext method to send results'subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i])); subject.onComplete(); }}... . . }Copy the code

We can see inside the fragment that it has already copied the native methods for requesting permissions and the native methods for notifying permissions callback. You can then send a notification at the result via Subject.

Here I will not detail the entire RxPermission source code, I have written related articles before, we can look at the specific:

Project requirements discussion – dynamic permission application analysis and related third-party library source code analysis

Project requirements discussion-walk you through writing RxPermission


6. Constructors and data passing

6.1 Constructors pass data

A fragment is an ordinary object that can be passed with a new method. We can use the constructor to define the parameter value and assign the value directly to it. The answer is yes, but not recommended.

public class FragmentOne extends Fragment {
    
    //'In other places direct FragmentOne one = new FragmentOne(" Frog to fly"); Pass the value '
    //'But we don't recommend that.'
    public FragmentOne(String value) {

    }
    
    
    //'Instead, pass it through the bundle. Fragment.setArguments(bundle) assign values to it.'
    public static FragmentOne newInstance(Bundle args) {
        FragmentOne fragment = new FragmentOne();
        if(args ! = null){ fragment.setArguments(args); }returnfragment; }}Copy the code

The reason: We can see that when the Activity is recreated, it will rebuild the Fragment it manages, and all the field values of the original Fragment will be lost (because the Fragment will call its own constructor with no arguments when it switches screens). But bundles set by the fragment.setarguments method will stay, so the data can be restored again, so try to pass the parameters as fragment.setarguments (Bundle Bundle)

6.2 Other data transmission methods

Activity and Fragment data transfer:

Fragment And Fragment data transfer

Let’s focus on setTargetFragment, because a lot of people don’t know that.

Our goal: FragmentA starts FragmentB, and then FragmentB does something and returns the result to FragmentA

FragmentB.setTargetFragment(FragmentA); Then in B: getTargetFragment().onActivityResult(getTargetRequestCode(), resultOK, I); Then in FragmentA:  @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }Copy the code

7. Fragment Rebuilds and restores data

Recommend the following article:

Best practices for saving/Restoring Activity and Fragment states

To quote:

To keep your code clean and extensible, it’s best to separate the Fragment and view states. If there are any properties that belong to the View, save and restore them inside the View. If there are any attributes that belong to the Fragment, save and restore them inside the Fragment.


8. Common monitoring Fragment display methods

This is more basic, I won’t go into details.


9. The listening Fragment changes

Back stack status change monitor:

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {}});Copy the code

Register fragment’s life listener:

List<Fragment> fragmentList = new ArrayList<>();

getActivity().getSupportFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentViewCreated(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull View v, @Nullable Bundle savedInstanceState) { super.onFragmentViewCreated(fm, f, v, savedInstanceState); fragmentList.add(f); } @Override public void onFragmentViewDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) { super.onFragmentViewDestroyed(fm, f); fragmentList.remove(f); }},false);

//'Here we notice the false in the last code, which means: no shards in the recursive shard, shards in the shard stack.'

Copy the code

10. DialogFragment:

We know that the Dialog class is rarely used these days. Instead, we use DialogFragment, which is essentially a Fragment.

In fact, I would like to write more about this, but I guess this basic Android development has been used, so I will directly use the basic introduction article of other authors online:

Android will know will – DialogFragment use summary

At the same time, the specific custom DialogFragment I have written before:

Project requirements discussion – implementation and analysis of imitation ios bottom pop-up box


Conclusion:

Haven’t written an article for a long time. A look incredibly fast half a year…… Behind prepare slowly fill up own blog. You are welcome to point out any mistakes