Communication between Fragments in ViewPager
Asked Answered
F

4

12

I'm trying to do this: http://android-er.blogspot.com/2012/06/communication-between-fragments-in.html Except that I'm using a FragmentStatePagerAdapter

I have an Activity with two fragments(FragmentA & FragmentB)

FragmentA has an edittext and a button, FragmentB has a textview

Now all I want is that whenever I enter something in the edittext and click the button, that something will appear on my textview.

MainActivity:

public class MainActivity extends FragmentActivity {


    ViewPager viewPager = null;
    String TabFragmentB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager = (ViewPager)findViewById(R.id.pager);    
        FragmentManager fragmentManager = getSupportFragmentManager();
        viewPager.setAdapter(new MyAdapter(fragmentManager));

    }

    public class MyAdapter extends FragmentStatePagerAdapter {  

        public MyAdapter (FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            Fragment fragment = null;

            if (i == 0)
            {
                fragment = new FragmentA();
            }
            if (i == 1)
            {
                fragment = new FragmentB();
            }
            return fragment;
        }

        @Override
        public int getCount() {
            return 2;
        }   
    }

    public void setTabFragmentB(String t) {
        TabFragmentB = t;   
    }

    public String getTabFragmentB() { 
        return TabFragmentB;
    }

}

FragmentA:

public class FragmentA extends Fragment {

    EditText et;
    Button bt;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.fraga, container, false);

        et = (EditText)v.findViewById(R.id.edit1);
        bt = (Button)v.findViewById(R.id.button1);
        bt.setOnClickListener(Click);

        return v;
    }

    OnClickListener Click = new OnClickListener(){

        @Override
        public void onClick(View v) {

            String textPassToB = et.getText().toString();

            String TabOfFragmentB = ((MainActivity)getActivity()).getTabFragmentB();

            FragmentB fragmentB = (FragmentB)getActivity()
               .getSupportFragmentManager()
               .findFragmentByTag(TabOfFragmentB);

            fragmentB.updateText(textPassToB);          
        }   
    };

}

FragmentB:

public class FragmentB extends Fragment {

    TextView tv;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.fragb, container, false);    

        tv = (TextView)v.findViewById(R.id.text1);
        String myTag = getTag();

        ((MainActivity)getActivity()).setTabFragmentB(myTag);

        return v;
    }

    public void updateText(String t){
          tv.setText(t);
         }

}

LogCat:

FATAL EXCEPTION: main
 java.lang.NullPointerException
        at lmf.sample1.FragmentA$1.onClick(FragmentA.java:43)
        at android.view.View.performClick(View.java:4212)
        at android.view.View$PerformClick.run(View.java:17476)
        at android.os.Handler.handleCallback(Handler.java:800)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:194)
        at android.app.ActivityThread.main(ActivityThread.java:5371)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
        at dalvik.system.NativeStart.main(Native Method)

Whenever I click the button on my first fragment, my app crashes. What the hell is the problem?

Formica answered 9/12, 2013 at 15:47 Comment(0)
R
30
  1. You could use Intents (register broadcast receiver in fragment B and send broadcasts from fragment A.
  2. Use EventBus: https://github.com/greenrobot/EventBus. It's my favorite approach. Very convinient to use, easy communications between any components (Activity & Services, for example).

Steps to do:

First, create some class to represent event when your text changes:

public class TextChangedEvent {
  public String newText;
  public TextChangedEvent(String newText) {
      this.newText = newText;
  }
}

Then, in fragment A:

//when text changes
EventBus bus = EventBus.getDefault();
bus.post(new TextChangedEvent(newText));

in fragment B:

EventBus bus = EventBus.getDefault();

//Register to EventBus
@Override
public void onCreate(SavedInstanceState savedState) {
 bus.register(this);
}

//catch Event from fragment A
public void onEvent(TextChangedEvent event) {
 yourTextView.setText(event.newText);
}
Recurrent answered 9/12, 2013 at 16:21 Comment(6)
Is the public class TextChangedEvent inside the MainActivity?Formica
No, it's a stand-alone class (of course, You could declare it as inner class of activity, if need (with public static modifiers), and refer to it as MainActivity.TextChangedEvent ).Recurrent
Of course, fragment B will only receive event if it's [fragment B] created already. So You need to set initial text in fragmennt's onCreate().Recurrent
The best approach everLaconic
Make sure you add the "@Subscribe" above the method you want to get the information from. Otherwise it'll crash.Accra
What will happen if event with data will come earlier than your fragments textfields will be initialized? It will crash?Penthea
S
7

Fragments have access to there parent Activity.
Therefore the simplest approach is to register a callback in the parent Activity.

Update: Submit cache added to MainActivity.

    public class MainActivity extends FragmentActivity {

        private OnButtonClicked mOnButtonClicked;
        private String mSubmitCache;

        public interface OnButtonClicked {
            void submit(final String s);
        }

        public void setOnButtonClicked(final OnButtonClicked c) {
            mOnButtonClicked = c;
            // deliver cached string, if any
            if (TextUtils.isEmpty(mSubmitCache) == false) {
                c.submit(mSubmitCache);
            }
        }

        public void buttonClicked(final String s)  {
            if (mOnButtonClicked == null) {
                // if FragmentB doesn't exist jet, cache value
                mSubmitCache = s;
                return;
            }
            mOnButtonClicked.submit(s);
        }
    }

    public class FragmentA extends Fragment implements OnClickListener {

        private MainActivity mMain;
        private Button mButton;

        @Override public onAttach(Activity a) {
            mMain = (MainActivity) a;
        }

        @Override public void onClick(View v) {
            mMain.buttonClicked("send this to FragmentB.");
        }
    }

    public class FragmentB extends Fragment implements MainActivity.OnButtonClicked {

        private MainActivity mMain;
        private TextView mTextView;

        // Called when the fragment's activity has been created
        // and this fragment's view hierarchy instantiated
        @Override public void onActivityCreated(Bundle savedState) {
            mMain = (MainActivity) getActivity();
            mMain.setOnButtonClicked(this);
        }

        @Override void submit(final String s) {
            mTextView.setText(s);
        }
    }
Spica answered 9/6, 2015 at 17:8 Comment(1)
I think this is a better approach then to use Broadcasts or any 3rd party library!Crane
W
3

I use Mr. Rodion's solution above. But in addition, Android Studio asked me to add @Subscribe annotation before onEvent method.

Like this:

@Subscribe
public void onEvent(TextChangedEvent event) {
    textView.setText(event.newText);
}

According to EventBus’ API:

Subscribers implement event handling methods (also called “subscriber methods”) that will be called when an event is posted. These are defined with the @Subscribe annotation. Please note that with EventBus 3 the method name can be chosen freely (no naming conventions like in EventBus 2).

Woundwort answered 1/5, 2016 at 14:57 Comment(0)
P
1

FragmentB is not even created until you switch to it so fragmentB.updateText(textPassToB); gives you NullPointerException.

You will need to store the text from the EditText in your activity and later when (if) the FragmentB is created you will need to read value from it.

Pewee answered 9/12, 2013 at 15:55 Comment(4)
I've edited the answer with suggestion how to solve.Pewee
You mean I'll pass the text from FragmentA to Activity. Then when I'm in the FragmentB, I'll get it from Activity?Formica
Exactly. Just provide gettter and setter for your instance variable in your activity and then in your fragmets use MyActivity act = (MyActivity) getActivity(); act.getMyVariable(). This is not the cleanest approach but for simple cases like yours it is acceptable .Pewee
Just click on the check mark on the left of the answer. It will become green.Pewee

© 2022 - 2024 — McMap. All rights reserved.