| guide language fragments as Android is the most basic, the most important one of the basic concept of, in the development are often deal with him. This article starts with the reasons for the existence of fragments and introduces all aspects related to them. Including the basic definition and use of fragments, internal implementation of stack rollback, Fragment communication, DialogFragment, use of ViewPager+Fragment, nested Fragment, lazy loading, etc.

FragmentDemo source code can be found below address: https://github.com/xiazdong/FragmentDemo

The basic concept

Fragment (Fragment for short) is proposed by Android 3.0 (API 11). In order to be compatible with lower versions, support-V4 library also develops a set of Fragment API, which is least compatible with Android 1.6.

In the past, the support-V4 library was a JAR package. Since version 24.2.0, the support-V4 library has been modularized into multiple JARS, including: Support-fragment, support-UI, support-media-compat, etc. This is done to reduce the size of APK packages. You can import whichever module you need.

If you want to introduce the whole support – v4 library, compile ‘com. Android. Support: support – v4:24.2.1’, if just want to introduce support – fragment library, The com. Android. Support: support – fragments: 24.2.1.

Because the support libraries are constantly updated, so it is recommended to use the android support library, support. The v4. App. Fragments, and the android system at don’t use. The app. The fragments. To use fragments from the Support library, an Activity must inherit FragmentActivity (AppCompatActivity is a subclass of FragmentActivity).

Fragment is officially defined as:

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.

According to the above definition:

  • Fragments depend on activities and cannot exist independently.

  • There can be multiple fragments in an Activity.

  • A Fragment can be reused by multiple activities.

  • Fragments have their own life cycle and can receive input events.

  • We can dynamically add or remove fragments while the Activity is running.

    Android 3.0 system is only for tablets, and closed source, at that time for mobile phones and tablets are two sets of source code, later Android 4.0 integrated mobile phone and tablet source code, so it is difficult to see Android 3.0 system on the market.

Fragment has the following advantages:

  • Modularity: Instead of writing all code in an Activity, we write code in separate fragments.

  • Reusability: Multiple activities can reuse one Fragment.

  • Adaptable (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.

The Fragment core classes are:

  • Fragment: The base class of a Fragment. Any created Fragment needs to inherit from this class.

  • FragmentManager: Manages and maintains fragments. It is an abstract class, and the concrete implementation class is FragmentManagerImpl.

  • FragmentTransaction: Operations such as adding or deleting fragments are performed in transaction mode. It is an abstract class, and the concrete implementation class is BackStackRecord. The support-fragment library is compatible with Android 1.6, caused by the ability to Nested fragments inside Android 4.2. GetChildFragmentManager () gets the FragmentManager that manages the child Fragment. In the child Fragment, getParentFragment() gets the parent Fragment.

The basic use

Here is the most basic way to use fragments. First, create a class that inherits fragments called Fragment1:

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
        returnfragment; }}Copy the code

The most common is onCreateView(), which returns the UI layout of the Fragment. Note that the third argument to the inflate() is false, because in the internal Fragment implementation, The layout is added to the container, and if set to true, the addition is repeated twice, and the following exception is thrown:

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

If you’re passing in parameters when you create a Fragment, you have to add them through setArguments(Bundle Bundle). It’s not recommended to add constructors with parameters to the Fragment, because adding them through setArguments(), This data can be retained if the Fragment is re-instantiated by the system due to memory constraints. Here are the official recommendations:

It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated.Copy the code

We can get the parameters passed in through getArguments() in the Fragment onAttach() and use them later. To get an Activity object, instead of calling getActivity(), it is recommended to force the Context object into an Activity object in onAttach().

After creating a Fragment, the next step is to add the Fragment to the Activity. There are two ways to add a Fragment to an Activity:

  • Static add: Pass in XML

    The disadvantage is that once added, it cannot be removed at run time.

  • Dynamic add: Add at run time. This method is flexible and therefore recommended.

A Fragment can be added to XML, but it is just a syntactic sugar. A Fragment is not a View, but at the same level as an Activity.

Only dynamic addition is shown here. First, your Activity needs a container to store your Fragment, usually FrameLayout. So add FrameLayout to your Activity’s layout file:

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>Copy the code

Then in onCreate(), add the Fragment to the Activity with the following code.

if (bundle == null) {
    getSupportFragmentManager().beginTransaction()
        .add(R.id.container, Fragment1.newInstance("hello world"), "f1")        //.addToBackStack("fname")
        .commit();
}Copy the code

A few things to note here:

  • Because we use the support library fragments, so need to use getSupportFragmentManager FragmentManager ().

  • Add () is one of many operations on a Fragment, including remove(), replace(), etc. The first argument is the id of the root container (FrameLayout ID, “@id/container”). The second argument is the Fragment object. The third parameter is the fragment’s tag name, The benefits of the specified tag is the follow-up we can Fragment1 frag = getSupportFragmentManager () findFragmentByTag (” f1 “) from FragmentManager finds the fragments.

  • In a single transaction, you can do multiple operations, such as add().remove().replace() at the same time.

  • The commit() operation is asynchronous and internally queued for processing via mmanager.enqueueAction (). CommitNow () : checkStateLoss() : checkStateLoss() : checkStateLoss() : checkStateLoss() : checkStateLoss() : checkStateLoss() : checkStateLoss() The commitAllowingStateLoss() method is a commit() method that doesn’t throw the exception version, but try to use commit() instead of commitAllowingStateLoss().

  • AddToBackStack (” fName “) is optional. FragmentManager has a BackStack, similar to an Activity’s task stack. If this statement is added, the transaction is added to the BackStack. When the user clicks the back button, the transaction is rolled back. The rollback is remove(frag1)); If this statement is not added, clicking the back button destroys the Activity directly.

  • A common problem with fragments is that the Fragment is overlapped by the system. This is because the Fragment is added to the activity again after being killed by the system and reinitialized. Therefore, we can determine whether the Fragment is killed by the system and reinitialized by adding an if statement to the periphery.

Fragment has a 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)Copy the code

This exception occurs because commit() is called after onSaveInstanceState(). First, onSaveInstanceState() is called after onPause() and before onStop(). OnRestoreInstanceState () follows onStart() and precedes onResume().

Therefore, the solutions to avoid this exception are:

  • Do not place a Fragment transaction in an asynchronous thread’s callback, such as onPostExecute() in AsyncTask, so onPostExecute() may be executed after onSaveInstanceState().

  • Use commitAllowingStateLoss() if you have to.

The life cycle

Fragment’s life cycle is similar to that of an Activity, but more complex than that of an Activity. The basic life cycle methods are shown below:

The explanation is as follows:

  • OnAttach () : Called when Fragment is associated with Activity. You can get Activity references through this method, and you can get parameters through getArguments().

  • OnCreate () : called when the Fragment is created.

  • OnCreateView () : Creates the Fragment layout.

  • 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 visible but not interactive.

  • OnStop () : called when Fragment is not visible.

  • OnDestroyView () : Called when the Fragment’s UI is removed from the view structure.

  • OnDestroy () : called when the Fragment is destroyed.

  • OnDetach () : called when the Fragment and Activity are unassociated.

Of the above methods, onCreateView() is the only one that does not need to write super when overridden.

Since fragments depend on activities, it is necessary to explain the life cycle of the Fragment together with the Activity’s life cycle methods, that is, the relationship and order between each Fragment life cycle method and each Activity life cycle method, as shown in the figure below:

Let’s use an example here to understand the Fragment lifecycle method. There are two fragments: F1 and F2. F1 joins the Activity during initialization. Click the button in F1 to call REPLACE and replace with F2.

When F1 is added to the Activity onCreate(), the log is as follows:

BasicActivity: [onCreate] BEGIN
BasicActivity: [onCreate] END
BasicActivity: [onStart] BEGIN
Fragment1: [onAttach] BEGIN 
Fragment1: [onAttach] END
BasicActivity: [onAttachFragment] BEGIN
BasicActivity: [onAttachFragment] END
Fragment1: [onCreate] BEGIN
Fragment1: [onCreate] END
Fragment1: [onCreateView]
Fragment1: [onViewCreated] BEGIN
Fragment1: [onViewCreated] END
Fragment1: [onActivityCreated] BEGIN
Fragment1: [onActivityCreated] END
Fragment1: [onStart] BEGIN
Fragment1: [onStart] END
BasicActivity: [onStart] END
BasicActivity: [onPostCreate] BEGIN
BasicActivity: [onPostCreate] END
BasicActivity: [onResume] BEGIN
BasicActivity: [onResume] END
BasicActivity: [onPostResume] BEGIN
Fragment1: [onResume] BEGIN
Fragment1: [onResume] END
BasicActivity: [onPostResume] END
BasicActivity: [onAttachedToWindow] BEGIN
BasicActivity: [onAttachedToWindow] ENDCopy the code

As can be seen:

  • Fragment onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart() are called in the Activity’s onStart().

  • Fragment’s onResume() is called after the Activity’s onResume().

The next two cases are addToBackStack() and addToBackStack().

1. When you click F1 and call replace() to F2 without addToBackStack(), the log is as follows:

Fragment2: [onAttach] BEGIN
Fragment2: [onAttach] END
BasicActivity: [onAttachFragment] BEGIN
BasicActivity: [onAttachFragment] END
Fragment2: [onCreate] BEGIN
Fragment2: [onCreate] END
Fragment1: [onPause] BEGIN
Fragment1: [onPause] END
Fragment1: [onStop] BEGIN
Fragment1: [onStop] END
Fragment1: [onDestroyView] BEGIN
Fragment1: [onDestroyView] END
Fragment1: [onDestroy] BEGIN
Fragment1: [onDestroy] END
Fragment1: [onDetach] BEGIN
Fragment1: [onDetach] END
Fragment2: [onCreateView]
Fragment2: [onViewCreated] BEGIN
Fragment2: [onViewCreated] END
Fragment2: [onActivityCreated] BEGIN
Fragment2: [onActivityCreated] END
Fragment2: [onStart] BEGIN
Fragment2: [onStart] END
Fragment2: [onResume] BEGIN
Fragment2: [onResume] ENDCopy the code

As you can see, F1 ends up calling onDestroy() and onDetach().

2. When you click F1 button, call replace() to replace F2 and add addToBackStack(), the log is as follows:

Fragment2: [onAttach] BEGIN
Fragment2: [onAttach] END
BasicActivity: [onAttachFragment] BEGIN
BasicActivity: [onAttachFragment] END
Fragment2: [onCreate] BEGIN
Fragment2: [onCreate] END
Fragment1: [onPause] BEGIN
Fragment1: [onPause] END
Fragment1: [onStop] BEGIN
Fragment1: [onStop] END
Fragment1: [onDestroyView] BEGIN
Fragment1: [onDestroyView] END
Fragment2: [onCreateView]
Fragment2: [onViewCreated] BEGIN
Fragment2: [onViewCreated] END
Fragment2: [onActivityCreated] BEGIN
Fragment2: [onActivityCreated] END
Fragment2: [onStart] BEGIN
Fragment2: [onStart] END
Fragment2: [onResume] BEGIN
Fragment2: [onResume] ENDCopy the code

As you can see, when F1 is replaced, only onDestroyView() ends up calling onDestroy() and onDetach(). F1 calls onCreateView()->onStart()->onResume(), so adding addToBackStack() to a Fragment transaction affects the Fragment lifecycle.

There are some basic methods for FragmentTransaction. Here’s how the Fragment lifecycle changes when these methods are called:

  • The add () : onAttach () – >… – > onResume ().

  • Remove () : onPause () – >… – > onDetach ().

  • Replace (): equivalent to the old Fragment calling remove() and the new Fragment calling add().

  • Show (): Do not call any lifecycle methods. This method is called only if the Fragment has been added to the container, and only setVisibility of the Fragment UI is true.

  • Hide (): Does not call any lifecycle methods. This method is called only if the Fragment has been added to the container, but only if the setVisibility of the Fragment UI is false.

  • Detach () : onPause () – > onStop () – > onDestroyView (). The UI is removed from the layout but is still managed by the FragmentManager.

  • The attach (), onCreateView () – > onStart () – > onResume ().

Fragment implementation principle and Back Stack

We know that the Activity has a task stack. The user adds the Activity to the stack through startActivity and clicks the back button to remove the Activity from the stack. Fragments have a similar Stack, called the Back Stack, which is managed by the FragmentManager. By default, Fragment transactions are not added to the rollback stack. If you want to add a Fragment transaction to the rollback stack, add addToBackStack(“”). If the Activity is not added to the rollback stack, clicking the back button will push the Activity directly off the stack. If rollback is added, the user clicks the back button to roll back the Fragment transaction.

We’ll explain how the Back Stack works by looking at the most common fragments:

getSupportFragmentManager().beginTransaction()
    .add(R.id.container, f1, "f1")
    .addToBackStack("")
    .commit();Copy the code

Add the Fragment to your Activity. Add the Fragment to your Activity. Create a BackStackRecord object that records the entire operation trajectory of the transaction (just one add and a rollback), and then commit the object to the FragmentManager’s execution queue for execution.

The BackStackRecord class is defined as follows

class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry.Runnable {}Copy the code

As you can see from the definition, BackStackRecord has three meanings:

  • Inherits FragmentTransaction, which is a transaction, and holds the entire operation trajectory of the entire transaction.

  • BackStackEntry is implemented as an element that can be rolled back to the stack precisely because this class has the entire transaction’s action track and therefore can be rolled back to the entire transaction when popBackStack() is used.

  • Inherits Runnable, which is put into the FragmentManager execution queue, waiting to be executed.

. Look at the first layer of meaning, getSupportFragmentManager beginTransaction () returns is BackStackRecord objects, the code is as follows:

public FragmentTransaction beginTransaction(a) {    return new BackStackRecord(this);
}Copy the code

The BackStackRecord class contains the entire operation trajectory of a transaction, in the form of a linked list. The linked list element is the Op class, representing one of the operations, defined as follows:

static final class Op {
    Op next; // The next node in the list
    Op prev; // The first node in the list
    int cmd;  // The operations are add or remove or replace or hide or show, etc
    Fragment fragment; // Which Fragment object to operate on}Copy the code

Let’s take a look at how these classes are used in a specific scenario, such as when our transaction does add. Add function definition:

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);    return this;
}Copy the code

The doAddOp() method creates an Op object and adds it to the list as follows:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    fragment.mTag = tag;  // Set the fragment tag
    fragment.mContainerId = fragment.mFragmentId = containerViewId;  // Set the fragment container ID
    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}Copy the code

AddOp () adds the created Op object to the linked list as follows:

void addOp(Op op) {    if (mHead == null) {
        mHead = mTail = op;
    } else {
        op.prev = mTail;
        mTail.next = op;
        mTail = op;
    }
    mNumOp++;
}Copy the code

AddToBackStack (” “) marks the mAddToBackStack variable true, which is used in commit(). Commit () : commitInternal() : commitInternal() : commitInternal() : commitInternal() : commitInternal()

int commitInternal(boolean allowStateLoss) {
    mCommitted = true;    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss); // Add the transaction to the queue
    return mIndex;
}Copy the code

If mAddToBackStack is true, allocBackStackIndex(this) is called to add the transaction to the fallback stack. The variable ArrayList mBackStackIndices of the FragmentManager class; It’s a rollback. The implementation is as follows:

public int allocBackStackIndex(BackStackRecord bse) {    if (mBackStackIndices == null) {
        mBackStackIndices = new ArrayList<BackStackRecord>();
    }    int index = mBackStackIndices.size();
    mBackStackIndices.add(bse);        return index;
}Copy the code

On commitInternal(), mmanager.enqueueAction (this, allowStateLoss); Is to add BackStackRecord to the queue, defined as follows:

public void enqueueAction(Runnable action, boolean allowStateLoss) {    if (mPendingActions == null) {
        mPendingActions = new ArrayList<Runnable>();
    }
    mPendingActions.add(action);    if (mPendingActions.size() == 1) {
        mHost.getHandler().removeCallbacks(mExecCommit);
        mHost.getHandler().post(mExecCommit); // Call execPendingActions() to execute transactions for the queue to be executed}}Copy the code

Mhost. getHandler() is the Handler for the main thread, so Runnable is executed on the main thread. Inside the mExecCommit, execPendingActions() is called. That is, execute all backlog unexecuted transactions in mPendingActions. How will transactions in the execution queue be executed? Call BackStackRecord’s run() method, which executes the Fragment lifecycle function and adds views to the Container.

AddToBackStack () corresponds to popBackStack(), which has the following variants:

  • PopBackStack () : pops the top of the rollback stack and rolls back the transaction.

  • PopBackStack (String name, int flag) : Name is an argument to addToBackStack(String name), which is used to find the specific element to unstack. Flag can be 0 or fragmentManager.pop_back_stack_inclusive. 0 means that only all elements above this element are popped, and POP_BACK_STACK_INCLUSIVE means that all elements above this element and above are popped. The pop-ups here contain all elements that rollback these transactions.

  • PopBackStack () is executed asynchronously, which is the MessageQueue thrown to the main thread, while popBackStackImmediate() is the synchronous version.

Let’s take a look at Demo1 to get a clearer picture of the use of the rollback stack. Functions are as follows: there are three fragments: F1, F2 and F3. When F1 is initialized, join the Activity. Click the button in F1 to jump to F2, click the button in F2 to jump to F3, and click the button in F3 to go back to F1.

In the Activity onCreate(), add F1 to the Activity:

getSupportFragmentManager().beginTransaction()
    .add(R.id.container, f1, "f1")
    .addToBackStack(Fragment1.class.getSimpleName())
    .commit();Copy the code

F1 button onClick() reads as follows:

getFragmentManager().beginTransaction()
    .replace(R.id.container, f2, "f2")
    .addToBackStack(Fragment2.class.getSimpleName())
    .commit();Copy the code

F2 button onClick() is as follows:

getFragmentManager().beginTransaction()
    .replace(R.id.container, f3, "f3")
    .addToBackStack(Fragment3.class.getSimpleName())
    .commit();Copy the code

The F3 button onClick() is as follows:

getFragmentManager().popBackStack(Fragment2.class.getSimpleName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);Copy the code

This completes the jump logic of the entire interface.

You added a point getSupportFragmentManager (.) findFragmentByTag () method is often used, he is FragmentManager method, FragmentManager is an abstract class, The FragmentManagerImpl is an implementation class that inherits the FragmentManager. Its internal implementation is:

class FragmentManagerImpl extends FragmentManager {
    ArrayList<Fragment> mActive;
    ArrayList<Fragment> mAdded;    public Fragment findFragmentByTag(String tag) {            if(mAdded ! =null&& tag ! =null) {                for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);                if(f ! =null && tag.equals(f.mTag)) {                        returnf; }}}if(mActive ! =null&& tag ! =null) {               for (int i=mActive.size()-1; i>=0; i--) {
                    Fragment f = mActive.get(i);                    if(f ! =null && tag.equals(f.mTag)) {                          returnf; }}}return null; }}Copy the code

As can be seen from the above, check whether the Fragment exists in mAdded first. If not, check whether the Fragment exists in mActive. MAdded is a collection of fragments that have been added to an Activity. MActive contains not only mAdded but also fragments that are not in the Activity but are being pulled back from the stack.

Fragments of communication

Fragment passes data to the Activity

First, define the interface in the Fragment and let the Activity implement it:

public interface OnFragmentInteractionListener {    void onItemClick(String str);  // Pass STR from Fragment to Activity}Copy the code

In fragments onAttach (), the parameter Context into OnFragmentInteractionListener objects:

public void onAttach(Context context) {    super.onAttach(context);        if (context instanceof OnFragmentInteractionListener) {
        mListener = (OnFragmentInteractionListener) context;
    } else {                throw new RuntimeException(context.toString()
                + " must implement OnFragmentInteractionListener"); }}Copy the code

Call mListener.onItemClick(” Hello “) on the appropriate Fragment to pass “hello” from the Fragment to the Activity.

FABridge

Since it is difficult to pass data from a Fragment to an Activity through an interface, you need to define an interface in the Fragment and let the Activity implement that interface. The FABridge (https://github.com/hongyangAndroid/FABridge) by removes the definition in the form of annotation.

Add dependencies to build.gradle:

annotationProcessor 'com. Zhy. Fabridge: fabridge - compiler: 1.0.0'compile 'com. Zhy. Fabridge: fabridge - API: 1.0.0'Copy the code

First define the method ID, in this case FAB_ITEM_CLICK, and then define the interface in the Activity:

@FCallbackId(id = FAB_ITEM_CLICK)public void onItemClick(String str) {  // The method name is arbitrary
    Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}Copy the code

Finally, within the Fragment, call the “ID=FAB_ITEM_CLICK” method (which could be in the Activity, or any class) with the following form:

Fabridge.call(mActivity,FAB_ITEM_CLICK,"data");  // call the method corresponding to the ID. "data" is the parameter valueCopy the code

The Activity passes data to the Fragment

It is easy for an Activity to pass data to a Fragment. Get the Fragment object and call the Fragment method. For example, to pass a string to the Fragment, define a method in the Fragment:

public void setString(String str) {     this.str = str;
}Copy the code

Call fragment.setString(“hello”) in your Activity.

Fragment communication

Since there is no dependency relationship between fragments, it is recommended to use activities as an intermediary rather than direct communication between fragments.

DialogFragment

DialogFragment is introduced in Android 3.0. It replaces Dialog and is used to implement Dialog boxes. Its advantage is that the dialog box state can be preserved even when the screen is rotated.

To customize the dialog style, simply inherit DialogFragment and rewrite onCreateView(), which returns the dialog UI. Here is an example of a rounded dialog box that implements a progress bar style.

public class ProgressDialogFragment extends DialogFragment {    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); // Eliminate the Title field
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));  // Make the background transparent
        setCancelable(false);  // Clicking on the outside cannot be cancelled
        View root = inflater.inflate(R.layout.fragment_progress_dialog, container);        return root;
    }     public static ProgressDialogFragment newInstance(a) {        return new ProgressDialogFragment(a); }}Copy the code

We use the progress bar animation Lottie (https://github.com/airbnb/lottie-android), Lottie animation (https://www.lottiefiles.com/) to find from here. It’s very easy to use, just download the JSON animation file and write to the XML:

<com.airbnb.lottie.LottieAnimationView
    android:layout_width="wrap_content"  // The size depends on the JSON file
    android:layout_height="wrap_content"
    app:lottie_fileName="loader_ring.json"   / / the JSON file
    app:lottie_loop="true"    // Loop
    app:lottie_autoPlay="true" />  // Auto playCopy the code

Then display the dialog with the following code:

ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");//fragment.dismiss();Copy the code

To achieve rounded corners, in addition to making the background transparent in onCreateView(), we also need to add a background to the UI:

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff"/>
    <corners
        android:radius="20dp"/>
</shape>Copy the code

ViewPager + fragments

The basic use

ViewPager is a class in the Support V4 library that provides interface sliding and inherits from ViewGroup. PagerAdapter is the ViewPager adapter class that provides the interface for the ViewPager. But in general, usually use the PagerAdapter two subclasses: FragmentPagerAdapter and FragmentStatePagerAdapter as ViewPager adapter, they are the characteristics of the interface is fragments.

In the support v13 and support v4 provides FragmentPagerAdapter and FragmentStatePagerAdapter, the difference is that: Support v13 used in android. App. Fragments, and support the v4 using android. Support. The v4. App. The fragments. Generally use the support FragmentPagerAdapter and FragmentStatePagerAdapter in v4.

By default, ViewPager caches interfaces adjacent to the current page. For example, when sliding to page 2, it initializes the interfaces on page 1 and page 3 (i.e. the Fragment object with the lifecycle function running to onResume()). You can set the number of offline cache interfaces by using setOffscreenPageLimit(count).

FragmentPagerAdapter and FragmentStatePagerAdapter need rewriting method are all the same, common rewrite method is as follows:

  • Public FragmentPagerAdapter(FragmentManager FM): constructor with the FragmentManager parameter. In a nested Fragment scenario, the arguments to the child PagerAdapter are passed to getChildFragmentManager().

  • Fragment getItem(int position): Returns the Fragment at position number, must be rewritten.

  • Int getCount(): Returns the number of pages in ViewPager, must be overridden.

  • Object instantiateItem(ViewGroup Container, int Position): A container is a Fragment of a ViewPager Object that returns the position position.

  • Void destroyItem(ViewGroup Container, int Position, Object Object): Container is the ViewPager Object, and Object is the Fragment Object.

  • getItemPosition(Object object): “Object” is a Fragment object. If POSITION_UNCHANGED is returned, the Fragment is not refreshed. If POSITION_NONE is returned, Call destroyItem() and instantiateItem() to destroy and rebuild the Fragment. POSITION_UNCHANGED is returned by default.

Lazy loading

Lazy loading is mainly applied to the situation where each page is Fragment and ViewPager. The scene is the main interface of wechat with four tabs at the bottom. When you slide to another TAB, “Loading” will be displayed first, and the normal interface will be displayed later.

By default, ViewPager caches the current page and adjacent left and right interfaces. The main reasons for lazy loading are as follows: The interface that the user does not enter requires a series of resource-consuming and time-consuming operations such as network and database. It is unnecessary to load data in advance.

The lazy loading idea here is: the user can not see the interface, only initialize the UI, but do not do any data loading. Wait until you get to the page, and then asynchronously load the data and update the UI.

Here is the effect similar to wechat, the entire UI layout is: Bottom with PagerBottomTabStrip (https://github.com/tyzlmjj/PagerBottomTabStrip), project implementation, the ViewPager is above, use FragmentPagerAdapter. The logic is: when the user slides to another interface, it will first show that the interface is loading, and when the data is loaded (instead of sleeping for 1 second), the normal interface will be displayed.

ViewPager caches the left and right adjacent interfaces by default. To avoid unnecessary reloading of data (repeated calls to onCreateView()), since there are four tabs, the offline cache radius is set to 3, that is, setOffscreenPageLimit(3).

The setUserVisibleHint(Boolean isVisible) method that relies primarily on the Fragment is called when the Fragment becomes visible. When the Fragment becomes invisible, setUserVisibleHint(false) is called when:

  • Before onAttach(), call setUserVisibleHint(false).

  • Before onCreateView(), setUserVisibleHint(true) is called if the interface is the current page, setUserVisibleHint(false) is called otherwise.

  • When the interface becomes visible, call setUserVisibleHint(true). * Call setUserVisibleHint(false) when the interface becomes invisible.

Lazy loading Fragment implementation:

public class LazyFragment extends Fragment {    private View mRootView;    private boolean mIsInited;    private boolean mIsPrepared;    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mRootView = inflater.inflate(R.layout.fragment_lazy, container, false);
        mIsPrepared = true;
        lazyLoad();            return mRootView;
    }                 public void lazyLoad(a) {        if(getUserVisibleHint() && mIsPrepared && ! mIsInited) {// Initialize asynchronously, display normal UI after initializationloadData(); }}private void loadData(a) {         new Thread() {         public void run(a) {                //1. Load data
                / / 2. Update the UI
                //3. mIsInited = true
            }
        }.start();
    }          @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {            super.setUserVisibleHint(isVisibleToUser);            if(isVisibleToUser) { lazyLoad(); }}public static LazyFragment newInstance(a) {           return new LazyFragment(a); }}Copy the code

Note:

  • There are two variables in the Fragment that control whether data loading is required:

    • MIsPrepared: If the UI doesn’t have the inflate yet, you don’t need to load the data because setUserVisibleHint() is called once before onCreateView(). If it is called then the UI doesn’t have the inflate yet, Therefore, data cannot be loaded.

    • MIsInited: indicates whether data has been loaded. If so, no further action is required. Because setUserVisibleHint(true) is called when the interface is visible, if you slide to the interface after data loading, slide away, and slide back, setUserVisibleHint(true) is called again. MIsInited =true So you don’t have to load the data again.

  • The lazyLoad () : Lazy core class. In this method, only the interface is visible (getUserVisibleHint()== True), the UI is ready (mIsPrepared), and no data has been loaded in the past (mIsInited==false). LoadData () is used to loadData. After data loading, set mIsInited to true.

The layout XML consists of two containers, one of which is the initial display state, r.I.C.container_Empty. When the data is loaded, r.I.C.container is displayed:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/container_empty" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" Android :text=" loading "/> </RelativeLayout> <RelativeLayout Android :id="@+id/container" Android :layout_width="match_parent"  android:layout_height="match_parent" android:visibility="gone" > ... </RelativeLayout> </FrameLayout>Copy the code

reference

  • Portal (https://www.raywenderlich.com/169885/android-fragments-tutorial-introduction-2)

  • Tutorial 1

    (http://assets.en.oreilly.com/1/event/68/Fragments%20for%20All%20Presentation.pdf)

  • Tutorial 2

    (http://vinsol.com/blog/2014/09/15/advocating-fragment-oriented-applications-in-android/)

  • Life cycle (https://github.com/xxv/android-lifecycle)

  • detach vs remove

    (https://stackoverflow.com/questions/9156406/whats-the-difference-between-detaching-a-fragment-and-removing-it)

  • Google I/O 2016: What the Fragment? (https://www.youtube.com/watch?v=k3IT-IJ0J98)

  • Google I/O 2017: Fragment Tricks

    (https://www.youtube.com/watch?v=eUG3VWnXFtg)

  • The difference between mAdded and mActive (https://stackoverflow.com/questions/25695960/difference-between-madded-mactive-in-source-code-of-suppo rt-fragmentmanager)

  • How to avoid an IllegalStateException anomalies (http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html)


If you think our content is good, please forward it to moments and share it with your friends