#android #android_studio #android_sdk
В книге Android для разработчиков 3е издание, авторы Пол Дейтел, Харви Дейтел, Александер Уолд, есть пример, в котором DialogFragment, является анонимным внутренним классом. Android Studio выводит сообщение "Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static". Возник вопрос - в API андроида, произошли какие-то изменения, и больше невозможно использовать фрагменты как анонимные классы? Или в книге были допущены ошибки? В книге рассматривается API23, в AS API27 private OnClickListener guessButtonListener = new OnClickListener() { @Override public void onClick(View v) { Button guessButton = ((Button) v); String guess = guessButton.getText().toString(); String answer = getCountryName(correctAnswer); ++totalGuesses; // Увеличение количества попыток пользователя if (guess.equals(answer)) { // Если ответ правилен ++correctAnswers; // Увеличить количество правильных ответов // Правильный ответ выводится зеленым цветом answerTextView.setText(answer + "!"); answerTextView.setTextColor( getResources().getColor(R.color.correct_answer, getContext().getTheme())); disableButtons(); // Блокировка всех кнопок ответов // Если пользователь правильно угадал FLAGS_IN_QUIZ флагов if (correctAnswers == FLAGS_IN_QUIZ) { // DialogFragment для вывода статистики и перезапуска DialogFragment quizResults = new DialogFragment() { // Создание окна AlertDialog @Override public Dialog onCreateDialog(Bundle bundle) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage( getString(R.string.results, totalGuesses, (1000 / (double) totalGuesses))); // Кнопка сброса "Reset Quiz" builder.setPositiveButton(R.string.reset_quiz, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { resetQuiz(); } } ); return builder.create(); // Вернуть AlertDialog } }; // Использование FragmentManager для вывода DialogFragment quizResults.setCancelable(false); quizResults.show(getFragmentManager(), "quiz results"); } else { // Ответ правильный, но викторина не закончена // Загрузка следующего флага после двухсекундной задержки handler.postDelayed( new Runnable() { @Override public void run() { animate(true); // Анимация исчезновения флага } }, 2000); // 2000 миллисекунд для двухсекундной задержки } } else { // Неправильный ответ flagImageView.startAnimation(shakeAnimation); // Встряхивание // Сообщение "Incorrect!" выводится красным шрифтом answerTextView.setText(R.string.incorrect_answer); answerTextView.setTextColor(getResources().getColor( R.color.incorrect_answer, getContext().getTheme())); guessButton.setEnabled(false); // Блокировка неправильного ответа } } };
Ответы
Ответ 1
Нестатический внутренний класс будет удерживать ссылку на родительский класс. В этом случае с фрагментом, возникает потенциальная утечка памяти, особенно если родительский класс это активити. Возможно, авторы просто это упустили, потому что случай довольно специфичный именно для Android.Ответ 2
Fragment не может быть анонимным классом. При восстановлении активити FragmentManager может попытаться создать фрагмент заново через рефлексию. Вы получите ошибку android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.squareup.MyActivity$1: make sure class name exists, is public, and has an empty constructor that is publicОтвет 3
С помощью этой ссылки удалось исправить ошибку: https://habr.com/ru/post/259805/ Необходимо создать класс, расширяющий DialogFragment. Он будет выводить наш диалог, а обрабатывать нажатие кнопки ОК будем в вызывающем фрагменте MainActivityFragment в методе onActivityResult, который получает код нажатой кнопки из вызываемого фрагмента AlertDialogFragment: import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; public class AlertDialogFragment extends DialogFragment { public static int totalGuesses; @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { totalGuesses = getTargetRequestCode(); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(getString(R.string.results, totalGuesses, (1000 / (double) totalGuesses))); builder.setPositiveButton(R.string.reset_quiz, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(totalGuesses, Activity.RESULT_OK, null); } }); return builder.create(); } } В вызывающем фрагменте MainActivityFragment создадим метод showDialog, показывающий диалог, и обрабатываем нажатие кнопки ОК в методе onActivityResult: //show dialog at the end of the game public void showDialog() { DialogFragment fragment = new AlertDialogFragment(); fragment.setTargetFragment(this, totalGuesses); fragment.setCancelable(false); fragment.show(getFragmentManager(), "quiz results"); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK) resetQuiz(); } Вместо кода с ошибкой пишем такой код: if (correctAnswers == FLAG_IN_QUIZ) { showDialog(); }
Комментариев нет:
Отправить комментарий