#java #android #android-fragments
#java #Android #android-фрагменты
Вопрос:
Я пытаюсь использовать View Pager с адаптером состояния фрагмента. Но получение NullPointerException
при попытке вызвать метод фрагмента после создания его экземпляра.
Вот класс activity и класс Adapter:
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);
}
}
Но когда я вызываю setTargetView()
метод после создания экземпляра FragmentOverview
, я получаю 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()
amp; onCreateView()
. Но как преодолеть эту проблему? Или я пытаюсь сделать неправильно?
Комментарии:
1. где
mTargetFrameLayout
определено или инициализировано?2. вы вызываете mTargetFrameLayout, не инициируя его?
3. извините, это была ошибка редактирования. Я отредактировал вопрос.
Ответ №1:
Я думаю, что ваше предположение верно. Макет фрагмента генерируется в классе onViewCreated()
of FragmentOverview
. Но этот метод начинает выполняться после выполнения Activity
методов жизненного цикла onCreate()
, onStart()
и onResume()
. Вы можете проверить связь жизненного цикла Activity
и Fragment
, перезаписав методы жизненного цикла и добавив журналы, как указано @Max_Hockeborn.
Таким образом, вы всегда будете получать представления, т. Е. mTargetFrameLayout
null, если вы вызываете setTargetView()
перед выполнением onCreateView()
в FragmentOverview
.
Чтобы решить эту проблему, вы можете добавить метод обратного вызова, который будет выполняться в родительском классе после выполнения onCreateView()
of FragmentOverveiw
. И в этом обратном вызове вы можете вызвать setTargetView()
.
Здесь я предоставляю возможное решение для вашего случая:
В FragmentOverview
добавьте эти строки:
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();
}
}
Это все для FragmentOverView
класса.
Теперь измените свой Activity
класс следующим образом:
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();
}
});
}
}
Надеюсь, это сработает. Прокомментируйте, если что-то пошло не так.
Ответ №2:
Это просто быстрая идея, которую я еще не мог протестировать. Вы могли бы попытаться изменить это:
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
к этому:
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
Чтобы проверить ваше предположение о том, что ваша проблема основана на порядке вызова, вы можете добавить несколько журналов и проверить порядок их вызова с помощью 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(); //ERROR IS HERE
и
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);
}
}
Последний может быть интересен для вас, чтобы посмотреть, завершен ли onCreateView (и инициализация переменных, вызывающих ваши проблемы) до вызова функции setTargetView().