Communication from Activity to Fragment with ViewPager
You need to set up some event listener logic where the fragment registers with the activity for the event it wants. The fragment should register on creation and unregister on destruction.
Then when the event occurs, the activity goes through the list of registered listeners and notifies them of the event.
I give a detailed example in this answer.
You can use an event bus library to simplify some of the wiring between the activity and the fragments.
Zuop
Updated on June 13, 2022Comments
-
Zuop almost 2 years
I have one Activity and two Fragments for a
tablayout
containing aviewpager
. Now I can communicate from the fragment to the Activity by implementing the google's guide callback interface. But how can I communicate the other way from activity to fragment? If something happens in the Activity (external events) I want to update the Fragment. I managed to get the Frag1 fragment withMyFragmentPagerAdapter a = (MyFragmentPagerAdapter) viewPager.getAdapter(); Frag1 frag = (Frag1) a.getItem(0);
but when I call public methods on frag I get a IllegalStateException: Fragment not attached to Activity probably because getItem(0) returns a new instance of Frag1 and this is not attached yet... is there anyone who can provide a clean solution for this whole Viewpager -> Activity to Fragment communication?
Some code for you:
in Activity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); if (viewPager != null) { viewPager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager())); } TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs); if (tabLayout != null) { tabLayout.setupWithViewPager(viewPager); } }
activity layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" tools:context="com.MainActivity"> <android.support.design.widget.TabLayout android:id="@+id/sliding_tabs" android:layout_width="match_parent" android:layout_height="wrap_content"/> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1"/> </LinearLayout>
MyFragmentPagerAdapter:
public class MyFragmentPagerAdapter extends FragmentPagerAdapter { final int PAGE_COUNT = 2; private String tabTitles[] = new String[] { "tab1", "tab2" }; public MyFragmentPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return Frag1.newInstance(position + 1); case 1: return Frag2.newInstance(position + 1); default: return null; } } @Override public int getCount() { return PAGE_COUNT; } @Override public CharSequence getPageTitle(int position) { return tabTitles[position]; } }
Frag1:
public class Frag1 extends Fragment { public static final String ARG_PAGE = "ARG_PAGE"; private int mPage; private onFrag1InteractionListener mListener; public Frag1() { // Required empty public constructor } /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @return A new instance of fragment Frag1. */ public static Frag1 newInstance(int page) { Frag1 fragment = new Frag1(); Bundle args = new Bundle(); args.putInt(ARG_PAGE, page); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mPage = getArguments().getInt(ARG_PAGE); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_1, container, false); return view; } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof onFrag1InteractionListener) { mListener = (onFrag1InteractionListener) context; } else { throw new RuntimeException(context.toString() + " must implement onFrag1InteractionListener"); } } @Override public void onDetach() { super.onDetach(); mListener = null; } public interface onFrag1InteractionListener { // TODO: Update argument type and name void onFrag1Interaction(Action action); }
-
Nitesh almost 8 yearsextend AppCompatActivity instead of Activity then check what is happening?
-
Zuop almost 8 yearsActivity already extends AppCompatActivity
-
Nitesh almost 8 yearsWhat do you mean by external events? Suppose if you want to update the fragment when tabs are swiped. Like that
-
hanbumpark almost 8 yearsmaybe you can find solution here. stackoverflow.com/questions/8532462/…
-
Zuop almost 8 yearsBy external events I thought of events that are not connected with the fragments in fact not with the user, for example if a new wifi is available the activity will notice and wants to communicate this to the fragment. @Hanbum Bak this doesn't help because I have no xml fragment, just the fragment layout itself and the fragment class (Frag1 extends Fragment)
-
Raghavendra almost 8 yearsyou can get which fragment which loaded currently. Otherwise I think you can use FragmentStatePagerAdapter and use getItemPosition in that adapter with POSITON_NONE. Better ref: here
-
Zuop almost 8 yearshow do i get the current loaded fragment? or how do i show frag1 if frag2 is showing and update a view on frag1
-
Raghavendra almost 8 yearsu have fragmentPagerAdapter reference in activity right. use adapter.getItem(viewPager.getCurrentItem()) to get the current fragment.
-
Swaminathan V almost 8 yearsDo you want to send some data to the fragment based on some events in its parent activity. am i correct ?? @Zuop
-
Zuop almost 8 years@Raghavendra i tried that: MyFragmentPagerAdapter adapter = (MyFragmentPagerAdapter) viewPager.getAdapter(); final Frag1 frag1 = (Frag1) adapter.getItem(viewPager.getCurrentItem()); but when i call the public method of the frag1 I get the illegalstateexception at Ragu Swarminathan no i dont want to send data, i just need the reference of the fragment so that i can call a method from the fragment
-
Swaminathan V almost 8 yearsDo you want to refresh the fragment when some events happens in its mainactivity? Does my question was correct ? @Zuop
-
Raghavendra almost 8 yearsI'm just guessing can u just comment this newInstance() method and use default constructor to get the fragment reference and try once.?
-
Zuop almost 8 yearsto be specific: the fragment contains a Imageview and with the method in Frag1
public void swapImage(boolean b)
i want to set another imagesource if b is true. -
Zuop almost 8 yearsI followed this tutorial guides.codepath.com/android/… and they used this newInstance method
-
-
Zuop almost 8 yearsI followed the instructions in the link of your answer and that solved it.. though I find it weird to implement a Interface to communicate from Fragment -> Activity and then additionally another interface to communicate from Activity -> Fragment
-
kris larson almost 8 yearsMy example didn't implement an interface on the activity, but doing that is still good practice for unit testing and reuse. Lots of Google's examples for things like navigation drawer do this.
-
Zuop almost 8 yearsOh okay.. I must have misinterpret your answer but nvm it works for now. Though you said "Define a listener interface. I usually do this as an inner interface within the activity" in the link :)
-
kris larson almost 8 yearsInstead of having a separate, standalone interface definition, I define the interface inside the activity, similar to an inner class. This makes sense because it's the activity that has the collection of listener impls that will receive the event callback. So the activity defines the interface, the fragments implement the interface, e.g.
extends Fragment implements MyActivity.EventListener
. Hopefully that makes it all a little clearer.