Reputation:
I'm trying to use View Pager with Fragment State Adapter. But getting a NullPointerException
when trying to invoke a Fragment method after creating its instance.
Here is the activity class and Adapter class:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview;
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_schedule);
ViewPager2 viewPager = findViewById(R.id.viewPager_AddScheduleActivity);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle()));
fragmentOverview = new FragmentOverview();
fragmentOverview.setTargetView(); //ERROR IS HERE
My view pager adapter is:
private class ViewPagerAdapter extends FragmentStateAdapter {
public ViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return fragmentOverview;
case 1:
return fragmentTodo;
default:
return null;
}
}
@Override
public int getItemCount() {
return 2;
}
}
My FragmentOverView class is:
public class FragmentOverview extends Fragment{
private FrameLayout mTargetFrameLayout;
private FrameLayout mDescriptionFrameLayout;
public FragmentOverview() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_overview, container, false);
mDescriptionFrameLayout = rootView.findViewById(R.id.descriptionView_frameLayout_OverviewFragment);
mTargetFrameLayout = rootView.findViewById(R.id.targetView_frameLayout_OverviewFragment);
return rootView;
}
public void setTargetView() {
mTargetFrameLayout.removeAllViews();
if (progress.maxProgress != null) {
//ADD A CHILD VIEW
mTargetFrameLayout.setVisibility(View.VISIBLE);
} else mTargetFrameLayout.setVisibility(View.GONE);
}
}
But when I call the setTargetView()
method after creating instance of FragmentOverview
, I'm getting a NullPointerException
. From log I noticed that the mTargetFrameLayout
is null.
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.FrameLayout.removeAllViews()' on a null object reference
I think this is because the activity lifecycle methods onCreate()
, onStart()
, onResume()
are called before the fragment lifecycle methods onCreate()
& onCreateView()
. But how to overcome this problem? Or am I trying to do in a wrong way?
Upvotes: 2
Views: 104
Reputation: 938
I think your assumption is right. The fragment layout is generated in onViewCreated()
of FragmentOverview
class. But this method start executing after the Activity
lifecycle methods onCreate()
, onStart()
and onResume()
are executed. You can checkout the lifecycle relation of Activity
and Fragment
by overwriting lifecycle methods and adding logs as mentioned by @Max_Hockeborn.
So you will always get the views i.e mTargetFrameLayout
null if you call setTargetView()
before executing onCreateView()
in FragmentOverview
.
To solve this, you can add a callback method that will be executed in parent class after executing the onCreateView()
of FragmentOverveiw
. And in that callback you can invoke setTargetView()
.
Here I'm providing a possible solution for your case:
In FragmentOverview
add this lines:
public class FragmentOverview extends Fragment{
//all your fields;
private OverviewCallbacks callbacks;
public FragmentOverview(OverviewCallbacks callbacks) {
this.callbacks = callbacks;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//inflate the rootView and find all the childViews;
//I think your codes are okey;
}
//Overwrite this method;
//You can also overwrite onViewCreated() lifecycle method;
//And add call this callback method from here;
@Override
public void onResume() {
super.onResume();
callbacks.onReach();
}
public void setTargetView() {
//Do what you want to do;
}
public interface OverviewCallbacks {
void onReach();
}
}
Thats all for FragmentOverView
class.
Now modify your Activity
class as follows:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview;
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
//Your codes
fragmentOverview = new FragmentOverview(new FragmentOverview.OverviewCallbacks() {
@Override
public void onReach() {
setTargetView();
}
});
}
}
Hope this will work. Comment if anything gone wrong.
Upvotes: 1
Reputation: 31
This is just a fast idea that I couldn't test yet. You could try to change this:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview;
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_schedule);
ViewPager2 viewPager = findViewById(R.id.viewPager_AddScheduleActivity);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle()));
fragmentOverview = new FragmentOverview();
fragmentOverview.setTargetView(); //ERROR IS HERE
to this:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview = new FragmentOverview();
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_schedule);
ViewPager2 viewPager = findViewById(R.id.viewPager_AddScheduleActivity);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle()));
fragmentOverview.setTargetView(); //ERROR IS HERE
To check your assumption that your problem is based on the call order you can add some logs and check the order they are called in with Logcat (given you use android studio) Something like this:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview;
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_schedule);
ViewPager2 viewPager = findViewById(R.id.viewPager_AddScheduleActivity);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle()));
Log.d("YOUR_TAG","Activity onCreate 1");
fragmentOverview = new FragmentOverview();
fragmentOverview.setTargetView(); //ERROR IS HERE
and
public class FragmentOverview extends Fragment{
private FrameLayout mTargetFrameLayout;
private FrameLayout mDescriptionFrameLayout;
public FragmentOverview() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_overview, container, false);
mDescriptionFrameLayout = rootView.findViewById(R.id.descriptionView_frameLayout_OverviewFragment);
mTargetFrameLayout = rootView.findViewById(R.id.targetView_frameLayout_OverviewFragment);
Log.d("YOUR_TAG","FragmentOverview onCreateView 1");
return rootView;
}
public void setTargetView() {
Log.d("YOUR_TAG","FragmentOverview setTargetView 1");
mTargetFrameLayout.removeAllViews();
if (progress.maxProgress != null) {
//ADD A CHILD VIEW
mTargetFrameLayout.setVisibility(View.VISIBLE);
} else mTargetFrameLayout.setVisibility(View.GONE);
}
}
The last one could be interesting for you to see if onCreateView (and the initiation of the variables that cause your problems) is finished before the setTargetView() function is called.
Upvotes: 0