This blog mainly starts from the encapsulation of BaseActivity and BaseFragment, and summarizes our attention and experience about Fragment in actual combat development. Take a look at the following renderings:

Here is the simulation of the user login module, you may say, very common effect, this is what. Well, I’ll tell you that there are so many modules made up of just two activities. When you read this blog post from cover to cover, you’ll be amazed. Without further ado, here we go.

It is a very suitable architecture for developing APP with multi-module Activity+ multi-fragment. Compared with multi-activity, APP with this architecture occupies less memory and improves performance. Compared with single Activity+ multiple Fragment, this kind of development logic is relatively simple, not prone to error.

For multi-module Activity+ multi-fragment, there are two concepts that we need to know about the peer Fragment: For example, the three fragments of QQ’s main interface, message, contact and dynamic are at the same level. The fragments of the main interface in our daily projects are also at the same level as the Fragment flow. For example, my example Demo can be interpreted as the user account flow, which can include: Login/registration module – forget/retrieve password module – User protocol module, these FRAgents belong to the flow type Fragment

My Demo uses a process Fragment, and with today’s topic — BaseActivity and BaseFragment encapsulation, let’s take a look.

1.BaseActivity encapsulation:

Public abstract class BaseActivity extends appactivity {// Layout file ID protected abstract int getContentViewId(); // ID of Fragment protected abstract int getFragmentContentId(); // addFragment protected void addFragment(BaseFragment fragment) {if (fragment! = null) { getSupportFragmentManager().beginTransaction() .replace(getFragmentContentId(), fragment, fragment.getClass().getSimpleName()) .addToBackStack(fragment.getClass().getSimpleName()) .commitAllowingStateLoss(); }} / / remove fragments protected void removeFragment () {if (getSupportFragmentManager () getBackStackEntryCount () > 1) { getSupportFragmentManager().popBackStack(); } else { finish(); }} @override public Boolean onKeyDown(int keyCode, KeyEvent event) { if (KeyEvent.KEYCODE_BACK == keyCode) { if (getSupportFragmentManager().getBackStackEntryCount() == 1)  { finish(); return true; } } return super.onKeyDown(keyCode, event); }}Copy the code

(1) two must implement the abstract methods, obtain the Layout file Layout resource ID, obtain the fragments in the Layout file ID (2) add fragments: Open a fragment that replaces the fragment identified by getFragmentContentId() in the current Layout container. By calling addToBackStack(String Tag), the replace transaction is saved to the back stack, so the user can roll back the transaction and bring back the previous fragment by pressing the back button, If addToBackStack(String Tag) is not called, the fragment is destroyed when the transaction commits and the user cannot navigate back to it. The tag parameter is used to mark the Transaction that is added to the BackStack. CommitAllowingStateLoss (), commitAllowingStateLoss(), commitAllowingStateLoss(), commitAllowingStateLoss() The interface method corresponding to addToBackStack() is popBackStack(), which inserts the transaction operation into the FragmentManager’s action queue and starts execution when the transaction is polled. If the number of transactions is greater than 1, execute the rollback operation. If the number of transactions is equal to 1, the current Activity will only have one Fragment left. Finish () Direct finish ()

2. Further encapsulation of BaseActivity — AppActivity:

/** * Created by tangyangkai on 16/5/4. */ public Abstract class AppActivity extends BaseActivity {// Get the first fragment protected abstract BaseFragment getFirstFragment(); } @override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState); setContentView(getContentViewId()); if (null ! = getIntent()) { handleIntent(getIntent()); } / / to avoid repetition and add fragments the if (null = = getSupportFragmentManager () getFragments ()) {BaseFragment firstFragment = getFirstFragment(); if (null ! = firstFragment) { addFragment(firstFragment); } } } @Override protected int getContentViewId() { return R.layout.activity_base; } @Override protected int getFragmentContentId() { return R.id.fragment_container; }}Copy the code

(1) an abstract method that must be implemented to retrieve the first Fragment that the current Activity should display. (2) An intent method is retrieved from the implementation of the Activity that needs to pass/receive data. (3) An intent method is retrieved from the onCreate() method of the Activity. Add the fragment and paste the activity_base.xml layout file with the following code:




Copy the code

3.BaseFragment encapsulation:

/** * Created by tangyangkai on 16/5/4. */ public abstract class BaseFragment extends Fragment { protected BaseActivity mActivity; protected abstract void initView(View view, Bundle savedInstanceState); // Get layout file ID protected abstract int getLayoutId(); // Get host Activity protected BaseActivity getHoldingActivity() {return mActivity; } @Override public void onAttach(Activity activity) { super.onAttach(activity); this.mActivity = (BaseActivity) activity; } // addFragment protected void addFragment(BaseFragment fragment) {if (null! = fragment) { getHoldingActivity().addFragment(fragment); }} // remove the fragment protected void removeFragment() {getHoldingActivity().removefragment (); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(getLayoutId(), container, false); initView(view, savedInstanceState); return view; }}Copy the code

In order to facilitate the introduction of the following article, I would like to add a situation: A special case of Android is that when the APP is running in the background, the system will recycle all the resources of the APP (killing the process of the APP) when the APP is running in the background, and then the APP will restart when the APP is returned to the foreground from the background. This out-of-memory situation can cause a number of problems, one of which is that a call to getActivity() returns null, indicating a null pointer exception. The solution is to set an Activity mActivity global variable in the Fragment base class, assign it to onAttach(Activity Activity), and use mActivity instead of getActivity(). The code comments are very detailed, you can understand it at a glance.

4. Use of Activity and Fragment: The encapsulation of BaseActivity and BaseFragment has been completed, and the next step is to use it in specific projects, which can be divided into two situations.

The first case: an Activity that does not receive data

/** * Created by tangyangkai on 16/5/10. */ public class MainActivity extends AppActivity { @Override protected BaseFragment getFirstFragment() { return MainFragment.newInstance(); }}Copy the code

The main interface of the Demo, MainActivity, does not receive data from other interfaces. As you can see, the code is fairly compact, and the corresponding MainFragment code is as follows:

public class MainFragment extends BaseFragment { private Button mainBtn, mainSecondBtn; public static MainFragment newInstance() { return new MainFragment(); } @Override protected void initView(View view, Bundle savedInstanceState) { mainBtn = (Button) view.findViewById(R.id.main_btn); mainBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Bundle data = new Bundle(); data.putString("username", "tangyankai"); Intent intent = new Intent(getActivity(), LoginActivity.class); intent.putExtras(data); startActivity(intent); }}); mainSecondBtn = (Button) view.findViewById(R.id.main_second_btn); mainSecondBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AddFragment (SecondFragment. NewInstance (" from the first interface jump ")); }}); } @Override protected int getLayoutId() { return R.layout.fragment_main; }}Copy the code

Very simple business logic, click the first button, carry data, jump to LoginActivity; Click the second button to jump to the registration module, where a deliberate parameter has been added, as described later here.

The second case: an Activity that receives data

/**
 * Created by tangyangkai on 16/5/10.
 */
public class LoginActivity extends AppActivity {

    private String username;
    @Override
    protected void handleIntent(Intent intent) {
        super.handleIntent(intent);
        Bundle bundle = intent.getExtras();
        if (null != bundle) {
            username = bundle.getString("username");
        }
    }

    @Override
    protected BaseFragment getFirstFragment() {
        return FirstFragment.newInstance(username);
    }

}Copy the code

As you can see, the LoginActivity overwrites the handleIntent() method to retrieve data that is being passed to it. Importantly, it creates a Fragment that passes a parameter

/** * Created by tangyangkai on 16/5/10. */ public class FirstFragment extends BaseFragment { @Override protected int getLayoutId() { return R.layout.fragment_first; } public static String FIRST_FRAGMENT = "first_fragment"; private String msg; private EditText usernameEdt; private TextView registerTxt, promiseTxt; private ImageView backImg; public static FirstFragment newInstance(String msg) { FirstFragment fragment = new FirstFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(FIRST_FRAGMENT, msg); fragment.setArguments(bundle); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (null ! = getArguments()) { msg = (String) getArguments().getSerializable(FIRST_FRAGMENT); } } @Override protected void initView(View view, Bundle savedInstanceState) { usernameEdt = (EditText) view.findViewById(R.id.username_edt); usernameEdt.setText(msg); registerTxt = (TextView) view.findViewById(R.id.register_txt); registerTxt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AddFragment (SecondFragment. NewInstance (" jump over from the login interface ")); }}); backImg = (ImageView) view.findViewById(R.id.first_back); backImg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { removeFragment(); }}); promiseTxt = (TextView) view.findViewById(R.id.promise_txt); promiseTxt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addFragment(ThirdFragment.newInstance()); }}); }}Copy the code

There’s a lot of code, so let’s just focus on it.

    public static FirstFragment newInstance(String msg) {
        FirstFragment fragment = new FirstFragment();
        Bundle bundle = new Bundle();
        bundle.putSerializable(FIRST_FRAGMENT, msg);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != getArguments()) {
            msg = (String) getArguments().getSerializable(FIRST_FRAGMENT);
        }
    }Copy the code

Add newInstance to the Fragment, pass in the required parameters, set them into the bundle, then setArguments(bundle), and get them in onCreate. This method of using arguments to create a Fragment is highly recommended: (1) This completes the decoupling between the Fragment and the Activity. A big reason to use fragments is to reuse them. This is illustrated when I click the second button on the home screen to jump to the registration screen. (2) Pass data on fragments, so I recommend using setArguments(Bundle args), and then take it out on onCreate using getArguments(). If there’s an exception due to a lack of memory, The system will help you save the data, will not cause the loss of data. The Intent principle is the same as that of an Activity. (3) Using newInstance(parameter) to create a Fragment object, the advantage is that the caller only needs the data to be passed, and does not need to care about the Key to pass the data.

Fragment: Use your Android Posture properly

(1) Click the register button to jump to the register module. Note that I passed a different parameter from the main interface here, in order to distinguish, and are displayed in the registration module. You will notice that in the Demo, clicking the register button in the login module displays different text than clicking the register button in the front screen to jump to the register module. This is purely a demonstration. In a real project, we can perform different operations on the Fragment and display different data depending on the parameters passed. Maximize Fragment reuse! (2) Click the back button, a word to help you fix, easy to return to the previous interface:

removeFragment();Copy the code

Of course, the effect is the same when you click the phone back button. (3) Click the User Protocol button to jump to the user protocol module.

As for the rest of the interface, you can add modules like forget your password/change your password, no problem. Let’s take a look at what peer types of fragments should be aware of.

5. Overlapping fragments caused by hide() and show() : Check whether savedInstanceState is null in the onCreate function of the base class. If it is not null, it indicates that the Fragment is saved. Instead of re-adding the fragment, read the fragment directly from the previously saved data Tag.

When you add, you add a TAB parameter

Transaction. The add (R.i, dc ontent, IndexFragment, "fg1");Copy the code
public void onCreate(Bundle savedInstanceState) { fManager = getFragmentManager(); if (savedInstanceState ! = null) { fg1 = (AllOfficialAccountFragment) fManager.findFragmentByTag("fg1"); fg2 = (MovieOfficialAccountFragment) fManager.findFragmentByTag("fg2"); fg3 = (NewsOfficialAccountFragment) fManager.findFragmentByTag("fg3"); fg4 = (OtherOfficialAccountFragment) fManager.findFragmentByTag("fg4"); } super.onCreate(savedInstanceState); }Copy the code

BaseActivity and BaseFragment encapsulation is over. This is just the most basic encapsulation. You can encapsulate some common methods into the base class, and let the base class Activity and Fragment play the most role.

Of course, if the business logic is simple, an Activity can handle that kind of interface, then there is no need to use this method. Here are some of the things I learned about fragments during encapsulation. For in-depth analysis of fragments and other considerations, you can refer to the resources just presented.

Download the source code