Listviews are not so simple anymore

While riviving an old project I was scratching my head about a bug that finally turned out to be a change in how listviews work. Previously, to add an element to a simple listview, I would do this:


But this stopped working. Now I have to do:

Somehow, this seems a waste of space and time to store and copy the list.
Listviews.aia (2.1 KB)

Wow I never used elements like a variable (your old project) but you had to save elements in a variable before close app or you didn't need to save data ?

No, in my project the data is read from the web and then displayed in a list.
It was just simpler to use the ListView elements like it is a list, but I guess, since the ListView has quite some new functionality since I used that option a few years ago, it cannot be done in the old way.

1 Like

The problem with the old approach is that it exposed the internal data structure of how the list view manages its state, which is generally a violation of OOP design principles. Now, the Elements property returns a copy of the list of elements so that users don't end up accidentally manipulating the internal state of the ListView as you do in your first iteration. It's just convenient that you then set the property to itself which updates a bunch of internal state to match, but if you hadn't done that things like indices, etc. would be off.

I think the correct way to proceed from here would be to add a new AddElements method similar to the Remove function added in the last release that handles adding elements and updating any internal state to keep everything in sync as a single block.

I should caution that I have seen people new to lists thinking that all lists must live in ListViews.

I hope this doesn't encourage that kind of thinking.

@ewpatton [quote="ewpatton, post:4, topic:104189"]
I think the correct way to proceed from here would be to add a new AddElements method similar to the Remove function added in the last release that handles adding elements and updating any internal state to keep everything in sync as a single block.
[/quote]
I think this would open a can of worms, because you would like to have many of the list methods, why not: reverse list, sort list, replace item, is in list etc.
And, actually, why not?

To me a ListView is what it says: a view of a list, and this view is a container with a list in it, so I do not think any OOP principle is violated.

To me the problem that crept in, is this Layout thing which is rather difficult to create and which makes the Listview not a view of list elements anymore because what are these layout elements?

I pleaded for a SimpleListView a while ago, I still think may would appreciate this.

Even in the "old days" I always sought to use/work with an underlying ai2 list to feed into listview elements, and generally never seek to work with listview elements directly (unless, of course, I am using the advanced features of the new listview)

The issue is that this violates of the idea of encapsulation. For example, the ListView internally stores information about the current selection, such as the index and value. If you then go and say "replace element in list ListView1.Elements at index i with foo" and i happens to be ListView1.SelectionIndex, now the internal state about the status of the selected item is violated. A variation would be removing the element at index 1, causing any cached index to now be invalid.

Of course, languages like LISP have the ability to reference these items as slots on an object and provide a fairly robust means of handling different scenarios via their generic method implementation (allow for defining "after" function when a property is set, for example). In the long term we could probably support logic like this but it would be a significant development effort to do so.

Your blocks in the first instance already demonstrated part of the problem. You can update the internal list of the ListView, but to trigger any sort of redraw you still have to reassign the property to itself. In fact, I would say this might the worst case scenario because the Elements getter returns the list "by reference" but the setters acts "by value" and copies the input. Ideally, we'd want the set/get pair to either by "by reference" or "by value". Ultimately for now we have decided "by value" is the correct implementation, which results in having to make the change you do. Maybe some day with significant development effort we could switch properties to being "by reference" instead of "by value", but I am not sure how pervasive the problem is.

Currently working with ListView. I will try to improve it a bit, but without touching the data. I will add an AddItem block and AddItemAtIndex. I can also add an AddItems block so that several items can be added at the same time. I will also try to separate updating data in the ListView from changing the appearance and creating a new adapter instance. I will also try to improve the search. However, I see a problem because Kodular has two separate ListView components.

For now, I will be interested to see how improvements will work out. I agree actually that it should not be possible to directly manipulate the list of a ListView. Probably I have some nasty bugs laying around someplace.

One possibility that I have been thinking about is potentially adopting the data source/sink logic that was created for the Chart component. Effectively, a list reference could be a data source and the list view could be a data sink. You could then say "drive this list view from this list" and then manipulating the list would automatically update the listview. The downside to this is that typically we have only allowed components to be data sources for other components and the logic might be confusing to people when applied to lists (which App Inventor treats somewhat as a primitive data structure).

Hello Ewpatton,

I see you just speak about list's? I ask Because I just was put a new cuestion a about m
Length issues, an it be that me problems are relatet because the changes of the programmings In the list blocks? Can you Take a look in this , but only when you have time, no worries please,

Thaaanks :slightly_smiling_face::slightly_smiling_face::slightly_smiling_face::slightly_smiling_face:

One possible downside of this:

If we introduce the idea of "pointers" (point to a memory address like in C++), two things can happen.

  • Reverse the elements in the ListView, then set the Label's text to the new elements.
  • Set the Label's text to a reversed list of the ListView's elements, with the ListView intact.

This would create new ambiguity. I agree that the best solution here is to introduce an AddElement method (void).

I would like to work on ListView, it may take a month, maybe more, but I would like this component to be as useful as possible and not require additional extensions. However, I will need some tips on which direction to go and what I can do. Can I write this component largely from scratch, or do I have to keep the old code base and just modify it? Can I add new classes, e.g. add a new adapter only for the simple ListView version with only the main text?

Assuming you mean to contribute to App Inventor core, I would suggest starting with a design doc that highlights the issues and what blocks/features you plan to add/modify to address them. This can help us give you feedback before too much implementation and also help us gauge the amount of effort needed for the iOS version of the implementation (unless you plan to do that as well).

Today I worked on ListView. I've made some tweaks to ListView that impact performance. The main fixes are loading data into the adapter without creating a new adapter instance. This prevents the ListView from resetting after data has been loaded. The second change is a different search/filter implementation. Now when loading a lot of data, filtering works smoothly. The error that appeared when we wanted to set the index to 0 when dictionary data was loaded has been corrected.

The changes that would need to be made in the iOS version are the new AddItem and AddItemAtIndex blocks. Additionally, the property changing the hint text in the search box. And the implementation of multiple selection, which was partly done earlier. I'm just wondering whether the indexes of many selected elements should be returned from the ListView as a list, or whether the user should create such a list himself in the AfterPicking event.

Additionally, I'm thinking about adding an item divider and the ability to add margins to the item and rounded corners to get a card view. Another change I want to add is aligning the text vertically with respect to the image and adding a reverse list display.

I might also think about adding GridLayout. But will someone add it later in the iOS version?

It sounds like you've done a bunch of work. I would suggest opening a PR against ucr in appinventor-sources. Susan should take a look at it.

So I will. I'll add a PR and continue on GitHub, although I don't fully understand it... maybe someone will build a test server so everyone can try ListView.

I forgot to mention the functionality of the AddItem and AddItemAtIndex blocks. Both blocks can accept String or Dictionary data created by the CreateElement block. I adopted the idea that if data was previously loaded from String, an attempt to add a Dictionary element will return a data incompatibility error and vice versa.

Hmm. I will need to think about that. Adding a valid dictionary item should reduce to the string form where the MainText key is used. Going the other way effectively would result in detail text and image being empty strings.

Maybe I'll just add a block like in Kodular, which has inputs for mainText, detailText and imageName and it will add data accordingly depending on whether we use Strings or Dictionary. And I have one more question. Should the methods and their parameters have the same names as in Kodular, or will some naming mapping be used when importing the project? And should I add all the properties and functions that are in Kodular in both ListView versions?