onCreateView() of nested fragment is not called
Asked Answered
M

2

2

I have been going through nested fragments documentation, best practices and all possible links here in StackOverflow. I have seen suggestions to avoid using fragment tag in layout files and instead add them via transaction for seamless transitions.

Before implementing it in my app, I tried a simple example and I found some unexpected behavior, child fragments onCreateView() is not getting called. I assume that , I am missing something in my understanding, so I am looking for some advice/help to sort this out.

The example flow is as below:--

MainActivity hosts a fragment(MessageFragment) and the MessageFragment hosts a child fragment(MessageTextFragment).

I add the child fragment programatically using fragment transaction. But onCreateView of the child fragment never gets called, because of which the sub-view is not inflated.

I am using v4 support library for fragments everywhere Here is my complete code:--

MainActivity file:--

 public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.root_layout);
            FragmentManager fragmentManager = getSupportFragmentManager();
            Fragment fragment = null;
            if (savedInstanceState == null) {
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragment=new MessageFragment().newInstance();
                fragmentTransaction.add(R.id.fragment_container, fragment);
                fragmentTransaction.commit();
            }
        }
}

layout file for MainActivity:-

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/wizard_layout_root"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:orientation="vertical">
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"/>
</RelativeLayout>

The first Fragment is: MessageFragment--

public class MessageFragment extends Fragment {
    private EditText msgText;

    private Activity activity;

    TextView tvTitle, tvContent, tvIntro;
    Button bAction;

    public static MessageFragment newInstance() {
        MessageFragment f = new MessageFragment();
        return (f);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.message, container, false);
        tvTitle = (TextView) view.findViewById(R.id.fragment_title);
        tvIntro = (TextView) view.findViewById(R.id.fragment_intro);
        tvContent = (TextView) view.findViewById(R.id.fragment_contents);
        bAction = (Button) view.findViewById(R.id.fragment_action);

        Fragment fragment = getChildFragmentManager().findFragmentById(R.id.sms_message);
        if (fragment == null) {
            Log.d("MESSAGE_FRAGMENT", "definetly inside");
            fragment = MessageEditFragment.newInstance();
            FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
            transaction.add(R.id.sms_message, fragment);
            transaction.commit();
            getChildFragmentManager().executePendingTransactions();
            Log.d("MESSAGE_FRAGMENT", "" + getChildFragmentManager().findFragmentById(R.id.sms_message));
        }
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        activity = getActivity();
        if (activity != null) {
            Fragment fragment = getChildFragmentManager().findFragmentById(R.id.sms_message);
            Log.d("MESSAGE_FRAGMENT", "onActivityCreated" + fragment);
            msgText = (EditText) ((MessageEditFragment)fragment).getView().findViewById(R.id.message_edit_text);
            bAction.setEnabled(!msgText.getText().toString().trim().equals(""));
            msgText.selectAll();
        }
    }
}

Layout of MessageFragment(Framelayout is the container for child fragment)

<?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="fill_parent"
    android:layout_height="fill_parent"
    android:descendantFocusability="beforeDescendants"
    android:focusableInTouchMode="true"
    android:orientation="vertical">
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/fragment_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"/>

            <TextView
                android:id="@+id/fragment_intro"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/fragment_title" />

            <LinearLayout
                android:id="@+id/ll_message"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/fragment_intro"
                android:layout_weight="1">
                <FrameLayout
                    android:id="@+id/sms_message"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent" />
            </LinearLayout>
            <Button
                android:id="@+id/fragment_action"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/ll_message"/>
        </RelativeLayout>
    </ScrollView>
</LinearLayout>

This is the child fragment

public class MessageEditFragment extends Fragment {

    private EditText messageEditText;

    private MessageLimitWatcher messageLimitWatcher;
    private int maxCharacters;
    private String messageHeader;
    private Button bAction;

    public static MessageEditFragment newInstance() {
        MessageEditFragment f = new MessageEditFragment();
        return(f);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d("MESSAGE_FRAGMENT", "onCreateView Of nested fragment");
        View view = inflater.inflate(R.layout.message_fragment, container, false);
        messageEditText = (EditText) view.findViewById(R.id.message_edit_text);
        messageEditText.requestFocus();
        return view;
    }

and its layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent">
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="130dp">
        <EditText
            android:id="@+id/message_edit_text"
            android:layout_height="130dp"
            android:layout_width="fill_parent" />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"/>
    </RelativeLayout>
</LinearLayout>

Logcat never prints the line:--

Log.d("MESSAGE_FRAGMENT", "onCreateView Of nested fragment");

and I get a nullpointer exception when I try to use the text field of child fragment, as it is not inflated.So basically, I get nullpointer at:--

((MessageEditFragment)fragment).getView().findViewById(R.id.message_edit_text);

Mckamey answered 27/8, 2015 at 3:21 Comment(7)
Firstly, comment out the code that's throwing NPE. Add log statements to onCreateView(...) & onActivityCreated(...) for both child & parent fragments: Log.i("ParentFrag", "onCreateView(...) :::: Parent")... 3 more log statements. Check the execution sequence. I suspect that child's onCreateView(...) is called after parent's onActivityCreated(...). I can offer a couple of solutions if you can verify this.Jeannajeanne
@Jeannajeanne , now that is a relief. You are right, I followed, what you stated and this was the order:-- onCreateView() parent called, onActivityCreated parent called,onCreateView of nested fragment and then onActivityCreated of nested fragment called. So this means, I am trying to access the child fragment earlier than its inflated. Can you suggest me your further view points, as I need the child fragments fields in the parents onActivityCreate.Mckamey
@Vikram, I am so very thankful to you,for looking into this.Further to this, I went other way around. So I was using parentFragment fields in onActivitycreated() of childFragment. using getParentFragment(). Now, my app is targeted to API level <16 also, where this getFragmentManager() is not supported. So, thats where I need your inputs. Is this link the correct way to access the parent fields??:-- https://mcmap.net/q/95199/-getparentfragment-api-16Mckamey
Also, please add your answer, I will be more than happy to accept it.Mckamey
seems getParentFragment() is available for fragment support library also. But I am really more willing to hear your offered solutions.Mckamey
How I would go about solving this: declare an interface with void onTextChanged(String);. Let the parent fragment implement this interface and do its bit when this method is called: bAction.setEnabled(!TextUtils.isEmpty(text) && !TextUtils.isEmpty(text.trim()));. Modify MessageEditFragment.newInstance() method to receive an instance of this interface. When the fragment is ready (onActivityCreated()), have it call the interface method and update the button state. This is a scalable solution: if you require 'more' communication between these fragments, add more methods.Jeannajeanne
@Vikram,I got your point.If you can add your answer, I will accept it, anyhow I was able to solve my issue because of your help. Thanks again.Mckamey
S
0

Don't call the transactions inside of onCreateView either use onActivityCteated or onCreate.

Also worth noting don't call, executePendingTransactions when you are inside a of the parent fragment, that is bound to break something.

Try to setup your parent fragment something like this:

public class MessageFragment extends Fragment {
  private EditText msgText;

  private Activity activity;

  TextView tvTitle, tvContent, tvIntro;
  Button bAction;

  public static MessageFragment newInstance() {
    MessageFragment f = new MessageFragment();
    return (f);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.message, container, false);
    tvTitle = (TextView) view.findViewById(R.id.fragment_title);
    tvIntro = (TextView) view.findViewById(R.id.fragment_intro);
    tvContent = (TextView) view.findViewById(R.id.fragment_contents);
    bAction = (Button) view.findViewById(R.id.fragment_action);
    return view;
  }

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    activity = getActivity();
    if (activity != null) {
        Fragment fragment = getChildFragmentManager().findFragmentById(R.id.sms_message);
        Log.d("MESSAGE_FRAGMENT", "onActivityCreated" + fragment);
        msgText = (EditText) ((MessageEditFragment)fragment).getView().findViewById(R.id.message_edit_text);
        bAction.setEnabled(!msgText.getText().toString().trim().equals(""));
        msgText.selectAll();
    }
    Fragment fragment = MessageEditFragment.newInstance();
    getChildFragmentManager().beginTransaction()
      .replace(R.id.sms_message, fragment)
      .commit();
  }
}
Siana answered 2/9, 2015 at 6:32 Comment(3)
Let me try this, I really appreciate your timeMckamey
This is literally strange, I tried in onCreate and also the same onActivityCreated but everywhere same null pointer. msgText field is defined in the child fragment and I am trying to use that in parent fragment, assuming, it will be available once the child fragment is inflated. But hard luck for me. oncreatVies of child fragment is not at all called...Mckamey
In case you can just give a try by simply copy pasting this code and if you can find what I am missing, will be a great favorMckamey
T
-2

Maybe you should not add child fragment(say call transaction.add within a fragment).

Do fragment transaction only on Activity.

When you want to do fragment transaction, you could notify the Activity hosting the current fragment, through EventBus, or an interface implemented by the host Activity.

Timmy answered 5/9, 2015 at 16:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.