fixed spinner infinite loop in appointments

This commit is contained in:
Alex
2026-04-09 02:48:55 -06:00
parent 992da24260
commit 4664fe177b
3 changed files with 69 additions and 19 deletions

View File

@@ -176,7 +176,7 @@ public class AppointmentDetailFragment extends Fragment {
*/
private void applyViewState(AppointmentDetailViewModel.ViewState state) {
isUpdatingUI = true;
// Mode specific UI
binding.tvApptMode.setText(state.isEditing ? "Edit Appointment" : "Add Appointment");
binding.tvAppointmentId.setText(DateTimeUtils.formatId(appointmentViewModel.getAppointmentId()));

View File

@@ -10,6 +10,7 @@ import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import com.example.petstoremobile.adapters.WhiteTextArrayAdapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -51,6 +52,12 @@ public class SpinnerUtils {
names.add(nameExtractor.apply(item));
}
// Only update adapter if contents changed to remove infinite loop when spinner is opened
if (isAdapterDataSame(spinner, names)) {
setSelectedId(spinner, data, defaultText, preselectedId, idExtractor);
return;
}
ArrayAdapter<String> adapter;
if (useWhiteText) {
adapter = new WhiteTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, names);
@@ -61,26 +68,41 @@ public class SpinnerUtils {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
setSelectedId(spinner, data, defaultText, preselectedId, idExtractor);
}
private static <T> void setSelectedId(Spinner spinner, List<T> data, String defaultText, Long preselectedId, Function<T, Long> idExtractor) {
if (preselectedId != null && preselectedId != -1) {
int offset = (defaultText != null) ? 1 : 0;
for (int i = 0; i < data.size(); i++) {
Long currentId = idExtractor.apply(data.get(i));
if (Objects.equals(currentId, preselectedId)) {
spinner.setSelection(i + offset);
if (spinner.getSelectedItemPosition() != i + offset) {
spinner.setSelection(i + offset);
}
break;
}
}
}
}
/**
* Checks if the adapter data is the same as the new data.
*/
private static boolean isAdapterDataSame(Spinner spinner, List<String> newNames) {
if (spinner.getAdapter() == null) return false;
if (spinner.getAdapter().getCount() != newNames.size()) return false;
for (int i = 0; i < newNames.size(); i++) {
if (!Objects.equals(spinner.getAdapter().getItem(i), newNames.get(i))) return false;
}
return true;
}
/**
* Sets up a simple string spinner for filtering with a callback.
*/
public static void setupStringFilterSpinner(Context context, Spinner spinner, String[] items, Runnable onSelectionChanged) {
WhiteTextArrayAdapter<String> adapter = new WhiteTextArrayAdapter<>(context,
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
updateStringSpinnerIfChanged(context, spinner, items, true);
setupFilterSpinner(spinner, onSelectionChanged);
}
@@ -111,7 +133,7 @@ public class SpinnerUtils {
if (value == null || spinner.getAdapter() == null) return;
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
int pos = adapter.getPosition(value);
if (pos >= 0) {
if (pos >= 0 && spinner.getSelectedItemPosition() != pos) {
spinner.setSelection(pos);
}
}
@@ -123,7 +145,9 @@ public class SpinnerUtils {
if (spinner == null || array == null || value == null) return;
for (int i = 0; i < array.length; i++) {
if (Objects.equals(array[i], value)) {
spinner.setSelection(i);
if (spinner.getSelectedItemPosition() != i) {
spinner.setSelection(i);
}
return;
}
}
@@ -133,8 +157,21 @@ public class SpinnerUtils {
* Configures a simple string array spinner.
*/
public static void setupStringSpinner(Context context, Spinner spinner, String[] items) {
BlackTextArrayAdapter<String> adapter = new BlackTextArrayAdapter<>(context,
android.R.layout.simple_spinner_item, items);
updateStringSpinnerIfChanged(context, spinner, items, false);
}
/**
* Updates a string spinner only if the items have changed.
*/
public static void updateStringSpinnerIfChanged(Context context, Spinner spinner, String[] items, boolean useWhiteText) {
if (isAdapterDataSame(spinner, Arrays.asList(items))) return;
ArrayAdapter<String> adapter;
if (useWhiteText) {
adapter = new WhiteTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, items);
} else {
adapter = new BlackTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, items);
}
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
}

View File

@@ -73,36 +73,49 @@ public class UIUtils {
}
/**
* Sets the enabled state and alpha for multiple views.
* Sets the enabled state and alpha for multiple views, only if changed.
*/
public static void setViewsEnabled(boolean enabled, View... views) {
for (View v : views) {
if (v != null) {
v.setEnabled(enabled);
v.setAlpha(enabled ? 1.0f : 0.5f);
if (v.isEnabled() != enabled) {
v.setEnabled(enabled);
}
float targetAlpha = enabled ? 1.0f : 0.5f;
if (Math.abs(v.getAlpha() - targetAlpha) > 0.01f) {
v.setAlpha(targetAlpha);
}
}
}
}
/**
* Sets enabled state for a field and updates alpha for both the field and its label.
* Sets enabled state for a field and updates alpha for both the field and its label, only if changed.
*/
public static void setFieldEnabled(boolean enabled, View field, View label) {
if (field != null) {
field.setEnabled(enabled);
field.setAlpha(enabled ? 1.0f : 0.5f);
if (field.isEnabled() != enabled) {
field.setEnabled(enabled);
}
float targetAlpha = enabled ? 1.0f : 0.5f;
if (Math.abs(field.getAlpha() - targetAlpha) > 0.01f) {
field.setAlpha(targetAlpha);
}
}
if (label != null) {
label.setAlpha(enabled ? 1.0f : 0.5f);
float targetAlpha = enabled ? 1.0f : 0.5f;
if (Math.abs(label.getAlpha() - targetAlpha) > 0.01f) {
label.setAlpha(targetAlpha);
}
}
}
/**
* Sets the alpha for multiple views.
* Sets the alpha for multiple views, only if changed.
*/
public static void setViewsAlpha(float alpha, View... views) {
for (View v : views) {
if (v != null) {
if (v != null && Math.abs(v.getAlpha() - alpha) > 0.01f) {
v.setAlpha(alpha);
}
}