B4A Library [class] Modified CustomListView with "Pull to refresh" feature

Update: New version based on xCustomListView: https://www.b4x.com/android/forum/threads/b4x-xcustomlistview-pull-to-refresh.94015/

This class is a modified version of CustomListView. It adds a "pull to refresh" feature.


When the user pull down the panel a refresh event is fired. You should catch this event and update the list as necessary. In this example a timer is used to simulate a long operation.

- You should call clv.AddTopPanel after you add the items with the top panel layout.
- If the list is too short for the scroll view to be scrollable then a "dummy" item will be added as the last item.
- You should call StopRefresh after the update.


  • PullToRefresh.zip
    10.7 KB · Views: 691
Last edited:


Licensed User
I wanted to do a tutorial a few months ago on the pull-to-refresh and other techniques used for lists, but the legal situation was not very clear. There was apparently a patent on the pull-to-refresh belonging to Twitter, which had abruptly stopped my intent. Now things are more clear. From a legal point of view, we will not be allowed very soon to use it without authorization but Twitter is not Apple and it doesn't seem interested in suing people. I think there's nothing serious to fear actually but I prefer to warn everyone tempted to include it in his/her application. Source.
Last edited:


B4X founder
Staff member
Licensed User
@Informatix, I did some searches about this issue:

1. Twitter has an internal agreement with its engineers about using their patents only for defensive purposes: Twitter introduces Innovators Patent Agreement, vows to not abuse patent system
2. There are thousands of iOS and Android applications that use this feature. Including Facebook and other popular apps.
3. There are many frameworks for both iOS and Android that implement this feature.

I'm not a lawyer but I don't see any reason not to use this feature.

If anyone wishes to further discuss this issue then please start a new thread.


Active Member
Licensed User
_Refresh event triggered

This seems to work pretty well on the whole, but for some reason I'm occasionally seeing the Refresh event triggered, even if the screen is not touched.


Active Member
Licensed User
I've not yet managed to find a consistent pattern, despite throwing in some extra logging; it was obvious something was triggering the refresh - my app shows a list of calendar events for the next 30 days, retrieved from a server, and I used the refresh to fetch the complete list, for the whole year.

Sometimes, shortly after fetching the 30 day list, it would update with the whole thing.

I'm using the same CustomListView and simply calling the Clear method when I retrieve a new set of data from the server.

I've now added

TopPanelVisible = False
TopPanelRefreshing = False

to the end of Public Sub Clear in the CustomListView and I think that's done the trick.


Active Member
Licensed User

Another quirk; most of the time, I can deal with the object that is returned by ItemClick, but if I want to delete it (and for some other operations), I need the index. That, it appears, is not always consistent.

I have my clv, dataList, and I've popped in this bit of code:

Sub dataList_ItemClick( index As Int, Value As Object )

   Log("Clicked on item " & index)

plus some extra log items to tell me when a refresh is triggered. The particular list I noticed this on had only one element, but the logging shows it's happening with more.

I start the app, tap on the item in the list, and it has an index of 0, as I'd expect.

But if I then trigger the refresh event, after which my code calls dataList_Clear, and the refills the clv, clicking on that first item returns an index of 1 (which, consequently, calls other things to fail).

Should I instead be re-initialising the clv, rather than simply calling Clear when I need to reload it?


Active Member
Licensed User
It's the correct index for the clv, but there can still be quirks.

For example, if I retrieve a list of messages in my app, I store them in a normal list, and display them in the clv. I can come back to the same list from elsewhere, without having to reload from the server, eg on an orientation change.

But, when a user wants to locally delete a message, via the clv, where I would previously use the clv remove item method and then delete the matching index from my original list of messages, I can't do that, as sometimes I'll be deleting the wrong item, if the indices no longer match.

I can work round it, by iterating over my original list and checking for the message id element to find the matching one, but it's a quirk that might catch other people out, if they're doing something similar, especially copying a list into a clv, so I thought it worth mentioning.


Licensed User
Is it expected that the refresh is only executed if the list is scrolled 1st? For short list that do not always expand beyond the clv panel/view length, I've been unable to get the pull to refresh to fire, as I am unable to scroll the list contents, which seems to be a prerequisite.

Also, is there a way to disable the refresh function, to minimize excessive refreshing? I know I could bypass in the _refresh code, but is there a way to disable (and re-enable) the function, to avoid showing the Refresh Panel at all?

Excellent Addition to functionality!



Licensed User
Thanks. I also had to graft the Sub DesignerCreateView() function into this version to get it to allow the CustomListView to be used in the Designer. For anyone else running into that.



Active Member
Licensed User
i have a project that i used the normall customlistview

i replaced the class withe the pull to refresh code
then i added the pulltorefresh.pal
and added

    Dim p As Panel
    clv.AddTopPanel(p, 50dip)

but im getting this error

** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **

main_rss_finished (java line: 640)
java.lang.Exception: Sub designercreateview was not found.
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
at anywheresoftware.b4a.objects.CustomViewWrapper.AfterDesignerScript(CustomViewWrapper.java:67)
at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:158)
at anywheresoftware.b4a.objects.ActivityWrapper.LoadLayout(ActivityWrapper.java:209)
at anywheresoftware.b4a.samples.customlistview.main._rss_finished(main.java:640)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
at anywheresoftware.b4a.BA$2.run(BA.java:328)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
java.lang.Exception: Sub designercreateview was not found.
Panel size is unknown. Layout may not be loaded correctly.

any ideas ?