Saturday, June 23, 2012

Communication between Fragments in ViewPager

In Previous exercise "ViewPager" (and fix of "NullPointerException of ViewPager when change orientation"), I create a ViewPager with three independent Fragments. Now, I want to make some input (EditText) from Fragment A, then transfer it to Fragment B.

Communication between Fragments in ViewPager


With FragmentManager, we can access another fragment from our current fragment by calling findFragmentByTag(<tag of target fragment>). But, what's the tag of Fragment B? I can't found any method something like setTag()!

Luckly; I found getTag() method to get the tag name of current fragment. So I create a mechanism to access the tag across fragments via the main activity, AndroidViewPagerActivity. - Call getTag() in onCreateView() of MyFragmentB. Then pass it to the main activity AndroidViewPagerActivity. When MyFragmentA need to pass something to MyFragmentB, it retrieve it from AndroidViewPagerActivity.

Modify the main activity, AndroidViewPagerActivity.java, to have a String TabFragmentB; and implement setTabFragmentB() and getTabFragmentB() to access it.
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;
 
 public void setTabFragmentB(String t){
  TabFragmentB = t;
 }
 
 public String getTabFragmentB(){
  return TabFragmentB;
 }
 
    /** 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();
  }

 }

}


Modify MyFragmentA.java and /res/layout/fragment_a.xml to add a ExitText and a Butten to send something.

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;

 @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);
  
  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);

   fragmentB.b_updateText(textPassToB);
   
   Toast.makeText(getActivity(), 
     "text sent to Fragment B:\n " + TabOfFragmentB, 
     Toast.LENGTH_LONG).show();
  }};

}


<?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"/>
</LinearLayout>


Modify MyFragmentB.java and /res/layout/fragment_b.xml to receive something.

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);
 }

}


<?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>


Download the files.

Next:
- AsyncTask run in background thread of a invisible Fragment of ViewPager

Please notice:
This exercise aim to demonstrate how to communicate between Fragments in ViewPager. But, you have to take care about live-cycle of the Fragments. For example, may be the target Fragment have not yet created when data is passed. Or, the Fragment have been destroyed after data passed. Read more: Life cycle of Fragments in ViewPager.

19 comments:

  1. great post!

    i have a quick question, how do i send my activity values to say FragmentB string? getActivity() doesn't seems to work in activity class file.

    ReplyDelete
  2. Hello I have a problem with our method. When I try to send any information to fragment B and it's ok but when I try to send any information to fragment C with the same method the program bug and stoped. Can you help me for this problem?

    ReplyDelete
  3. Hello ligol,

    It's problem of life-cycle of Fragments. Please read Life cycle of Fragments in ViewPager.

    ReplyDelete
  4. Thanks,

    It's nice.Work for the me.

    ReplyDelete
  5. This solution is INGENIOUS. I have been trying to solve a problem with using FragmentASDF.method() not being able to be static (but that function call requires it). This solution helped fix that! Thank you so much!

    ReplyDelete
  6. 200 lines of code just to send one String :) nice

    ReplyDelete
  7. getTag() is always null for me, any body know why ? thanks

    ReplyDelete
  8. In Build Paths, delete the reference to
    android-support-v4.jar- /home/eric/android....(missing)
    for the download to work.

    ReplyDelete
  9. Delete the reference to
    android-support-v4.jar- /home/eric/android....(missing)
    for the download to work.

    ReplyDelete
  10. I have an error on mViewPager.setId(R.id.pager);

    cant find the "pager"

    ReplyDelete
  11. Thank you for Helpful tutorial to communicate between fragments without using interfaces

    ReplyDelete
  12. Hello!
    When I click the button not only data but also screen move to next screen, can u give me code?

    ReplyDelete
  13. if want to add more questional bottons, how should I do?

    ReplyDelete
  14. hi, if I want to pass a data(.kml file) how can i do??
    Thanks

    ReplyDelete
  15. Is it possible to send and receive data from one fragment to other in Navigationdrawer?
    i have 2 fragments in Navigationdrawer.I am trying to send data from one fragment to other.. ...can u please tell me how to do it?

    ReplyDelete
  16. Implementing this code to my app does not show text in next fragment. any reason ?

    ReplyDelete
  17. WOW DUDE this worked like a charm

    ReplyDelete