Apologies if this is poorly explained, I am having difficulty in understanding it myself. If you point out anything you don't understand I will do my best to correct any issues. Okay so here we go.
Several classes. (D&D sheet, sheet has weapons user can equip, this is about equipping said weapons which is stored in a list)
What I've gathered so far is that when I create a new attackListViewContentAdapter it loops at a rapid and continued pace. So much so that the screen does not respond to me touching any of the widgets. I've done things like log a number each time it passes so it shows when it's doing it again and again. If you need information on that I can show you where I put the logs and what shows in my Logcat when I add an additional view (row).
I believe that it's something to do with the onChangedListener which keeps being triggered, even if I found the reason why how do I then get to a stage where I can create a new view and have the listener so it can record changes.
Please note in the interests of space I will be using abbreviated code. I've ignored things like dialog boxes and widgets which aren't relevant. So if it seems like something missing or you need to view the classes, it's possibly in the file which I've linked above each one.
public class CombatFragment extends Fragment {
@BindView(R.id.lv_attack_spellcasting_content)
ListView lv_attack_spellcasting_title;
@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.content_combat, container, false);
RealmList<Weapon> weaponList = sheet.getWeaponList();
final AttackListViewContentAdapter attackListViewContentAdapter = new AttackListViewContentAdapter(getActivity(), sheet, realm, weaponList);
weaponList.addChangeListener(new RealmChangeListener<RealmList<Weapon>>() {
@Override
public void onChange(RealmList<Weapon> weapons) {
/* Gives the adaptor a kick to know that the weapon realm list has changed */
attackListViewContentAdapter.notifyDataSetChanged();
loopOnChanged++;
}
});
lv_attack_spellcasting_title.setAdapter(attackListViewContentAdapter);
playerInit();
return rootView;
}
// This is a fake method, this is just to show that the .add is in it's own method which is triggered by a button press and not in onCreate
public void buttonPress() {
sheet.getWeaponList().add(realm.createObject(Weapon.class));
}
} `
public class AttackListViewContentAdapter extends ArrayAdapter<Weapon> {
public AttackListViewContentAdapter(Context context, Sheet sheet, Realm realm, List<Weapon> weaponList) {
super(context, 0, weaponList);
this.sheet = sheet;
this.realm = realm;
}
@Override
@NonNull
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
//Because you're returning the view (AttachToRoot is false) the ArrayAdaptor (This class) will handle adding the view to the list.
convertView =
LayoutInflater.from(getContext()).inflate(R.layout.attack_list_item, parent, false);
return convertView;
}
}
public class Weapon extends RealmObject {
@PrimaryKey
int weaponID;
//properties, set get methods etc.
}
public class Sheet extends RealmObject {
@PrimaryKey
private int sheetID;
private RealmList<Weapon> weaponList;
public RealmList<Weapon> getWeaponList() {
return weaponList;
}
public void setWeaponList(RealmList<Weapon> weaponList) {
this.weaponList = weaponList;
}
}
<ListView
android:id="@+id/lv_attack_spellcasting_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnCount="7"
android:rowCount="1" />
Nothing really in there to include
Your problem is happening because of bad initialization of your spinner widgets within AttackListViewContentAdapter
class.
setOnItemSelectedListener
is set.onChange
and onItemSelected
methods. I mean, your spinners onItemSelected
callbacks, execute a realm transanctions, then, those transanctions fire your onChange
callback and finally, your onChange
callback invokes notifyDataSetChanged()
which make cycle start again going into an infinite loop.To solve your problem, you should follow the next steps inside AttackListViewContentAdapter.java:
A) Remove the following lines from addWeaponToUI()
method:
private void addWeaponToUI() {
et_name_value.setText(weapon.getWeaponName());
np_damage_number_of_die_value.setValue(weapon.getWeaponDamageNumberOfDie());
SheetEnum.Ability ability = SheetEnum.Ability.getEnumValue(weapon.getWeaponAbilityBonusInt());
tv_attack_bonus_value.setText(String.valueOf(sheet.getAbilityBonus(ability)));
// REMOVE below lines!
//s_damage_die_type_value.setSelection(weapon.getWeaponDamageDieTypeInt());
//s_damage_type_value.setSelection(weapon.getWeaponDamageTypeInt());
//s_ability_bonus_value.setSelection(weapon.getWeaponAbilityBonusInt());
}
B) Invoke spinner setSelection()
before setOnItemSelectedListener()
, then check selected item is not equal to selected position to avoid an infinite loop:
ArrayAdapter<CharSequence> damageDieTypeAdapter = ArrayAdapter.createFromResource(getContext(), R.array.die_type, android.R.layout.simple_spinner_dropdown_item);
s_damage_die_type_value.setAdapter(damageDieTypeAdapter);
//Set selection before listener
s_damage_die_type_value.setSelection(weapon.getWeaponDamageDieTypeInt());
s_damage_die_type_value.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View v, final int position, long id) {
//Check selected position is not equal to current position to avoid an infinite loop
if (position != weapon.getWeaponDamageDieTypeInt()) {
String[] value = getContext().getResources().getStringArray(R.array.die_type);
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
weapon.setWeaponDamageDieType(position);
}
});
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
C) Repeat Step B for s_damage_type_value
and s_ability_bonus_value
spinners