Android Tutorial πŸ’‘ Part 2 Examples - Creating long lists using xCustomListView with Lazy Loading - Newer developers

kps

Member
@Peter Simpson
Thank you for the example under #1.
I have always felt comfortable and very educative to take an existing example, modify it, play with it and understand how it works.

Out of curiosity, I wanted to check if the search feature works for non-English languages also.
I modified the names in chinook.db and tried it out.
It works!
I am so excited !

Thanks a lot.

Best regards,
KPS
 

Daniel44

Active Member
Licensed User

Hey Sir wonderful job it is very useful. Can you answer a question? I have a database with Countries and their cities and I'm using your Airport Example but I don't know how group the countries with their cities, in the airport example the header shows the airport name and below its city. I'm looking for something like than but below the header all Country's cities Can you post an example ? Thank you Sir
 

Peter Simpson

Expert
Licensed User
Longtime User

Hello @Daniel44,
Working from memory (or should I say what I think is a logical design), I think that the following should work. I've not tested it, I'm answering your question directly on my phone, I have not computers or laptops currently switched on.
B4X:
SELECT * FROM airports ORDER BY Country, City ASC;
Or something like that.

Enjoy...
 

Daniel44

Active Member
Licensed User
Thank you for asking Sir. Maybe I didn't explain myself well. I've changed this:

B4X:
If New.Length = 0  Then
        AirportsCount = Starter.SQL.ExecQuerySingleResult("SELECT COUNT(*) FROM `airports`;") 'The amuont of airports listed in the database
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT * FROM `airports` ORDER BY `Name` ASC LIMIT ${Limit};"$, Null) 'I NO NOT RECOMMEND that you load the whole database
    Else
        AirportsCount = 0
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT * FROM `airports` WHERE `Name` Like '%${New}%' ORDER BY `Name` ASC LIMIT 500;"$, Null) 'Limited for slower devices
    End If

    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
    If Success Then
        Dim StartTime As Long = DateTime.Now
        Do While rs.NextRow
            Dim AD As AirportData
                AD.Initialize
                AD.Name    = rs.GetString("Name")
                AD.AirportID = rs.GetString("Airport ID")
                AD.IATA    = rs.GetString("IATA")
                AD.ICAO    = rs.GetString("ICAO")
                AD.City    = rs.GetString("City")
                AD.Latitude    = rs.GetString("Latitude")
                AD.Longitude = rs.GetString("Longitude")
                AD.Altitude    = $"${rs.GetString("Altitude in feet")} ft"$
                
            Dim Pnl As B4XView = XUI.CreatePanel("")
            Pnl.SetLayoutAnimated(0, 0, 0, CLVAirports.AsView.Width, 130dip) 'Panel height + 4 for drop shadow
            CLVAirports.Add(Pnl, AD)
        Loop
        rs.Close



To this:
B4X:
Dim SenderFilter As Object
    If New.Length = 0  Then
        AirportsCount = Starter.SQL.ExecQuerySingleResult("SELECT COUNT(*) FROM `airports`;") 'The amuont of airports listed in the database
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT  * FROM airports order by `Country`,`City` ASC LIMIT ${Limit};"$, Null) 'I NO NOT RECOMMEND that you load the whole database
    Else
        AirportsCount = 0
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT * FROM `airports` WHERE `Country` Like '%${New}%' ORDER BY `Country` ASC LIMIT 500;"$, Null) 'Limited for slower devices
    End If

    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
    If Success Then
        Dim StartTime As Long = DateTime.Now
        Do While rs.NextRow
            Dim AD As AirportData
                AD.Initialize
                AD.Name    = rs.GetString("Country")
                AD.AirportID = rs.GetString("Airport ID")
                AD.IATA    = rs.GetString("IATA")
                AD.ICAO    = rs.GetString("ICAO")
                AD.City    = rs.GetString("City")
                AD.Latitude    = rs.GetString("Latitude")
                AD.Longitude = rs.GetString("Longitude")
                AD.Altitude    = $"${rs.GetString("Altitude in feet")} ft"$
                
            Dim Pnl As B4XView = XUI.CreatePanel("")
            Pnl.SetLayoutAnimated(0, 0, 0, CLVAirports.AsView.Width, 130dip) 'Panel height + 4 for drop shadow
            CLVAirports.Add(Pnl, AD)
        Loop
        rs.Close

these changes show me this:


and I would like this:



I mean i.e. each country with all its respective cities without the country repeating itself

Would that be possible? If so, what should I change?. Thank you Sir.
 

Unobtainius

Active Member
Licensed User
Longtime User
@Peter Simpson
ive just finished watching the b4a to b4j video and am simply mind blown. I know we are aiming to write b4x cross platform stuff and in a way your video demonstrates just why we can. I couldn't believe how easily you did it. Bravo sir and I especially loved the what the ?? bit. I'm in a hospital waiting room just now (for my old mum) so will have to wait to have a look at the lazy loading examples but I'm sure they will provide me lots of insight. Thanks Peter, keep the exceptional work
 

QinLiwei

New Member
Hi, Peter Simpson, I can't download these examples because I can't access www.dropbox.com, but I really need them. Could you upload them to the B4X forum or send me an email?My email address is :88251404@qq.com. I am looking forward to your reply. Thank you very much!
 

anOparator

Active Member
Licensed User
Longtime User
This is another good post on CustomListView but I get this error compiling the Invoices example, in Debug and Release mode using B4A 10.2
B4X:
Copying updated assets files (6)
*** Service (starter) Create ***
java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/core/content/ContextCompat;
    at anywheresoftware.b4a.objects.RuntimePermissions.GetSafeDirDefaultExternal(RuntimePermissions.java:120)
    at com.simplysoftware.chinookinvoices.starter$ResumableSub_Service_Create.resume(starter.java:188)
    at com.simplysoftware.chinookinvoices.starter._service_create(starter.java:165)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:348)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at com.simplysoftware.chinookinvoices.starter.onCreate(starter.java:56)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3342)
    at android.app.ActivityThread.-wrap4(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1680)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6518)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.core.content.ContextCompat" on path: DexPathList[[zip file "/data/app/com.simplysoftware.chinookinvoices-n5cNwPjejJXmRQHC4CW1GA==/base.apk"],nativeLibraryDirectories=[/data/app/com.simplysoftware.chinookinvoices-n5cNwPjejJXmRQHC4CW1GA==/lib/arm, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    ... 20 more
java.lang.RuntimeException: Unable to create service com.simplysoftware.chinookinvoices.starter: java.lang.RuntimeException: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/core/content/ContextCompat;
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3352)
    at android.app.ActivityThread.-wrap4(Unknown Source:0)
It was uploaded to https://www.dropbox.com/s/6c669byshlonjrn/xCLV LL Invoices.zip?dl=0
What to do, what to do?
 

DonManfred

Expert
Licensed User
Longtime User
This is another good post on CustomListView but I get this error compiling the Invoices example, in Debug and Release mode using B4A 10.2
1. You should create a new thread for any issue you have
2. You are not using AndroidX. You still use android-support which is deprecated and not longer available.
Follow the Installation instructions carefully: https://www.b4x.com/b4a.html
 

Unobtainius

Active Member
Licensed User
Longtime User
If you think about the custom list control simply as a list that can accept any layout you want to place into each item, it opens your mind to many possibilities. In general people use it to show the same type of item layout over and over, but you don't have to.

For what you are looking to achieve I would have a layout for the header (country) and a layout for the airport. Then have a variable to hold the country name and if the country you are about to insert is different from that, insert the header as a new item, then the airport as a new item. If the country has not changed then just add the airport as an item. Just remember to update your variable to the country being inserted after your decision.

Just one possible solution
 

Daniel44

Active Member
Licensed User
Thank you @Unobtainius
 
Hi @Unobtainius and @Daniel44,

I have just made it a header for a group of airport(s) with same country.
I make it because it is very helpful if customlistview present items grouped in section.


The layout Airports.bal is splitted into airportinfo1.bal for header and airportinfo2.bal for item


airportinfo1.bal


airportinfo2.bal

VisibleRangeChanged event:
Sub CLVAirports_VisibleRangeChanged (FirstIndex As Int, LastIndex As Int)
    LogColor("FirstIndex: " & FirstIndex & CRLF & "LastIndex: " & LastIndex, Colors.Magenta)
    Dim ExtraSize As Int = 25 'List size
    For i = Max(0, FirstIndex - ExtraSize) To Min(LastIndex + ExtraSize, CLVAirports.Size - 1)
        Dim Pnl As B4XView = CLVAirports.GetPanel(i)
        If i > FirstIndex - ExtraSize And i < LastIndex + ExtraSize Then
            If Pnl.NumberOfViews = 0 Then 'Add each item/layout to the list/main layout               
                Select Pnl.Tag
                    Case "header"
                        Dim country_1 As String = CLVAirports.GetValue(i)
                        Pnl.LoadLayout("airportinfo1")
                        LblName.Text = country_1
                    Case "item"
                        Dim AD As AirportData = CLVAirports.GetValue(i)
                        Pnl.LoadLayout("airportinfo2")
                        LblAirportId.Text = AD.AirportID
                        LblIATA.Text = AD.IATA
                        LblICAO.Text = AD.ICAO
                        LblCity.Text = AD.City
                        LblLatitude.Text = AD.Latitude
                        LblLongitude.Text = AD.Longitude
                        LblAltitude.Text = AD.Altitude
                    Case Else
                        Continue
                End Select               
            End If
        Else 'Not visible
            If Pnl.NumberOfViews > 0 Then
                Pnl.RemoveAllViews 'Remove none visable item/layouts from the list/main layout
            End If
        End If
    Next
End Sub

CLVAirports Add Row:
Sub TxtSearchFilter_TextChanged (Old As String, New As String)
    If New.Length = 1 Or New.Length = 2 Then Return
    CLVAirports.Clear
    Sleep(0)

    Dim SenderFilter As Object
    If New.Length = 0 Then
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT  * FROM airports order by `Country`,`City` ASC LIMIT 500;"$, Null)
    Else
        SenderFilter = Starter.sql.ExecQueryAsync("SQL", $"SELECT * FROM `airports` WHERE `Country` Like '%${New}%' ORDER BY `Country` ASC LIMIT 500;"$, Null) 'Limited for slower devices
    End If

    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, RS As ResultSet)
    If Success Then
        Dim StartTime As Long = DateTime.Now
        Dim idx As Int
        Dim currCountry As String = ""
        Do While RS.NextRow
            idx = idx + 1           
            If currCountry <> RS.GetString("Country") Then
                currCountry = RS.GetString("Country")
                Dim PnlHeader As B4XView = XUI.CreatePanel("")
                PnlHeader.Tag = "header"
                'Dim Height As Int = 60dip
                PnlHeader.SetLayoutAnimated(0, 0, 0, CLVAirports.AsView.Width, 65dip) 'Panel height + 4 for drop shadow
                CLVAirports.Add(PnlHeader, idx & ". " & RS.GetString("Country"))
            End If
            Dim Ad As AirportData
            Ad.Initialize
            Ad.Name    = RS.GetString("Country")
            Ad.AirportID = RS.GetString("Airport ID")
            Ad.IATA    = RS.GetString("IATA")
            Ad.ICAO    = RS.GetString("ICAO")
            Ad.City    = RS.GetString("City")
            Ad.Latitude    = RS.GetString("Latitude")
            Ad.Longitude = RS.GetString("Longitude")
            Ad.Altitude    = $"${RS.GetString("Altitude in feet")} ft"$               
            Dim PnlItem As B4XView = XUI.CreatePanel("")
            PnlItem.Tag = "item"
            PnlItem.SetLayoutAnimated(0, 0, 0, CLVAirports.AsView.Width, 70dip) 'Panel height + 4 for drop shadow
            CLVAirports.Add(PnlItem, Ad)
        Loop
        RS.Close       
        Log($"List population time = ${NumberFormat2((DateTime.Now - StartTime) / 1000, 1, 2, 2, False)} seconds to populate ${CLVAirports.Size} airport names"$)
    Else
        Log(LastException)
    End If   
    'Add an icon to the activity text by using CSBuilder
    Activity.Title = CS.Initialize.Typeface(Typeface.FONTAWESOME).Append(Chr(0xF072)).Append($" ${CLVAirports.Size} Airports"$).PopAll
End Sub
 

beelze69

Active Member
Licensed User
Longtime User
Hi !,

I ran your Airports Lazy Loading Example.. It is a great program.

However, I encountered a small issue when running it in 'debug' mode and I press the 'back' button on my smartphone.

It is giving me the following error:

Unexpected event (missing RaiseSynchronousEvents): txtsearchfilter_textchanged
Check the unfiltered logs for the full stack trace.


Requesting for help.

Thanks..
 

Daniel44

Active Member
Licensed User
Thank you so much Sir!
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…