英文:
Views of Fragment Layout causing NullPointerException
问题
我正在尝试使用带有Fragment State Adapter的View Pager。但在创建实例后尝试调用片段方法时出现“NullPointerException”。
以下是活动类和适配器类:
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
我的视图分页适配器是:
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;
}
}
我的FragmentOverView类是:
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);
}
}
但在创建FragmentOverview
实例后调用setTargetView()
方法时,我收到了“NullPointerException”。从日志中我注意到mTargetFrameLayout
为空。
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.FrameLayout.removeAllViews()' on a null object reference
我认为这是因为活动的生命周期方法onCreate()
、onStart()
、onResume()
在片段的生命周期方法onCreate()
和onCreateView()
之前被调用。但是如何解决这个问题呢?或者我是不是在错误的方式中尝试做这件事?
英文:
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?
答案1
得分: 1
我认为你的假设是正确的。片段布局是在FragmentOverview
类的onViewCreated()
方法中生成的。但是这个方法在执行Activity
生命周期方法onCreate()
、onStart()
和onResume()
之后开始执行。你可以通过覆盖生命周期方法并按照@Max_Hockeborn的建议添加日志来查看Activity
和Fragment
之间的生命周期关系。
因此,如果在FragmentOverview
的onCreateView()
执行之前调用setTargetView()
,你将始终得到视图,即mTargetFrameLayout
为空。
为了解决这个问题,你可以在父类中添加一个回调方法,在执行FragmentOverveiw
的onCreateView()
后执行该回调。在回调中,你可以调用setTargetView()
。
下面是你的情况的一个可能解决方案:
在FragmentOverview
中添加以下代码:
public class FragmentOverview extends Fragment{
//所有的字段;
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) {
//填充rootView并找到所有的子视图;
//我认为你的代码是正确的;
}
//覆盖这个方法;
//你也可以覆盖onViewCreated()生命周期方法;
//然后从这里调用这个回调方法;
@Override
public void onResume() {
super.onResume();
callbacks.onReach();
}
public void setTargetView() {
//做你想做的事情;
}
public interface OverviewCallbacks {
void onReach();
}
}
以上是FragmentOverView
类的全部内容。
现在根据以下内容修改你的Activity
类:
public class AddScheduleActivity extends AppCompatActivity {
private FragmentOverview fragmentOverview;
private FragmentTodo fragmentTodo;
@Override
protected void onCreate(Bundle savedInstanceState) {
//你的代码
fragmentOverview = new FragmentOverview(new FragmentOverview.OverviewCallbacks() {
@Override
public void onReach() {
setTargetView();
}
});
}
}
希望这样可以解决问题。如果有任何问题,请进行评论。
英文:
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.
答案2
得分: 0
这只是一个我还没有测试过的快速想法。你可以尝试将这个部分进行更改:
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(); //错误在这里
改成这样:
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(); //错误在这里
为了检查你的问题是否与调用顺序有关,你可以添加一些日志并使用 Logcat 检查它们被调用的顺序(假设你在使用 Android Studio)。类似这样:
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(); //错误在这里
和
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) {
//添加一个子视图
mTargetFrameLayout.setVisibility(View.VISIBLE);
} else mTargetFrameLayout.setVisibility(View.GONE);
}
}
最后一个代码片段可能对你有趣,以查看 onCreateView(以及导致问题的变量初始化)是否在调用 setTargetView() 函数之前完成。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论