I am trying to set a text for my textview every time I update the user interface.
public class CourseDetailsFragment extends Fragment {
private static final String TAG = "CourseDetailsFragment";
private OnFragmentInteractionListener listener;
private static CourseDetailsFragment instance;
protected @Bind(R.id.courseName)TextView courseName;
Course c;
View view;
public static CourseDetailsFragment getInstance(){
if (instance == null) {
instance = new CourseDetailsFragment();
}
return instance;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
}
@Override
public void onActivityCreated(Bundle savedInstance) {
super.onActivityCreated(savedInstance);
((AppCompatActivity)getActivity()).getSupportActionBar().setLogo(R.drawable.ic_overview);
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("Course Detail"); //redundant?
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_course_details, container, false);
ButterKnife.bind(this, view);
return view;
}
public void updateUI(Course cData) {
c = cData
courseName.setText(cData.getName());
}
}
This where I am trying to set text for my textview. However courseName
remains null
.
public void updateUI(Course cData) {
c = cData
courseName.setText(cData.getName());
}
This is where updateUI gets called.
public void onDataEntry(boolean saved, Fragment fragment, String identifier, Object T) {
if (T instanceof Course){
Course courseData = (Course)T;
Log.d(TAG, "onDataEntry: " + courseData.getName() + " " + courseData.getGrade());
CourseDetailsFragment.getInstance().updateUI(courseData);
}
}
This is my stacktrace:
02-19 17:31:56.317 4689-4689/hsleiden.nl.barometer E/AndroidRuntime: FATAL EXCEPTION: main
Process: hsleiden.nl.barometer, PID: 4689
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at hsleiden.nl.barometer.views.fragment.CourseDetailsFragment.updateUI(CourseDetailsFragment.java:101)
at hsleiden.nl.barometer.views.activity.BaseActivity.onDataEntry(BaseActivity.java:306)
at hsleiden.nl.barometer.adapter.CourseAdapter$1.onClick(CourseAdapter.java:57)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
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)
The problem is this line:
CourseDetailsFragment.getInstance().updateUI(courseData);
The thing is, your @butterknife.Bind
annotated TextView
will remain null until the call to ButterKnife.bind(this, view)
. To make that call, you need a valid parent of your view hierarchy, which you can obtain no earlier than when onCreateView
lifecycle method is called, hence the TextView
is null until onCreateView
. Now, onCreateView
is called after onCreate
, which in turn is called after onAttach
, which is triggered by attaching the fragment to an Activity
, usually through the use of FragmentManager
. Calling CourseDetailsFragment.getInstance().updateUI(courseData)
does not automatically trigger the fragment lifecycle, it only instantiates the Fragment
instance, and as until that Fragment
is attached to an Activity
and onCreateView
is called, your courseName
is a null reference, which means that calling the updateUi
method instantly after creation of the Fragment
is doomed to result in a null pointer exception.
The only option that this line of code would work without throwing exception is if this Fragment
had been previously instantiated, attached to an activity and it had gone through the onCreateView
method.
For reasons described above, I don't recommend implementing fragments as singletons, especially if you want to make direct calls on a reference to such fragment from other components. Due to the lifecycles, Activities and Fragments are state machines in practice, and your updateUi
method doesn't consider that state, which is the root of your problem.
For a quick fix, you can override the onFragmentsResumed
method in your Activity
- if the fragment was previously attached, your references should be valid at that point.