2

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.

CombatFragment

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));
   }
} `

AttackListViewContentAdapter

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;
    }
}

Weapon

public class Weapon extends RealmObject {
    @PrimaryKey
    int weaponID;
    //properties, set get methods etc.
}

Sheet

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;
    }
}

content_combat

            <ListView
            android:id="@+id/lv_attack_spellcasting_content"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"

            android:columnCount="7"
            android:rowCount="1" />

attack_list_item

Nothing really in there to include

Gideon Sassoon
  • 544
  • 1
  • 9
  • 29

1 Answers1

1

Your problem is happening because of bad initialization of your spinner widgets within AttackListViewContentAdapter class.

  1. You must set your spinners selection before setOnItemSelectedListener is set.
  2. You must check whether your spinner selection is not equal to current selection, to avoid an infinite loop between 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

AlexTa
  • 5,133
  • 3
  • 29
  • 46
  • Oh the spinner thing, I liked this solution for that. https://stackoverflow.com/a/42639757/2413303 – EpicPandaForce Jan 16 '18 at 15:32
  • 1
    You are rigth @EpicPandaForce, It is coolest solution to handle spinner onItemSelected first event, but looking to his/her code, I believe he/she is going to understand better this way... – AlexTa Jan 16 '18 at 15:38
  • What @AlexTa said. – Gideon Sassoon Jan 18 '18 at 15:59
  • @GideonSassoon, Does it work? If so, mark my answer as valid and if it don't, let me know, you should support people who help you and give them some feedback. – AlexTa Jan 19 '18 at 11:12
  • @AlexTa Hi I tested it and it solved my issues wonderfully (It dragged out other bugs under the mat which I wanted to squish before getting back to you), additionally you also solved an issue for another one of my problems of why my spinners weren't setting in other places as well. You took the time to: • point out why this was occuring, • went through my git to look for and find out what was wrong (which is a lot more than most people are willing to do so thank you for that as well.) – Gideon Sassoon Jan 19 '18 at 12:00