Представления макета фрагмента, вызывающие исключение NullPointerException

#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().