1

I am having troubling sending data from one fragment to another fragment. I was following the android guideline which tells me to create an interface to communicate with activity and fragments. I'm using the bottom navigation to switch over fragments. Anyway, here's the error from android studio:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.makkhay.cameratranslate.Favorite.displayReceivedData(java.lang.String)' on a null object reference
        at com.example.makkhay.cameratranslate.HomeActivity.sendData(HomeActivity.java:143)
        at com.example.makkhay.cameratranslate.HomeFragment$2.onClick(HomeFragment.java:162)
        at android.view.View.performClick(View.java:6256)
        at android.view.View$PerformClick.run(View.java:24697)
        at android.os.Handler.handleCallback(Handler.java:789)

For some reason, my fragments are always null even after initializing it. I tried initializing it both ways, onCreate method, and the sendData method, the instance always seems to be null. I think the main culprit might be the DrawerLayout. Since it is the parent xml and there is another xml called "app_bar_home.xml"; which is hosting the two fragments using bottom navigation, the newly created instances are always null

Here's my main activity which is holding two fragments HomeActivity.java

public class HomeActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener,HomeFragment.SendMessage {

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            Fragment selectedFragment = null;
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    selectedFragment = new HomeFragment();
                    break;
                case R.id.navigation_favorite:
                    selectedFragment = new Favorite();
                    break;
            }

            getSupportFragmentManager().beginTransaction().replace(R.id.app_bar,
                    selectedFragment).commit();

            return true;
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

        //I added this if statement to keep the selected fragment when rotating the device
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.app_bar,
                    new HomeFragment()).commit();
        }




    }

 @Override
    public void sendData(String message) {
        Favorite f = (Favorite) getSupportFragmentManager().findFragmentById(R.id.favFragment);
        f.displayReceivedData(message);
    }
}

Here's my xml for my activity activity_home.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        android:id="@+id/app_bar"
        layout="@layout/app_bar_home"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />




    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_home"
        app:menu="@menu/activity_home_drawer" />




</android.support.v4.widget.DrawerLayout>

Here's the app_bar_home.xml which is also part of the activity

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HomeActivity">



    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        >

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            app:layout_collapseMode="pin"
            android:id="@+id/toolbar"
            />

    </android.support.design.widget.AppBarLayout>


    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="#000"
        app:itemIconTint="@color/color_selector"
        app:itemTextColor="@color/color_selector"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>

Here's the fragment 1 from which I want to send the data to another fragment. HomeFragment.java

public class HomeFragment extends Fragment implements View.OnClickListener{
SendMessage SM;
private Button clearButton, favButton, shareButton;

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_home, container, false);

return v;
}

 @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        favButton = view.findViewById(R.id.favButton);

        favButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SM.sendData(" test");
            }
        });

    }



    interface SendMessage {
        void sendData(String message);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        try {
            SM = (SendMessage) getActivity();
        } catch (ClassCastException e) {
            throw new ClassCastException("Error in retrieving data. Please try again");
        }
    }


}

HomeFragment.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/homeFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HomeFragment"
    >


    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            app:layout_collapseMode="pin" />
 </android.support.design.widget.AppBarLayout>



        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="6dp"
            android:layout_marginEnd="6dp"
            android:layout_marginLeft="6dp"
            android:layout_marginRight="6dp"
            android:layout_marginStart="6dp"
            android:layout_marginTop="56dp"
            android:background="@drawable/card_shadow"
            android:divider="?android:dividerHorizontal"
            android:paddingTop="6dp"
            android:paddingBottom="6dp"
            android:showDividers="middle">




                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_weight="1"
                            android:paddingBottom="16dp"
                            android:gravity="right"
                            android:orientation="vertical">

                            <Button
                                android:id="@+id/favButton"
                                android:layout_width="48dp"
                                android:layout_height="48dp"
                                android:layout_marginTop="4dp"
                                android:background="@drawable/ic_favorite_black_12dp"
                                />

                        </LinearLayout>


                    </LinearLayout>






    </android.support.constraint.ConstraintLayout>



</android.support.constraint.ConstraintLayout>

Finally here's the 2nd fragment where I want to recieve the data Favorite.java

public class Favorite extends Fragment {

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

        View v = inflater.inflate(R.layout.fragment_favorite, container, false);
        favText = v.findViewById(R.id.tv_recycler_item_1);
        return  v;
    }

    protected void displayReceivedData(String message)
    {
        Toast.makeText(getContext(),"rec:" + message,Toast.LENGTH_SHORT).show();
    }

}

fragment_favorite.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/favFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Favorite">


    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_scrolling"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/bg_red" />


            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/Widget.AppCompat.PopupMenu.Overflow" />

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>


    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"

        >

        <TextView
            android:id="@+id/txtData"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="548dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/recycler_view_recycler_view"
            app:layout_constraintStart_toStartOf="parent" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view_recycler_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="382dp"
            android:layout_marginEnd="344dp"
            android:layout_marginRight="344dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">

        </android.support.v7.widget.RecyclerView>



    </android.support.constraint.ConstraintLayout>


</android.support.design.widget.CoordinatorLayout>

Let me know if you need more info. thanks in advance :)

makkhay gurung
  • 528
  • 1
  • 4
  • 15

4 Answers4

0

you can pass data between fragment by putting as argument in bundle like following:

Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);

For retrieving you can do following

Bundle bundle = this.getArguments();
if (bundle != null) {
        int myInt = bundle.getInt(key);
}
patel dhaval r
  • 1,237
  • 1
  • 8
  • 17
  • But can I use bundle to send a data when both fragments are nested inside one activity? I'm using bottom navigation here. Thanks for replying. – makkhay gurung Apr 07 '18 at 04:21
  • In that case you can do that make one method in activity and access that method in both fragment. For Example like this https://stackoverflow.com/questions/12659747/call-an-activity-method-from-a-fragment – patel dhaval r Apr 07 '18 at 04:25
  • Yeah, I have seen that question and I went through it. – makkhay gurung Apr 07 '18 at 05:33
0

On my view best way of communication is by using EventBus between Activity-fragment, fragment-fragment, Activity-Activty, very easy and less complicated

Here is an Example Create a model of what you want to share, so here is model for sending string declare the string variable use constructor and getter for setting and getting message

public MessageEvent(String Message)
{
    this.msg=msg;
}
 public String getMsg() {
    return msg;}

Now in your fragment 1 post the message before that register eventbus on OnAttach and deregister it in OnDettach

 EventBus.getDefault().post(new MessageEvent("My Message));

In Fragment 2 get the message in OnEvent Method(even in this fragment don't forget to register and deregister Event Bus)

@Subscribe
public void onEvent(MessageEvent event) {
        this.msg=event.getMsg();
        callTheMethodToDoYourOperation(msg);
}

This a basic example, but check about event bus before using this and moreover in both fragment you require to Register, deregister and subscribe onevent, and if this is one way communication let onEvent be empty

Hope this helps

Ashish Sharma
  • 566
  • 5
  • 20
0

The NullPointerException occurred because your Fragment is not initialized yet, so before interacting with is make sure you check is it initialized and visible or not.

if (f!= null && f.isVisible()) {
   f.displayReceivedData(message);
}

You can also use Bundle to pass data between Fragment too.

Note: Before posting question understand the error and try to debug the root cause of the error. You'll definitely solve it on your own.

Happy coding :)

Akshay
  • 6,029
  • 7
  • 40
  • 59
  • Thanks for replying. I added your line of code to check for null. Looks like my fragment hasn't been initialized properly. As a result, the data I'm passing isn't being received by another fragment. Will continue to work tomorrow. Time for bed :) – makkhay gurung Apr 07 '18 at 07:56
  • @makkhaygurung is my answer worked for you please accept it or you can up vote it :) – Akshay Apr 07 '18 at 10:36
  • For some reason, my fragments are always null even after initializing it. I tried initializing it both ways, onCreate method, and the sendData method, the instance always seems to be null. I think the main culprit might be the DrawerLayout. Since it is the parent xml and there is another xml called "app_bar_home.xml"; which is hosting the two fragments using bottom navigation, the newly created instances are always null. – makkhay gurung Apr 07 '18 at 16:27
0

You are getting Null pointer on method where you receive data, that fragment instance is null. Favorite f is null. Create one instance if that fragment and then call .replace

dev
  • 260
  • 2
  • 12
  • If I do replace, doesn't it change one fragment with another? I don't want to replace the current fragment when I clicked the button. – makkhay gurung Apr 07 '18 at 07:58