Some problems with Viewpager and fragment in Android app _android

Source: Internet
Author: User
Tags object object


Before you know how Viewpager works, review how ListView works:



ListView only when you need to display some list items does it request the available view objects; If you create a view object for all of the list item data, you waste memory;
Who is ListView to apply for view objects? The answer is adapter. Adapter is a controller object that takes the data from the model layer, creates and populates the necessary view objects, and returns the prepared view objects to the ListView;
First, by calling the adapter GetCount () method, ListView asks how many objects are included in the array list (to avoid errors that array crosses over); ListView then invokes adapter's GetView (int, View, ViewGroup) method.
Viewpager is similar to ListView in some way, the difference being that ListView fills the view through Arrayadapter.getview (int position, view Convertview, ViewGroup parent) Viewpager fragmentpageradapter.getitem (int position) generates fragment at the specified location.



And what we need to focus on is:
How does the Viewpager and its adapter work together?
Disclaimer: This article is for android.support.v4.app.*
Inherits from Android.support.v4.view.PagerAdapter, each page is a fragment, and all fragment instances are kept in Fragment manager. So it works for a small number of fixed fragment, such as a set of tabs for pagination. Except when the fragment is not visible, its view hierarchy may be destroyed, and the fragment of each page will be saved in memory. (Translated from the comments section of the code file)
Inherited from Android.support.v4.view.PagerAdapter, each page is a fragment, when fragment is not needed (such as not visible), the entire fragment will be destroyed, in addition to saved State is saved (the saved bundle is used to restore the fragment instance). So it applies to many pages of the situation. (Translated from the comments section of the code file)
These two subclasses need to implement GetItem (int) and Android.support.v4.view.PagerAdapter.getCount ().



Understand the typical usage of Viewpager and fragmentpageradapter with a piece of code first
Do a detailed analysis later:

// Set a PagerAdapter to supply views for this pager.
 ViewPager viewPager = (ViewPager) findViewById (R.id.my_viewpager_id);
 viewPager.setAdapter (mMyFragmentPagerAdapter);
 
 private FragmentPagerAdapter mMyFragmentPagerAdapter = new FragmentPagerAdapter (getSupportFragmentManager ()) {
  @Override
  public int getCount () {
   return 2; // Return the number of views available.
  }
 
  @Override
  public Fragment getItem (int position) {
   return new MyFragment (); // Return the Fragment associated with a specified position.
  }
 
  // Called when the host view is attempting to determine if an item's position has changed.
  @Override
  public int getItemPosition (Object object) {
   if (object instanceof MyFragment) {
    ((MyFragment) object) .updateView ();
   }
   return super.getItemPosition (object);
  }
 };
 
 private class MyFragment extends Fragment {
  @Override
  public void onCreate (Bundle savedInstanceState) {
   super.onCreate (savedInstanceState);
   // do something such as init data
  }
 
  @Override
  public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   View view = inflater.inflate (R.layout.fragment_my, container, false);
   // init view in the fragment
   return view;
  }
 
  public void updateView () {
   // do something to update the fragment
  }
 }
FragmentPagerAdapter and FragmentStatePagerAdapter manage the Fragment slightly differently. Before examining the difference between the two in detail, we first feel it through two more intuitive ways:

Visually compare the difference between FragmentPagerAdapter and FragmentStatePagerAdapter through two pictures
Note: These two pictures are from the "Authorized Programming Guide for Android". The original picture has 3 Fragments. I added 1 Fragment and the method I was transferred to.
Fragment Management of FragmentPagerAdapter:


Fragment Management of FragmentStatePageAdapter:


Detailed analysis of the calling of adapter method and fragment lifecycle method
Well, after the experience is over, we need to explore the details and sort out the process of creating and destroying the fragment by the adapter. In the process, which of the adapter method and fragment lifecycle method has been transferred, which are the same, and which are different.

When initially on page 0, the adapter not only created a Fragment instance for page 0, but also created a Fragment instance for the adjacent page 1:

// just started on page0
D / Adapter (25946): getItem (0)
D / Fragment0 (25946): newInstance (2015-09-10) // Note: newInstance () calls the constructor method of Fragment, the same below.
D / Adapter (25946): getItem (1)
D / Fragment1 (25946): newInstance (Hello World, I'm li2.)
D / Fragment0 (25946): onAttach ()
D / Fragment0 (25946): onCreate ()
D / Fragment0 (25946): onCreateView ()
D / Fragment1 (25946): onAttach ()
D / Fragment1 (25946): onCreate ()
D / Fragment1 (25946): onCreateView ()
The first time you slide from page 0 to page 1, the adapter will also create a fragment instance for the adjacent page 2;

// The first slide to page1
D / Adapter (25946): onPageSelected (1)
D / Adapter (25946): getItem (2)
D / Fragment2 (25946): newInstance (true)
D / Fragment2 (25946): onAttach ()
D / Fragment2 (25946): onCreate ()
D / Fragment2 (25946): onCreateView ()
FragmentPagerAdapter and FragmentStatePagerAdapter said in unison: Now, please rest assured that the subordinates will prepare the next page view for you! muah!
They have the same attitude towards the next page, but for the previous page, they have done different things:

FragmentPagerAdapter said: The instance on the previous page is still retained, but its view is destroyed:
// Nth time (N is not equal to 1) swipe right to select page2
D / Adapter (25946): onPageSelected (2)
D / Adapter (25946): destroyItem (0) // destroy page0's view
D / Fragment0 (25946): onDestroyView ()
D / Fragment3 (25946): onCreateView () // The Fragment instance of page3 is still saved in FragmentManager, so just create its view
FragmentStatePagerAdapter said: The instances and views on the previous page have been destroyed by me:
// Nth time (N is not equal to 1) slide to the right to select page2
D / Adapter (27880): onPageSelected (2)
D / Adapter (27880): destroyItem (0) // destroy page0 instance and view
D / Adapter (27880): getItem (3) // Create a fragment of page3
D / Fragment3 (27880): newInstance ()
D / Fragment0 (27880): onDestroyView ()
D / Fragment0 (27880): onDestroy ()
D / Fragment0 (27880): onDetach ()
D / Fragment3 (27880): onAttach ()
D / Fragment3 (27880): onCreate ()
D / Fragment3 (27880): onCreateView ()
Fragment getItem (int position)
// Return the Fragment associated with a specified position.
public abstract Fragment getItem (int position);
When the adapter needs a Fragment at the specified position, and the Fragment does not exist, getItem is transferred to and returns an instance of Fragment to the adapter.
Therefore, it is necessary to emphasize again that getItem is to create a new Fragment, but this method name may be mistaken for returning an existing Fragment.
For FragmentPagerAdapter, when the Fragment of each page is created, this function will not be transferred. For FragmentStatePagerAdapter, because Fragment will be destroyed, it will still be transferred.
Since we must instantiate a Fragment in getItem, when getItem () is called, the corresponding life cycle function of Fragment is transferred to:

D / Adapter (25946): getItem (1)
D / Fragment1 (25946): newInstance (Hello World, I'm li2.) // newInstance () calls the constructor method of Fragment;
D / Fragment1 (25946): onAttach ()
D / Fragment1 (25946): onCreate ()
D / Fragment1 (25946): onCreateView ()
void destroyItem (ViewGroup container, int position, Object object)
// Remove a page for the given position.
public void FragmentPagerAdapter.destroyItem (ViewGroup container, int position, Object object) {
  mCurTransaction.detach ((Fragment) object);
}

public void FragmentStatePagerAdapter.destroyItem (ViewGroup container, int position, Object object) {
  mSavedState.set (position, mFragmentManager.saveFragmentInstanceState (fragment));
  mFragments.set (position, null);
  mCurTransaction.remove (fragment);
}

Destroy the fragment at the specified position. From the source code, you can see the difference between the two, one detach and one remove, which will call different Fragment life cycle functions:

// For FragmentPagerAdapter
D / Adapter (25946): onPageSelected (2)
D / Adapter (25946): destroyItem (0)
D / Fragment0 (25946): onDestroyView () // destroy the view

// For FragmentStatePagerAdapter
D / Adapter (27880): onPageSelected (2)
D / Adapter (27880): destroyItem (0)
D / Fragment0 (27880): onDestroyView () // destroy the view
D / Fragment0 (27880): onDestroy () // destroy the instance
D / Fragment0 (27880): onDetach ()
FragmentPagerAdapter and FragmentSComparison summary of tatePagerAdapter

The two methods of use are basically the same, the only difference is that when the fragment that is no longer needed is uninstalled, the processing method used is different:

Using FragmentStatePagerAdapter will destroy unnecessary fragments. After the transaction is committed, the fragment can be completely removed from the activity's FragmentManager. The "state" in the class name indicates that when the fragment is destroyed, it will save the Bundle information in its onSaveInstanceState (Bundle) method. After the user switches back to the original page, the saved instance state can be used to restore and generate a new fragment.
FragmentPagerAdapter's approach is very different. For fragments that are no longer needed, FragmentPagerAdapter chooses to call the transaction's detach (Fragment) method instead of the remove (Fragment) method to handle it. In other words, the FragmentPagerAdapter just destroyed the fragment's view, but still retains the fragment instance in the FragmentManager. Therefore, the fragment created by FragmentPagerAdapter will never be destroyed.
Update Fragment in ViewPager
When calling notifyDataSetChanged (), the calling methods of the two adapters are the same, and the getItemPosition of the current page and the two adjacent pages will be called.

// Called when the host view is attempting to determine if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
public int getItemPosition (Object object) {
  return POSITION_UNCHANGED;
}
The solution found from the Internet is to overwrite getItemPosition to return POSITION_NONE to trigger the destruction and reconstruction of Fragment. However, this will lead to frequent destruction and reconstruction of Fragment, which is not the best method.
Later, I focused on the entry parameter object, "representing an item", which is actually a Fragment. I only need to provide the Fragment with a public method to update the view:

@Override
// To update fragment in ViewPager, we should override getItemPosition () method,
// in this method, we call the fragment's public updating method.
public int getItemPosition (Object object) {
  Log.d (TAG, "getItemPosition (" + object.getClass (). GetSimpleName () + ")");
  if (object instanceof Page0Fragment) {
    ((Page0Fragment) object) .updateDate (mDate);
  } else if (object instanceof Page1Fragment) {
    ((Page1Fragment) object) .updateContent (mContent);
  } else if (object instanceof Page2Fragment) {
    ((Page2Fragment) object) .updateCheckedStatus (mChecked);
  } else if (...) {
  }
  return super.getItemPosition (object);
};

// Method invocation when updating the interface
// When the current page is 0
D / Adapter (21517): notifyDataSetChanged (+0)
D / Adapter (21517): getItemPosition (Page0Fragment)
D / Fragment0 (21517): updateDate (2015-09-12)
D / Adapter (21517): getItemPosition (Page1Fragment)
D / Fragment1 (21517): updateContent (Hello World, I am li2.)

// When the current page is 1
D / Adapter (21517): notifyDataSetChanged (+1)
D / Adapter (21517): getItemPosition (Page0Fragment)
D / Fragment0 (21517): updateDate (2015-09-13)
D / Adapter (21517): getItemPosition (Page1Fragment)
D / Fragment1 (21517): updateContent (Hello World, I am li2.)
D / Adapter (21517): getItemPosition (Page2Fragment)
D / Fragment2 (21517): updateCheckedStatus (true)

When I first called notifyDataSetChanged to try to update the Fragment, I did this: save all Fragments with the arraylist, and when it needs to be updated, remove the Fragment from the arraylist, and then call the update method of the Fragment. This approach is very fishy, at that time I did not understand the adapter Fragment manager is managing all the Fragment for me. And I just need:

Override getCount to tell the adapter how many fragments;
Override getItem to instantiate a Fragment at a specified position and return it to the adapter;
Override getItemPosition, forcibly transform the entry parameter into a custom fragment, and then call the update method of the fragment to complete the update.
You only need to overwrite the methods of these adapters. The adapter will do all the management work for you, and you don't need to save and maintain the fragment yourself.

Replace Fragment in ViewPager
The application scenario may be like this, such as a set of buttons, Day / Month / Year, and a ViewPager that contains several Fragments. Click on different buttons to show different fragments.
For specific implementation, please refer to the following code:
github.com/li2/Update_Replace_Fragment_In_ViewPager/ContainerFragment.java

Some misunderstandings
ViewPager.getChildCount () returns Fragments that are not destroyed by the current ViewPager, not all Fragments. To get the total number of fragments, you should call ViewPager.getAdapter (). GetCount ().

Use Fragment + ListView in ViewPager, after switching multiple times, there is no data display in ListView?

ViewPager + Fragment dynamic addition and deletion cache
cause:

We often use ViewPager + Fragment in development. Sometimes there may be such a requirement. The content in ViewPager needs to be dynamically added and deleted. However, we all know that ViewPager is loading the current page in order to ensure the smoothness of sliding. At that time, the contents of the left and right pages of the pager page have been loaded into the memory, so if we do not perform any processing at this time, I will achieve the desired effect.
solution:

1. Replace FragmentPagerAdapter with FragmentStatePagerAdapter, because as long as the former is loaded, the view in fragment remains in memory, no matter how you refresh it in this process, clearing is useless until the program exits; the latter can meet our needs . 2. We can rewrite the Adapter's method – getItemPosition () to return PagerAdapter.POSITION_NONE. The following is quoted content:

@Override

  public int getItemPosition (Object object) {

  return PagerAdapter.POSITION_NONE;

 }

At this step, we can truly achieve arbitrary, complete deletion of fragment in the viewpager, add and delete at will.
Second, make good use of Dialog pages that are simple to interact with or only display functions. If you use an Activity to display, it is too cumbersome and expensive. If you use Fragment, the life cycle of egg pain is not easy to handle. Full-screen Dialog to simulate an Activity is a good choice.
Third, the Splash page: Almost every page will have a Spalsh page, usually we will use an Activity to load a full-screen background image, or put an app logo, display 2 seconds, jump to the login or home page In the meantime, some data initialization, check and update operations may be done. I believe most small partners do the same, but do n’t you think it ’s a waste to kill an Activity after only 2 seconds? Personally think that such overhead is very cost-effective. We can borrow the above item and use a Dialog to simulate a Splash page. After 2 seconds, dismiss the Dialog, and check for updates, initialize data, and other operations are placed in the MainActivity. Or use Fragment instead of SplashActivity and other methods, can achieve the same effect of Splash page.
4. Be kind to internal classes In development, we will often use internal classes. The emergence of internal classes solves the limitation of Java's single inheritance, making development more flexible. But if the internal class is not used well, there will be a nightmare of Android Developer, OOM !. why? Students with a slightly better background should all know that internal classes can access member variables and methods of external classes, because internal classes hold references to external classes. When you use an internal class in an Activity, when the Activity is destroyed, you If the inner class is not released, this Activity cannot be recycled by GC, because the inner class holds the application of Activity.
Fifth, library. I can't use id to case in the switch in the library. This was already mentioned in my previous blog post. Here we talk about a library pit. When we introduce a dependency library, the dependency library generally comes with a support v4 package. The version of this v4 package is generally the same as the version when we created the project. But once the version of the v4 package in the dependency library of our own project's v4 package is inconsistent, a large number of inexplicable error logs will follow. The processing method at this time is also very simple. Since v4 packages are backward compatible, we only need to keep the version of the dependent library consistent with the version of our own project. I ’ll summarize it here for the time being. If there is something wrong with the above remarks, I hope you friends can point it out in time.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.