Привет, ребята, мне действительно нужна ваша помощь. Я потратил около 5 дней, пытаясь обновить мой recyclerview только тогда, когда пользователь нажимает OK в диалоговом окне, которое появляется из панели действий меню. Я пробовал все возможные методы, которые только мог придумать, все методы, которые я видел в stackoverflow, YouTube и т. д., И ни один из них у меня не работал. Как мне получить обновление recyclerview во фрагменте после закрытия диалогового окна? Я знаю, что есть похожие вопросы по обновлению меню и (повторное использование с диалоговыми фрагментами), но ни у одного из них нет комбинации.
Из бесчисленных попыток текущая конфигурация кода, представленная ниже, не вызывает никаких ошибок, однако recyclerview остается пустым. Ближайшая попытка найти решение заключалась в создании адаптера и настройке ресайклера в onOptionsItemSelected. Но, очевидно, он обновляется только тогда, когда пользователь нажимает кнопку, и при первом щелчке создается пустой recyclerview.
Фрагмент:
(Есть много повторяющегося кода с комментариями из разных попыток)
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
private String Routine_name, Routine_split;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
@Override
public void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter) {
Routine_name = name;
Routine_split = split;
adapter = DialogAdapter;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
//Report that this fragment would like to participate in populating the options menu by
//receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
//If true, the fragment has menu items to contribute.
setHasOptionsMenu(true);
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//BuildRecyclerView();
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
/*Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
layoutManager = new LinearLayoutManager(getActivity());
adapter = new ExerciseRoutineAdapter(Routine_information);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);*/
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), "Routine Dialog");
//Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
BuildRecyclerView();
//adapter.notifyItemInserted(Routine_information.size());
//if (!Routine_name.equals("") && !Routine_split.equals("")) {
//}
}
return super.onOptionsItemSelected(item);
}
public void BuildRecyclerView(){
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
public void BuildAdapter(){
//adapter = new ExerciseRoutineAdapter(getContext(),Routine_information);
adapter.notifyItemInserted(Routine_information.size());
}
}
Мой фрагмент диалога:
public class ExerciseRoutine_Dialog extends DialogFragment{
private TextView ActionOK, ActionCANCEL;
private EditText Routine_name, Routine_split;
private RoutineDialogListener activityCommander;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView.Adapter adapter;
public interface RoutineDialogListener{
void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
activityCommander = (RoutineDialogListener) getTargetFragment();
}catch(ClassCastException e){
throw new ClassCastException(context.toString() + "Must Implement RoutineDialogListener");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
ActionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
ActionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
//recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if (!name.equals("") && !split.equals("")) {
Routine_information.add(new ExerciseRoutine_Information(name, split));
adapter = new ExerciseRoutineAdapter(getContext(), Routine_information);
activityCommander.sendInput(name, split, adapter);
adapter.notifyItemInserted(Routine_information.size());
}
getDialog().dismiss();
}
});
return view;
}
}
Я пробовал это, но заметил, что моя основная проблема в том, что я не знаю, куда поместить код, обновляющий адаптер. Как я уже сказал выше, я пробовал как onCreate, так и onOptionsItemSelected, но оба не работали. Какие-либо предложения?
Если вы посмотрите на закомментированный код, вы увидите много способов, которыми я пытался
К сожалению, сейчас у меня очень мало времени, чтобы что-нибудь записать. Я постараюсь сделать это через несколько дней, если до тех пор никто не сможет / не поможет вам.
Хорошо, спасибо, но сахар
Ваш текущий подход, похоже, состоит в том, чтобы передать RecyclerView.Adapter в DialogFragment и попытаться вставить новые данные на месте. Я считаю, что это проблемная установка. Цель диалога - предложить пользователям некоторые средства для ввода требуемых данных, точки. Ему не следует поручить работу по управлению RecyclerView или его Adapter, потому что в этом случае ваши компоненты будут очень тесно связаны:
Представьте, что сначала вы используете ListView в своей реализации, и вдруг кто-то решает запретить каждый ListView в вашем приложении (возможно, из соображений производительности) и просит вас обменять их все на RecyclerView. Тогда ваш подход вынудит вас изменить код для DialogFragment (он должен обслуживать другой тип Adapter). Более слабосвязанная реализация позволит вам вносить изменения в один компонент без необходимости переписывать слишком много других.
Вот почему я не буду пытаться заставить ваш код работать как есть, вместо этого я хочу показать вам другой способ:
Поскольку RecyclerView является частью пользовательского интерфейса Fragment, Fragment - это то место, где находится код, связанный с управлением RecyclerView. В принципе возможно иметь Adapter как внутренний класс Fragment, но я предпочитаю иметь его как отдельный класс, если код становится немного длиннее, а также потому, что он обеспечивает «развязку».
Интерфейсы играют очень важную роль в хорошей архитектуре, поэтому DialogFragment по-прежнему будет использовать интерфейс для отправки своих данных. Но это зависит от класса, который фактически реализует интерфейс (здесь: Fragment), чтобы передавать данные любым заинтересованным третьим сторонам, например. RecyclerView.Adapter (Adapter, в свою очередь, может иметь собственный интерфейс для публикации важных событий, таких как щелчки по элементам списка).
Сказав это, вот несколько фрагментов кода:
DialogFragment
public class ExerciseRoutine_Dialog extends DialogFragment {
private EditText Routine_name, Routine_split;
public interface RoutineDialogListener{
/**
* There is some new ExerciseRoutine_Information
*/
void sendInput(String name, String split);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
TextView actionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
TextView actionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
actionCANCEL.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getDialog().dismiss();
}
});
actionOK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if (!name.equals("") && !split.equals("")) {
// just send the input to the main Fragment
RoutineDialogListener listener = getListener();
if (listener != null) {
listener.sendInput(name, split);
}
}
getDialog().dismiss();
}
});
return view;
}
/**
* Tries to find a suitable listener, examining first the hosting Fragment (if any) and then the Activity.
* Will return null if this fails
* @return x
*/
private RoutineDialogListener getListener(){
RoutineDialogListener listener;
try{
Fragment onInputSelected_Fragment = getTargetFragment();
if (onInputSelected_Fragment != null){
listener = (RoutineDialogListener) onInputSelected_Fragment;
}
else {
Activity onInputSelected_Activity = getActivity();
listener = (RoutineDialogListener) onInputSelected_Activity;
}
return listener;
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
return null;
}
}
Fragment:
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
public static final String ROUTINE_DIALOG = "Routine Dialog";
private ArrayList<ExerciseRoutine_Information> routineInformations = new ArrayList<>();
private RecyclerView.Adapter adapter;
public static ExerciseRoutine instance(){
return new ExerciseRoutine();
}
@Override
public void sendInput(String name, String split) {
routineInformations.add(new ExerciseRoutine_Information(name, split));
adapter.notifyItemInserted(routineInformations.size());
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
setHasOptionsMenu(true);
RecyclerView recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new ExerciseRoutineAdapter(getContext(), routineInformations);
// So far you have a RecyclerView with an empty List.
recyclerView.setAdapter(adapter);
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
showDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showDialog(){
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), ROUTINE_DIALOG);
}
}
Эй, нет, ваш код имеет больше смысла, чем то, что я делал, но мне было интересно, почему вы сделали функцию getListener вместо использования onAttach? Также listener.sendInput (name, split) никогда не вызывается, так как split имеет значение null, есть идеи, почему?
@David - я использую getListener () вместо установки слушателя в onAttach (), потому что, например, в вашем случае, если пользователи нажимают «Отмена», определение слушателя не требуется. Это своего рода ленивая инициализация. Строка: расколоть не должна быть нулевой, потому что ей присвоены некоторые EditText.getText (). ToString (), и они могут быть пустыми, но не могут быть нулевыми. (Я запустил код, который опубликовал в образце приложения, и он отлично сработал, но, конечно, мне пришлось делать свои собственные макеты, поэтому я не могу быть уверен, где есть какие-то различия)
Основная проблема в том, что «посылать» адаптер - не лучшая идея. Вместо этого вы должны отправлять данные из диалога в хост-фрагмент / действие. У вас может быть метод интерфейса, принимающий List <ExerciseRoutine_Information>, и как только вы получите данные, вы передадите их адаптеру (который всегда должен быть создан в том же фрагменте, где существует RecyclerView) и вызвать notifyDatasetChanged (). "передать их ..." <=> написать какой-нибудь метод для добавления новых данных в существующий список данных адаптера. Очистите список, прежде чем добавлять что-либо, если это необходимо.