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);
NPE
. Add log statements toonCreateView(...)
&onActivityCreated(...)
for both child & parent fragments:Log.i("ParentFrag", "onCreateView(...) :::: Parent")
... 3 more log statements. Check the execution sequence. I suspect that child'sonCreateView(...)
is called after parent'sonActivityCreated(...)
. I can offer a couple of solutions if you can verify this. – Jeannajeanneinterface
withvoid onTextChanged(String);
. Let the parent fragment implement thisinterface
and do its bit when this method is called:bAction.setEnabled(!TextUtils.isEmpty(text) && !TextUtils.isEmpty(text.trim()));
. ModifyMessageEditFragment.newInstance()
method to receive an instance of this interface. When the fragment is ready (onActivityCreated()
), have it call the interface method and update thebutton
state. This is a scalable solution: if you require 'more' communication between these fragments, add more methods. – Jeannajeanne