Инициализация фрагментов вне onCreate() в Android

#android #android-studio #android-layout #android-fragments #android-activity

#Android #android-studio #android-макет #android-фрагменты #android-активность

Вопрос:

Я очень новичок в разработке Android. У меня в основном есть действие с некоторыми кнопками, например, «seeTreePicture» и «seeSeaPicture». Когда я нажимаю кнопку, я хочу использовать фрагмент, который я назвал «ContentViewer», для отображения случайного изображения дерева / моря, а также иметь кнопки под изображением, чтобы уничтожить экземпляр фрагмента ContentViewer и вернуться в меню. Проблема в том, что если я попытаюсь использовать транзакцию фрагмента где-либо, кроме onCreate() действия, я получаю исключение нулевого указателя при попытке получить доступ к представлению во фрагменте.

Моя активность и вещи, связанные с фрагментом:

 public class SeeActivity extends AppCompatActivity {

DisplayFragment displayFragment;
Button seeTreeButton;
Button seeSeaButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_see);

    seeTreeButton = findViewById(R.id.seeTreeButton);
    seeSeaButton = findViewById(R.id.seeSeaButton);
    displayFragment = new DisplayFragment();

    seeTreeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.add(R.id.fragmentContainer, displayFragment);
            fragmentTransaction.commit();
            fragmentTransaction.addToBackStack(null);

            displayFragment.changeImage(randomTree);
        }
    });

}

}
 

В моем фрагменте изменение изображения просто изменяет источник изображения ImageView:

 public void changeImage(int treeResource)
{
    img = getView().findViewById(R.id.imageView);
    img.setImageResource(treeResource);
}
 

Я получаю исключение с нулевым указателем при попытке получить доступ к представлению из getView() во фрагменте, что означает, что onCreateView не был вызван. Тем не менее, если я помещу ту же транзакцию в метод onCreate() действия, это сработает. Что я делаю не так?

Ответ №1:

Проблема с вашим кодом заключается в том, что вы обращаетесь getView() к своему фрагменту до того, как фрагмент прошел инициализацию представления. Причина, по которой у вас нет сбоя при выполнении транзакции в onCreate() методе вашей активности, заключается в том, что к тому времени, когда вы нажимаете на кнопку, ваш фрагмент уже прошел onCreateView() и инициализировал его просмотр. Ознакомьтесь с руководством по жизненному циклу фрагмента и имейте в виду, что вы не должны получать доступ к вашему представлению фрагмента до его создания или после его уничтожения. Для получения дополнительной информации о том, почему ваш вид фрагмента не инициализируется мгновенно, ознакомьтесь с этим руководством. Что касается решения, рассмотрите возможность установки аргументов для вашего фрагмента, прежде чем добавлять его в свою транзакцию, как здесь:

 Bundle args = new Bundle();
args.putInt(DisplayFragment.IMG_RESOURCE_ARG, randomTree);
displayFragment.setArguments(args);
 

Затем в ваших onCreateView() onViewCreated() методах или вашего фрагмента восстановите аргументы, подобные here, и установите ресурс изображения:

 @Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
    int resourceId = requireArguments().getInt(IMG_RESOURCE_ARG);
    img = view.findViewById(R.id.imageView);
    img.setImageResource(resourceId);
}
 

Ответ №2:

Потому что транзакция фрагмента не происходит мгновенно. Это происходит асинхронно. Таким образом, фактическая работа по созданию представлений еще не выполнена. Ваши варианты — либо использовать commitNow() вместо commit (который будет выполнять это синхронно, но потребует гораздо больше времени и, возможно, приведет к тому, что ваше приложение будет заметно приостановлено), либо дождаться фактического завершения транзакции фрагмента. Это можно легко сделать, поместив его в Runnable и передав этот runnable в runOnCommit