#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(); }
Комментариев нет:
Отправить комментарий