Tuesday, September 25, 2012

Life cycle of Fragments in ViewPager

In the exercise "Communication between Fragments in ViewPager" demonstrate how to pass data from Fragment A to Fragment B in a ViewPager. It's assumed that Fragment B is created when data is passed, and keep alive.

It's a case that the target Fragment haven't been created when data is passed, java.lang.NullPointerException will be thrown. And also another case the target Fragment have been destroyed after data passed.

So it's modified to keep buffer (saveToB and saveToC) in main activity (AndroidViewPagerActivity.java) to hold the data passed. In Fragment B and C, check any data exist in onResume() call-back method.

Life cycle of Fragments in ViewPager


fragment_a.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical" >
   <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="It's Fragment A" />
   <EditText
       android:id="@+id/a_input"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
   <Button
       android:id="@+id/a_enter"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" 
       android:text="Enter to Fragment B"/>
   <Button
       android:id="@+id/a_enter_toC"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" 
       android:text="Enter to Fragment C"/>
</LinearLayout>


MyFragmentA.java
package com.exercise.AndroidViewPager;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MyFragmentA extends Fragment {
 
 EditText A_input;
 Button A_enter;
 Button A_enter_toC;

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  View myFragmentView = inflater.inflate(R.layout.fragment_a, container, false);
  
  A_input = (EditText)myFragmentView.findViewById(R.id.a_input);
  A_enter = (Button)myFragmentView.findViewById(R.id.a_enter);
  A_enter.setOnClickListener(A_enterOnClickListener);
  A_enter_toC = (Button)myFragmentView.findViewById(R.id.a_enter_toC);
  A_enter_toC.setOnClickListener(AtoC_enterOnClickListener);
  
  return myFragmentView;
 }
 
 OnClickListener A_enterOnClickListener
 = new OnClickListener(){

  @Override
  public void onClick(View arg0) {
   
   String textPassToB = A_input.getText().toString();
   
   String TabOfFragmentB = ((AndroidViewPagerActivity)getActivity()).getTabFragmentB();
   
   MyFragmentB fragmentB = (MyFragmentB)getActivity()
     .getSupportFragmentManager()
     .findFragmentByTag(TabOfFragmentB);

   if(fragmentB == null){
    ((AndroidViewPagerActivity)getActivity()).putSaveToB(textPassToB);
    Toast.makeText(getActivity(), 
      "fragmentB == null", 
      Toast.LENGTH_LONG).show();
   }else{
    ((AndroidViewPagerActivity)getActivity()).putSaveToB(textPassToB);
    
    fragmentB.b_updateText(textPassToB);
    Toast.makeText(getActivity(), 
      "text sent to Fragment B:\n " + TabOfFragmentB, 
      Toast.LENGTH_LONG).show();
   }
   
   
  }};
  
  OnClickListener AtoC_enterOnClickListener
  = new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    
    String textPassToC = A_input.getText().toString();
    
    String TabOfFragmentC = ((AndroidViewPagerActivity)getActivity()).getTabFragmentC();
    
    MyFragmentC fragmentC = (MyFragmentC)getActivity()
      .getSupportFragmentManager()
      .findFragmentByTag(TabOfFragmentC);
    
    if(fragmentC == null){
     ((AndroidViewPagerActivity)getActivity()).putSaveToC(textPassToC);
     Toast.makeText(getActivity(), 
       "fragmentC == null", 
       Toast.LENGTH_LONG).show();
    }else{
     ((AndroidViewPagerActivity)getActivity()).putSaveToC(textPassToC);
     
     fragmentC.c_updateText(textPassToC);
     Toast.makeText(getActivity(), 
       "text sent to Fragment C:\n " + TabOfFragmentC, 
       Toast.LENGTH_LONG).show();
    }

    
   }};

}


fragment_b.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical" >
   <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="It's Fragment B" />
   <TextView
       android:id="@+id/b_received"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
   <ImageView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:scaleType="center"
       android:src="@drawable/ic_launcher"/>
</LinearLayout>


MyFragmentB.java
package com.exercise.AndroidViewPager;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class MyFragmentB extends Fragment {

 TextView b_received;
 
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  View myFragmentView = inflater.inflate(R.layout.fragment_b, container, false);
  
  b_received = (TextView)myFragmentView.findViewById(R.id.b_received);
  String myTag = getTag();
  
  ((AndroidViewPagerActivity)getActivity()).setTabFragmentB(myTag);
  
  Toast.makeText(getActivity(), 
    "MyFragmentB.onCreateView(): " + myTag, 
    Toast.LENGTH_LONG).show();

  return myFragmentView;
 }
 
 public void b_updateText(String t){
  b_received.setText(t);
 }

 @Override
 public void onResume() {
  super.onResume();
  String t = ((AndroidViewPagerActivity)getActivity()).getSaveToB();
  if(t != null){
   b_received.setText(t);
  }
  
  Toast.makeText(getActivity(), 
    "MyFragmentB.onResume()",
    Toast.LENGTH_LONG).show();
 }
 
 @Override
 public void onDestroyView() {
  super.onDestroyView();
  Toast.makeText(getActivity(), 
    "MyFragmentB.onDestroyView()",
    Toast.LENGTH_LONG).show();
 }

}


fragment_c.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical" >
   <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="It's Fragment C" />
   <TextView
       android:id="@+id/c_received"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</LinearLayout>


MyFragmentC.java
package com.exercise.AndroidViewPager;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class MyFragmentC extends Fragment {
 
 TextView c_received;

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  View myFragmentView = inflater.inflate(R.layout.fragment_c, container, false);
  
  c_received = (TextView)myFragmentView.findViewById(R.id.c_received);
  String myTag = getTag();
  
  ((AndroidViewPagerActivity)getActivity()).setTabFragmentC(myTag);
  
  Toast.makeText(getActivity(), 
    "MyFragmentC.onCreateView(): " + myTag, 
    Toast.LENGTH_LONG).show();
  
  return myFragmentView;
 }
 
 public void c_updateText(String t){
  c_received.setText(t);
 }
 
 @Override
 public void onResume() {
  super.onResume();
  String t = ((AndroidViewPagerActivity)getActivity()).getSaveToC();
  if(t != null){
   c_received.setText(t);
  }
  
  Toast.makeText(getActivity(), 
    "MyFragmentC.onResume()",
    Toast.LENGTH_LONG).show();
 }

 @Override
 public void onDestroyView() {
  super.onDestroyView();
  Toast.makeText(getActivity(), 
    "MyFragmentC.onDestroyView()",
    Toast.LENGTH_LONG).show();
 }

}


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

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>


AndroidViewPagerActivity.java
package com.exercise.AndroidViewPager;

import java.util.ArrayList;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;

public class AndroidViewPagerActivity extends FragmentActivity {
 
 ViewPager mViewPager;
 TabsAdapter mTabsAdapter;
 
 String TabFragmentB;
 String TabFragmentC;
 
 String saveToB = null;
 String saveToC = null;
 
 public void setTabFragmentB(String t){
  TabFragmentB = t;
 }
 
 public String getTabFragmentB(){
  return TabFragmentB;
 }
 
 public void setTabFragmentC(String t){
  TabFragmentC = t;
 }
 
 public String getTabFragmentC(){
  return TabFragmentC;
 }
 
 public void putSaveToB(String textToSave){
  saveToB = textToSave;
 }
 
 public void putSaveToC(String textToSave){
  saveToC = textToSave;
 }
 
 public String getSaveToB(){
  String t = saveToB;
  return t;
 }
 
 public String getSaveToC(){
  String t = saveToC;
  return t;
 }
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mViewPager = new ViewPager(this);
        mViewPager.setId(R.id.pager);
        setContentView(mViewPager);
        
        final ActionBar bar = getActionBar();
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

        mTabsAdapter = new TabsAdapter(this, mViewPager);
        mTabsAdapter.addTab(bar.newTab().setText("Fragment A"),
                MyFragmentA.class, null);
        mTabsAdapter.addTab(bar.newTab().setText("Fragment B"),
          MyFragmentB.class, null);
        mTabsAdapter.addTab(bar.newTab().setText("Fragment C"),
          MyFragmentC.class, null);
        
        if (savedInstanceState != null) {
            bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }

    }

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  //super.onSaveInstanceState(outState);
  outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
 }
 
 public static class TabsAdapter extends FragmentPagerAdapter
  implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
  
  private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
        
        static final class TabInfo {
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(Class<?> _class, Bundle _args) {
                clss = _class;
                args = _args;
            }
        }

  public TabsAdapter(FragmentActivity activity, ViewPager pager) {
   super(activity.getSupportFragmentManager());
            mContext = activity;
            mActionBar = activity.getActionBar();
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }

  public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
            TabInfo info = new TabInfo(clss, args);
            tab.setTag(info);
            tab.setTabListener(this);
            mTabs.add(info);
            mActionBar.addTab(tab);
            notifyDataSetChanged();
        }

  @Override
  public void onPageScrollStateChanged(int state) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onPageSelected(int position) {
   // TODO Auto-generated method stub
   mActionBar.setSelectedNavigationItem(position);
  }

  @Override
  public void onTabReselected(Tab tab, FragmentTransaction ft) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onTabSelected(Tab tab, FragmentTransaction ft) {
   Object tag = tab.getTag();
            for (int i=0; i<mTabs.size(); i++) {
                if (mTabs.get(i) == tag) {
                    mViewPager.setCurrentItem(i);
                }
            }
  }

  @Override
  public void onTabUnselected(Tab tab, FragmentTransaction ft) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public Fragment getItem(int position) {
   TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext, info.clss.getName(), info.args);
  }

  @Override
  public int getCount() {
   return mTabs.size();
  }

 }

}


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.exercise.AndroidViewPager"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="11" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidViewPagerActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


download filesDownload the files.


2 comments:

Anonymous said...

I like This Example ;-)

good man :D

Jovanny said...

Thanks a lot!
Your code example was very very useful for me!