7

I have a Fragment with a method setName() that changes an EditText text, by means of the setText function.

What is the best way to call that method from the activity that hosts that fragment by means of a ViewPager?

In other words, how can I access a Fragment's methods (which change that fragment's layout, for example) from the Activity that hosts that fragment by means of a ViewPager?

I am asking this because I have tried several ways, but always with errors.

JZweige
  • 721
  • 2
  • 9
  • 15
  • You can try to create an instance variable of the Fragment with the `setName()` method in your Activity class. Assign the variable to the Fragment when you instantiate it. you can then call `fragment.setName()` from your activity – Scott Jul 16 '13 at 20:05
  • I have this problem, but calling fragment.setName() via a stored reference to it, isn't the problem. The problem is that from the fragment, the getActivity() returns null so that views cannot be accessed. I can access the views from the activity, though, and the fragment COULD put info into the views belonging to the fragment at configuration. But later, the getActivity() returns null and could it be something with the viewPager taking control of context so that getActivity is actually not valid? So one would need to request the view from the Viewpager? I am asking. – carl Mar 06 '14 at 22:21

4 Answers4

12

Best way to do this, just call

CallingFragmentName fragment = (CallingFragmentName) viewPager
                    .getAdapter()
                    .instantiateItem(viewPager, viewPager.getCurrentItem());

It will re-instantiate your calling Fragment, so that it will not throw null pointer exception.

Zain
  • 37,492
  • 7
  • 60
  • 84
Ritesh Adulkar
  • 841
  • 11
  • 18
7

I know this is a little late, but I ran into the same problem and maybe it will help others if you already solved it.

The first problem I found with ViewPager is that it is almost impossible to get a reference to a fragment. The fragments are created dynamically in getItem() and therefore you can't set an ID and they are automatically re-taged by the swicher, so you can't find it by tag either. There are some ways out there to do it, but they are all workarounds. (Update data in ListFragment as part of ViewPager)

The way I solved it was using essentially a double Callback. Fragment A has an interface implemented by the Main Activity, the Main Activity has a interface implemented by Fragment B. On e.g. a button clink in Fragment A the callback function in Main Activity is called, which than in turn calls the callback in Fragment B. Look at the code below. I hope I posted everything and it will help. btw, I have only tried this with a ViewPager, but I assume it would work with any sort of Fragment communication.

Main Avtivity java:

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

public class MainActivity extends FragmentActivity implements FragmentA.Caller {

    SectionsPagerAdapter mSectionsPagerAdapter;
    ViewPager mViewPager;
    PassCallToB passOnToB = null;
    FragmentManager myManager = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);
    }

    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
            MyManager = fm;
        }

        @Override
        public Fragment getItem(int position) {
            Fragment fragment = null;
            if(position == 0) {
                fragment = new FragmentA();
            } else if (position == 1) {
                fragment = new FragmentB();
                passOnToB = (PassCallToB)fragment;
            }
            return fragment;
        }

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

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
            case 0:
                return "Frag A";
            case 1:
                return "Frag B";
            }
            return null;
        }

        public void setCallback() {
            List<Fragment> frags = myManager.getFragments();
            for(Fragment fragment : frags) {
                if(fragment instanceof FragmentB){
                    passOnToB = (PassCallToB)fragment;
                }
            }
        }
    }

    public interface PassCallToB {
        public void passItOn();
    }

    @Override
    public void CallB() {
        if(passOnToB instanceof Fragment)
            passOnToB.passItOn();
        else {
            mSectionsPagerAdapter.setCallback();
            passOnToB.passItOn();
        }
    }
}

Main Activity xml:

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <android.support.v4.view.PagerTitleStrip
        android:id="@+id/pager_title_strip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:background="#33b5e5"
        android:paddingBottom="4dp"
        android:paddingTop="4dp"
        android:textColor="#fff" />

</android.support.v4.view.ViewPager>

Fragment A java:

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

public class FragmentA extends Fragment {

    Button btnCallB = null;

    Caller listener = null;

    public FragmentA() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle inState) {
        View rootView = inflater.inflate(R.layout.fragment_a, container, false);

        btnCallB = (Button)rootView.findViewById(R.id.btnCallB);
        btnCallB.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                listener.CallB();
            }

        });

        return rootView;
    }

    public interface Caller {
        public void CallB();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (activity instanceof FragmentActivity) {
          listener = (Caller) activity;
        } else {
          throw new ClassCastException(activity.toString() + " must implemenet listener");
        }
    }

     @Override
      public void onDetach() {
        super.onDetach();
        listener = null;
      }
}

Fragment A xml:

<RelativeLayout 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" >


    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="This is Fragment A" />

    <Button
        android:id="@+id/btnCallB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView1"
        android:text="Call Fragment B" />

</RelativeLayout>

Fragment B Java:

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.Toast;

public class FragmentB extends Fragment implements MainActivity.PassCallToB {

    public FragmentB() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle inState) {
        View rootView = inflater.inflate(R.layout.fragment_b, container, false);
        return rootView;
    }

    @Override
    public void passItOn() {
        Toast.makeText(getActivity(), "Hello from B", Toast.LENGTH_SHORT).show();

    }
}

Fragment B xml:

<RelativeLayout 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" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="This is Fragment B" />

</RelativeLayout>
Community
  • 1
  • 1
ckn
  • 141
  • 1
  • 8
  • This does not work when you rotate. Because when you rotate the activity and fragments are recreated which also recreates your SectionsPagerAdapter but it never calls getItem (Android recreates the fragments without calling getItem) meaning passOnToB remains null. – rve Nov 28 '14 at 10:12
  • I edited my code so it will work after screen rotation. The trick is to set the callback not only in getItem(), but also include a custom method in the pager adapter that will search trough the fragments and set the callback manually, when the correct fragment is found. Now you only have to check if the callback is set and set it if not in the passOn method. I hope this is somewhat clear. – ckn Dec 31 '15 at 05:22
  • Any idea with mine? http://stackoverflow.com/questions/39907625/why-is-accessing-textview-of-a-fragment-inside-activity-throwing-an-error – Si8 Oct 07 '16 at 00:35
3

You can access public methods within the fragments held by your ViewPager. You need to either (1) store a reference to the Fragment when you create it and add it to the list that will back your pager adapter or (2) you need to get a reference to the fragment from the pager adapter itself. For example:

Fragment fragmentA = null; //instance variable

fragmenA = new Fragment(); //whereever you instantiate your fragment

If your method is

public void setName(String args){
    //do something
}

all you would do is call that method from the reference to the fragment held by your ViewPager

fragmentA.setName(args);

You pass whatever arguments you need just like calling a regular method. Note this ONLY works if you are calling a method within a fragment from its containing ViewPager or FragmentActivity. If you want to do the reverse, fragment to activity, you need to use an inerface.

Rarw
  • 7,645
  • 3
  • 28
  • 46
  • Yes, I know this already. But what if I am trying to instantiate it using something like Frag1 fragment = (Frag1) mAppSectionsAdapter.getItem(0);? How I am supposed to use the method then without the null pointer exception? – JZweige Jul 16 '13 at 20:22
  • It's like fragment.getView() is always null! – JZweige Jul 16 '13 at 20:23
  • You want to get the reference from your adapter? I'm not at my computer but I can post code later if that's the issue. – Rarw Jul 17 '13 at 00:22
  • Any help with mine please... http://stackoverflow.com/questions/39907625/why-is-accessing-textview-of-a-fragment-inside-activity-throwing-an-error – Si8 Oct 07 '16 at 00:38
  • Your answer always works for me in view pager thankyou man , only for you i starred the question now.. Thanx man :) – Sumit Kumar Jun 02 '17 at 10:44
  • this does not work . Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference – Yaroslav Dukal Nov 10 '17 at 23:29
2

Fragment

private static FragmentName instance;

public static synchronized FragmentName getInstance()
{
    return instance;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance=this;
....
}

public void methodName()
{...}

Activity

FragmentName.getInstance().methodName();
Android Developer
  • 9,157
  • 18
  • 82
  • 139