1

Long story short: I have a listView with a customAdapter (called MathAdapter). This Adapter handles an ArrayList of type CalculationModel. By default there are ~15 CalculationModels in my List, however, under special conditions, i have to delete about 6 of them.

Since i am pretty new to Android Development (I'm coming from iOS), my first basic question is:

Where would one normally manipulate the ListData? In my case, a fragment initializes the adapter and passes a list of my data. During the lifetime of my app, i manipulate this list and call notifiyDataSetChanged. Everything works as expected, and I'm able to remove or add 1 CalculationModel at a time without any problems.

There is also no problem when deleting 6 CalculationModels, works fine too. The App crashes when i try to reinsert them with the following ErrorLog:

04-27 07:48:59.194 1766-1766/com.example.test E/AndroidRuntime: FATAL EXCEPTION: main
   Process: com.example.test, PID: 1766
   java.lang.ArrayIndexOutOfBoundsException: length=11; index=11
       at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:8271)
       at android.widget.ListView.layoutChildren(ListView.java:1600)
       at android.widget.AbsListView.onLayout(AbsListView.java:2528)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at com.example.test.TouchDisableView.onLayout(TouchDisableView.java:57)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:15655)
       at android.view.ViewGroup.layout(ViewGroup.java:4856)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2284)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2004)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1236)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6471)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
       at android.view.Choreographer.doCallbacks(Choreographer.java:603)
       at android.view.Choreographer.doFrame(Choreographer.java:573)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
       at android.os.Handler.handleCallback(Handler.java:733)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:157)
       at android.app.ActivityThread.main(ActivityThread.java:5356)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:515)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
       at dalvik.system.NativeStart.main(Native Method)

To make it clear, im not new to Java, and i see that ArrayIndexOutOfBounds is the problem, but i have no clue where this happens, since my list and the item count in the adapter are higher than 11 (14 as expected)

Code:

    List<CalculationModel> data;
    List<CalculationModel> backupData = new ArrayList<>();    

@Override
public void onResume() {
    if (mathApp.isCleanPhase) {
        removeNonClearData();
    } else {
        addNonClearData();
    }

    adapter.notifyDataSetChanged();

    super.onResume();
}


    public void removeNonClearData() {
    if (backupData.size() > 0) return;

    for (int i = 0; i < data.size(); i++) {
        CalculationModel tmp = data.get(i);

        String sName = tmp.getSName();

        if (sName.equals("r") || sName.equals("wv") || sName.equals("qv") || sName.equals("tw") || sName.equals("ti") || sName.equals("h") || sName.equals("rs")) {
            backupData.add(data.remove(i));
            i--;
        }
    }

    adapter.itemCount = data.size();
}

public void addNonClearData() {
    if (backupData.size() == 0) return;

    for (int i = 0; i < backupData.size(); i++) {
        //'r' is a default parameter, which shall be in row 6
        if (backupData.get(i).getSName().equals("r")) data.add(5, backupData.remove(i));
        else data.add(backupData.remove(i));
    }

    adapter.itemCount = data.size();

    print(data.size());
}

EDIT: Forgot to mention, the data gets basically loaded the way i want it to, but the app crashes when im scrolling back up again in my list. All the data is in the list i want to see there, but when scrolling up, --> Crash.

Like mentioned, i just don't understand where this Error comes from, since the log gives no information about it...

Patrick
  • 12,336
  • 15
  • 73
  • 115
glace
  • 2,162
  • 1
  • 17
  • 23
  • can you post code? – Bala Raja Apr 27 '16 at 06:01
  • added code. hope it helps – glace Apr 27 '16 at 06:12
  • at which list the error occurs? data or backupData ? – Opiatefuchs Apr 27 '16 at 06:13
  • data is the list which gets passed to the adapter in onCreateView – glace Apr 27 '16 at 06:14
  • I am not really sure about, but I think the problem is anywhere here: `backupData.add(data.remove(i)); i--;` . You are removing some entries from `data` , but the loop should loop until `data.size()` is reached. Even if you sub from `i`, the loop goes on. So at any time, you reach a value that is not inside the `data`list. The `data.size()` that is referred on the head of the loop, will not minimized, so you have a wrong value here. Maybe I am confused, but that is what I assume.... – Opiatefuchs Apr 27 '16 at 06:19
  • valid point, but i guess not the source of the error. Edited question – glace Apr 27 '16 at 06:23
  • sorry, a more clear example what I mean. For example, `data.size()` is 12 at the time you are initializing the loop. some view loops later, you are removing one, so the `data.size()` is 11. BUT, the loop still goes on until 12 is reached because that´s the value when the loop starts...And at the end this must cause an error, because the loop reaches 12 which is not in the `data` list anymore...This must be the cause for the `ArrayIndexOutOfBoundException` – Opiatefuchs Apr 27 '16 at 06:23
  • so a possible way to do it, add the values into your backup list, but remove it from data list, after adding the values in a separated loop. – Opiatefuchs Apr 27 '16 at 06:27
  • Please see edit. addNonClearData and removeNonClearData are getting called in onResume, which works FINE. Error occurs while scrolling the list-. There are no manipulations while scrolling – glace Apr 27 '16 at 06:31
  • ok, see your edit. Next point is, that you have to load your adapter again after all loop stuff is done. I think the ListView "thinks" there are just let´s say 12 values, but there aren´t. If I remove some values from a list, I allways initialize the adapter newly, the listview still points to the same object as passed at the beginning. So instead calling for example `adapter = new YourAdpater(yourNewList)`and after that `notifyDataSetChanged()` should work better..and maybe set the list again.... – Opiatefuchs Apr 27 '16 at 06:33
  • Makes sense, but why do all other manipulations work with just notifyDataSetChanged? How would i load my Adapter again? just create a new variable and pass it to my parentView? That sounds like something that should not be needed to be done... EDIT: I don't understand your edit. Which statement should be done? – glace Apr 27 '16 at 06:37
  • sorry for confusing answer, ListView and it´s behaviour is allways a big topic here in SO. If an adapter is created, it allways holds the object passed at this time. So there are several possibilities to handel this. In my case, I setup a new list to the adapter and set the new adapter to the ListView. This will use much resources, but worked better in my case. If you doesn´t want this, you can use one of the adapter methods: `add(), insert(), remove(), clear()`, only after that, `notifyDataSetChanged` will have an effect. – Opiatefuchs Apr 27 '16 at 07:00
  • I really appreciate the help, but that notifyDataSetChanged has only an effect when using add() etc is simply not true. Manipulating the list i passed to the adapter in the beginning works everywhere, except for the described case – glace Apr 27 '16 at 07:06

2 Answers2

3

After a long time (basically the last 2 days) of frustration and confusion I finally managed to understand how this Adapters for ListViews work (At least i hope).

As described in the question i have the following setup: A Fragment holding a list of data, and the adapter, which gets this list of data at initialization.

Now there are two different behaviors of the adapter when manipulating data.

If one just edits one of the objects in the list (e.g. i want to change param "value" of my CalculationModel on position 4, everything works fine and changes are accepted with notifyDataSetChanged().

If one deletes a complete object from the list, notifyDataSetChanged() is also working and doing the expected job.

But, if one was about to add a new object to the list, (or in my case, re-insert the removed one) it is NECESSARY to recreate the whole adapter, since notifyDataSetChanged() has no effect anymore.

Thanks for the help, i may not have made it without the input in the comments.

glace
  • 2,162
  • 1
  • 17
  • 23
  • 1
    I confirm :) i refactor the creation in function of adapter and in every add i call this function and its work fine , thanks man – Dahar Youssef Mar 25 '20 at 15:35
1
when you are removing data from array call notifyDataSetChanged(); 
Dilip Dubey
  • 485
  • 2
  • 11
  • have you checked in debug is on Resume method are calling or not?? – Dilip Dubey Apr 27 '16 at 07:06
  • Yes it is called. Data gets manipulated as expected, Error occurs when scrolling the list afterwards – glace Apr 27 '16 at 07:07
  • for (int i = 0; i < backupData.size(); i++) { //'r' is a default parameter, which shall be in row 6 if (backupData.get(i).getSName().equals("r")) data.add(5, backupData.remove(i)); else data.add(backupData.remove(i)); } you have to add i-- – Dilip Dubey Apr 27 '16 at 07:16
  • Upvote for finding a bug in my code. Thanks for that. but the initial problem is still unsolved. Error when scrolling the list up after re-adding the values – glace Apr 27 '16 at 07:31
  • @glace use adapter.notifyDataSetInvalidated(); it may solve problem public void onResume() { if (mathApp.isCleanPhase) { removeNonClearData(); } else { addNonClearData(); } adapter.notifyDataSetChanged(); adapter.notifyDataSetInvalidated(); super.onResume(); } – Dilip Dubey Apr 27 '16 at 07:55