This is very weird and hard to explain, so screenshots may help.
I have a Fragment
that launches a DialogFragment
to filter the results of a RecyclerView
. The dialog shows three options that the user can check or uncheck.
If the user cancels the dialog by taping the back button or touching outside the dialog, I want to revert the selection made by user, so the choices will be checked or unchecked as when the dialog was open.
To achieve that I keep the current filters applied to the RecyclerView
in a boolean
array (test
) in the Fragment
. Therefore, I overwritten the onCancel()
method of DialogFragment
to call another method in the Fragment
that sets again the current filters to the instance of the DialogFragment
.
The problem I have is that the second time the user opens the dialog and cancels it, the boolean object in the array of my Fragment
that is in the position of the option unchecked/checked, changes the value.
By default, all options are checked:
Then the user unchecks an option but do not click on accept:
As the user made changes to the dialog options but did not apply them, I revert the changes. Logcat shows the values before and after applying again the same filters:
2024-01-18 20:37:33.205 31320-31320 Filters
com.example.futbol D Values of test array before calling setSeleccion: [true, true, true]2024-01-18 20:37:33.206 31320-31320 Filters
com.example.futbol D Values of test array after calling setSeleccion: [true, true, true]
Then, the user opens the dialog again, and as expected, all options are checked. So far, so good:
Now the user unchecks another option, and as before, do not accept:
However, when I check the logs, I see test
array changed even before calling setSeleccion()
:
2024-01-18 20:41:14.832 31320-31320 Filters
com.example.futbol D Values of test array before calling setSeleccion: [true, true, false]2024-01-18 20:41:14.832 31320-31320 Filters
com.example.futbol D Values of test array after calling setSeleccion: [true, true, false]
Indeed, if the user opens the dialog again, the option he unchecked last time is unchecked:
In my code, the test
array is used two times only, one to declare it, and the second one as a parameter of setSeleccion()
.
My Fragment
code (I removed unrelated lines):
public class ListaFragment extends BaseTemporadaFragment
implements Adaptador.OnEventClickListener,
FiltroFragment.FiltroListener {
public static final String TAG = "ListaFragment";
private boolean[] test = new boolean[]{true, true, true};
private FiltroFragment dialogFragment;
public ListaFragment() {
}
public static ListaFragment newInstance(int temporada, boolean[] filtros) {
ListaFragment fragment = new ListaFragment();
Bundle args = new Bundle();
args.putInt("temporada", temporada);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.dialogFragment = new FiltroFragment();
this.dialogFragment.setListener(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_lista, container, false);
((AppCompatActivity) this.getActivity()).getSupportActionBar()
.setTitle(R.string.ver_lista);
this.setHasOptionsMenu(true);
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}
// TODO
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.filtro, menu);
super.onCreateOptionsMenu(menu, inflater);
}
// TODO
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menu_filtro) {
this.dialogFragment.show(MainActivity.fragmentManager, FiltroFragment.TAG);
}
return super.onOptionsItemSelected(item);
}
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
}
@Override
public void OnCancelListener(DialogFragment dialog) {
Log.d("Filters", "Values of test array before calling setSeleccion: " + Arrays.toString(this.test));
this.dialogFragment.setSeleccion(this.test);
Log.d("Filters", "Values of test array after calling setSeleccion: " + Arrays.toString(this.test));
}
@Override
public void onClick(View view) {
}
// TODO
@Override
public void onItemClick(Partido partido) {
}
}
My DialogFragment
:
public class FiltroFragment extends DialogFragment {
public interface FiltroListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void OnCancelListener(DialogFragment dialog);
}
private FiltroListener listener;
public static final String[] TIPOS = new String[]{"Liga", "Copa", "Amistosos"};
private boolean[] seleccion = new boolean[]{true, true, true};
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
builder.setTitle(this.getResources().getString(R.string.titulo));
builder.setMultiChoiceItems(FiltroFragment.TIPOS, this.seleccion,
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i, boolean b) {
//cambiarSeleccion(i, b);
}
});
builder.setPositiveButton(R.string.aplicar,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
listener.onDialogPositiveClick(FiltroFragment.this);
});
return builder.create();
}
@Override
public void onCancel(@NonNull DialogInterface dialog) {
super.onCancel(dialog);
listener.OnCancelListener(FiltroFragment.this);
}
public boolean[] getSeleccion() {
return this.seleccion;
}
public void setSeleccion(boolean[] seleccion) {
this.seleccion = seleccion;
}
public void setListener(FiltroListener listener) {
this.listener = listener;
}
}
As you can see my setSeleccion()
method just changes the array of booleans of my dialog, but it does not modify test
array.
I also tried to debug my app breaking on value change of test
array, but it does not break!!!
How is it possible that test
array is changing the value?
I'm fairly sure this is how you explain the behaviour:
You open the dialog, modify it, cancel it, in OnCancelListener
you call this.dialogFragment.setSeleccion(this.test)
and at this point your dialog now has a reference to your test
variable.
You open the dialog again, everything looks fine (because test = [true, true, true]
).
You unchecked one of the options, changing the value of DialogFragment.seleccion
AND the value of ListaFragment.test
(because it was a reference to the same array).
In summary, Java passed your array by reference to your dialog, so when you updated the value, the original array was updated.