0

I'm trying to use a FrameLayout on one of my activities. I want this FrameLayout to be a placeholder in an activity and I want to add/replace/remove fragments inside it.

I have a simple cafe app used for studying android. For simplicity I will post the code for the Cake and Breads category.

Problem: NullPointerException. I don't know the reason WHY my fragment's view is null. I'm guessing maybe the fragment lifecycle didn't run so the view was not inflated thus accessing the view throws a nullpointer? Please help me understand WHY a nullpointer is thrown.

Flow of the App

User selects a specific cake or bread in a ListFragment > CakeAndBreadActivity is called > Display Cake or Bread specific detail

I'm looking for: the explanation and reason behind the NullPointerException in the view and How can I display the Cakes and Bread details into the fragment when using FrameLayout.

NullPointerException

--------- beginning of crash
10-25 01:41:21.550 2374-2374/com.cafe E/AndroidRuntime: FATAL EXCEPTION: main
                                                             Process: com.cafe, PID: 2374
                                                             java.lang.RuntimeException: Unable to start activity ComponentInfo{com.cafe/com.cafe.CakeAndBreadActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
                                                                 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
                                                                 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                 at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                 at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                 at android.os.Looper.loop(Looper.java:148)
                                                                 at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                              Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
                                                                 at com.cafe.CakeAndBreadDetailFragment.displayCakeAndBreadDetails(CakeAndBreadDetailFragment.java:97)
                                                                 at com.cafe.CakeAndBreadActivity.displayCakeAndBreadDetails(CakeAndBreadActivity.java:30)
                                                                 at com.cafe.CakeAndBreadActivity.onCreate(CakeAndBreadActivity.java:19)
                                                                 at android.app.Activity.performCreate(Activity.java:6237)
                                                                 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
                                                                 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
                                                                 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
                                                                 at android.app.ActivityThread.-wrap11(ActivityThread.java) 
                                                                 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
                                                                 at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                 at android.os.Looper.loop(Looper.java:148) 
                                                                 at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                                 at java.lang.reflect.Method.invoke(Native Method) 
                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

CakeAndBreadActivity

public class CakeAndBreadActivity extends Activity implements CakeAndBreadDetailFragment.onCakeAndBreadDetailFragmentListener {

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

    public void displayCakeAndBreadDetails(){

        Intent cakeAndBreadIntent = getIntent();
        int cakeAndBreadId = Integer.valueOf(cakeAndBreadIntent.getIntExtra("cakeAndBreadNumber", 0));

        CakeAndBreadDetailFragment cakeAndBreadDetail = new CakeAndBreadDetailFragment();

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        cakeAndBreadDetail.displayCakeAndBreadDetails(cakeAndBreadId);
        fragmentTransaction.replace(R.id.cake_and_bread_detail_frame_layout_id, cakeAndBreadDetail);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();

    }

    @Override
    public void onCakeAndBreadDetail(int id) {

    }
}

activity_cake_and_bread.xml

<?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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.cafe.CakeAndBreadActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/cake_and_bread_detail_frame_layout_id"/>

</LinearLayout>

CakeAndBreadDetailFragment

public class CakeAndBreadDetailFragment extends Fragment {

    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    private String mParam1;
    private String mParam2;

    private onCakeAndBreadDetailFragmentListener mListener;

    public CakeAndBreadDetailFragment() {
    }

    public static CakeAndBreadDetailFragment newInstance(String param1, String param2) {
        CakeAndBreadDetailFragment fragment = new CakeAndBreadDetailFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_cake_and_bread_detail, container, false);
    }

    public void onButtonPressed(int id) {
        if (mListener != null) {
            mListener.onCakeAndBreadDetail(id);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof onCakeAndBreadDetailFragmentListener) {
            mListener = (onCakeAndBreadDetailFragmentListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement onCakeAndBreadDetailFragmentListener");
        }
    }

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

    public interface onCakeAndBreadDetailFragmentListener {
        // TODO: Update argument type and name
        void onCakeAndBreadDetail(int id);
    }

    public void displayCakeAndBreadDetails(int id){
        CakeAndBread cakeAndBread = (CakeAndBread)CakeAndBread.getCakeAndBreadList().get(id);
        View cakeAndBreadView = getView();
        TextView cakeAndBreadTextView = (TextView)cakeAndBreadView.findViewById(R.id.cake_and_bread_detail_fragment_id);
        cakeAndBreadTextView.setText(cakeAndBread.getCakeAndBreadName() + " " + cakeAndBread.getCakeAndBreadNutritionFacts());
    }
}

fragment_cake_and_bread_detail.xml

<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"
    tools:context="com.cafe.CakeAndBreadDetailFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:id="@+id/cake_and_bread_detail_fragment_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         /> 
</LinearLayout>

EDIT: as suggested, the fragment was not completely loaded when I'm trying to access the view, thus the nullpointerexception is thrown. Now my problem is the framelayout or the fragment doesn't show the textview that I placed in the fragment, how do I solve this? I'm passing cake and bread id and then setting the text in the setText(...) method of the TextView.

zbryan
  • 805
  • 2
  • 12
  • 24
  • Possible duplicate of [What is a NullPointerException, and how do I fix it?](http://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – Chisko Oct 25 '16 at 01:58
  • I don't think so, I know what a NullPointerException is. My code throws a nullpointer because I'm trying to access an object that is null. What I don't understand is why my object is null. I think the culprit is the Fragment Lifecycle but I'm not sure. – zbryan Oct 25 '16 at 02:02
  • you are calling the function displayCakeAndBreadDetails before the fragment is load. you need to call the function after the fragment is loaded – ZeroOne Oct 25 '16 at 02:11

2 Answers2

3
    cakeAndBreadDetail.displayCakeAndBreadDetails(cakeAndBreadId);
    fragmentTransaction.replace(R.id.cake_and_bread_detail_frame_layout_id, cakeAndBreadDetail);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();

You are calling displayCakeAndBreadDetails(..) which calls displayCakeAndBreadDetails(..) which calls getView(). At that point you haven't inserted the fragment yet (happens after fragmentTransaction.commit();) so there is no view associated with the fragment. Hence getView() returns null.

Kiskae
  • 24,655
  • 2
  • 77
  • 74
  • I have placed the displayCakeAndBreadDetails(..) after the fragmentTransaction.commit(). Still the nullpointer is thrown. Does the Fragment loads completely after the fragmentTransaction.commit()? – zbryan Oct 25 '16 at 02:20
  • I moved the wrong method. Now it doesn't throw any exception, but why does the framelayout empty? I can't see my fragment's text view in the screen. – zbryan Oct 25 '16 at 02:26
  • The fragment lifecycle is a mess (See http://stackoverflow.com/a/36340059/1452094). The best approach would be to pass `cakeAndBreadId` to the fragment in the constructor and calling the method in the appropriate lifecycle callback (after onCreateView) instead of trying to guess from the outside. – Kiskae Oct 25 '16 at 02:27
1

@Kiskae is correct. You're calling cakeAndBreadDetail.displayCakeAndBreadDetails(cakeAndBreadId), when the fragment isn't ready which will result in a NullPointerException. Try:

public class CakeAndBreadActivity extends Activity implements CakeAndBreadDetailFragment.onCakeAndBreadDetailFragmentListener {
    public void displayCakeAndBreadDetails(){
        Intent cakeAndBreadIntent = getIntent();
        int cakeAndBreadId = Integer.valueOf(cakeAndBreadIntent.getIntExtra("cakeAndBreadNumber", 0));

        CakeAndBreadDetailFragment cakeAndBreadDetail = CakeAndBreadDetailFragment.newInstance(cakeAndBreadId);

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.cake_and_bread_detail_frame_layout_id, cakeAndBreadDetail);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
    }
}

public class CakeAndBreadDetailFragment extends Fragment {
    private static final String ARG_CAKE_AND_BREAD_ID = "ARG_CAKE_AND_BREAD_ID";

    public CakeAndBreadDetailFragment() {
    }

    public static CakeAndBreadDetailFragment newInstance(int cakeAndBreadId) {
        CakeAndBreadDetailFragment fragment = new CakeAndBreadDetailFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_CAKE_AND_BREAD_ID, cakeAndBreadId);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_cake_and_bread_detail, container, false);

        if (getArguments != null) {
            int cakeAndBreadId = getArguments.getInt(ARG_CAKE_AND_BREAD_ID, -1);
            if (cakeAndBreadId != -1) {
                displayCakeAndBreadDetails(cakeAndBreadId);
            }
        }

        return view;
    }

    public void displayCakeAndBreadDetails(int id){
        ...
    }
}
SunnySydeUp
  • 6,680
  • 4
  • 28
  • 32