Android Question Lazy loading in xCustomListView

Sergey_New

Well-Known Member
Licensed User
Longtime User
Created CLV to display media files (gif, jpg, png, pdf) and their names.
B4X:
Sub Process_Globals
    Private xui As XUI
End Sub

Sub Globals
    Private CLV1 As CustomListView
    Private ImView As B4XView
    Private lblTitle As B4XView
    Dim folder As String
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Dim media As ClassMedia
    Dim filename As String
    Dim n As Int
    Activity.LoadLayout("1")
    For Each k As String In parser.db.media.Keys
        If parser.db.media.ContainsKey(k) Then
            media=parser.db.media.Get(k)
            n=media.mediaFile.LastIndexOf2("\",media.mediaFile.Length-1)
            filename=media.mediaFile.SubString(n+1)
            If media.form="PDF" Then
                filename="pdf.png"
                folder=File.DirAssets
            Else
                folder=Starter.myFolder
            End If
            CLV1.Add(CreateItem(CLV1.AsView.Width, media.titl, filename), "")
        End If
    Next
End Sub

Private Sub CreateItem(Width As Int, Title As String, Image As String) As Panel
    Dim p As B4XView = xui.CreatePanel("")
    Dim height As Int = 50dip
    p.SetLayoutAnimated(0, 0, 0, Width, height)
    p.LoadLayout("Card1")
    lblTitle.TextSize=Starter.TextSize
    lblTitle.Text = Title
    ImView.SetBitmap(xui.LoadBitmapResize(folder, Image, ImView.Width, ImView.Height, True))
    Return p
End Sub
Works well, but the loading time for all files is more than 3 seconds.
Please help with using Lazy loading. How should I change my code?
Thanks!
 

Lucas Siqueira

Active Member
Licensed User
Longtime User
When you have a list with many items or when you have a list with images, loading may take a while, it is recommended to use your CLV VisibleRangeChanged


output3.gif


here is an example that combines ReachEnd + VisibleRangeChanged.




B4XMainPage

B4X:
#Region Shared Files
#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files"
'Ctrl + click to sync files: ide://run?file=%WINDIR%\System32\Robocopy.exe&args=..\..\Shared+Files&args=..\Files&FilesSync=True
#End Region

'Ctrl + click to export as zip: ide://run?File=%B4X%\Zipper.jar&Args=exemploLista50RegistrosETodosVisiveis.zip

Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
  
    Public pgCLVReachEnd As pageCLVReachEnd
    Public pgCLVVisibleRangeChanged As pageCLVVisibleRangeChanged
    Public pgCLVCombinado As pageCLVCombinado
  
    Public SQL1 As SQL
    Private Nomes As List = Array As String("Lucas", "Miguel", "Alice", "Sofia", "João", "Helena", "Gabriel", "Isabella", "Bernardo", "Lorenzo")
    Private Sobrenomes As List = Array As String("Silva", "Santos", "Oliveira", "Souza", "Pereira", "Lima", "Carvalho", "Ferreira", "Ribeiro", "Almeida")
    Private Cidades As List = Array As String("São Paulo", "Rio de Janeiro", "Belo Horizonte", "Salvador", "Curitiba", "Porto Alegre", "Recife", "Fortaleza", "Brasília", "Manaus")
    Private Estados As List = Array As String("SP", "RJ", "MG", "BA", "PR", "RS", "PE", "CE", "DF", "AM")
    Private bitmaps As List = Array("pexels-photo-446811.jpeg", "pexels-photo-571195.jpeg", "pexels-photo-736212.jpeg", "pexels-photo-592798.jpeg")
End Sub

Public Sub Initialize
'    B4XPages.GetManager.LogEvents = True
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
  
    B4XPages.AddPage("pgCLVReachEnd", pgCLVReachEnd.Initialize)
    B4XPages.AddPage("pgCLVVisibleRangeChanged", pgCLVVisibleRangeChanged.Initialize)
    B4XPages.AddPage("pgCLVCombinado", pgCLVCombinado.Initialize)
  
    #if b4j
        xui.SetDataFolder("b4x")
        sql.InitializeSQLite(xui.DefaultFolder, "dados.db", True)
    #Else
        SQL1.Initialize(xui.DefaultFolder, "dados.db", True)
    #End If
  
    Dim n As Long = DateTime.Now
  
    Wait for (GeraDadosAleatorios(5000)) Complete (success As Boolean)
  
    Log("A inserção dos dados levou: " & (DateTime.Now - n) & "ms")
  
    Log("Dados inseridos com sucesso.")
End Sub

Private Sub btnCLVReachEnd_Click
    B4XPages.ShowPage("pgCLVReachEnd")
End Sub

Private Sub btnCLVVisibleRangeChanged_Click
    B4XPages.ShowPage("pgCLVVisibleRangeChanged")
End Sub

Private Sub btnCLVCombinado_Click
    B4XPages.ShowPage("pgCLVCombinado")
End Sub




#Region FUNCOES

Sub CriaTabela As ResumableSub
    ' Cria a tabela no banco de dados SQLite
    SQL1.ExecNonQuery("CREATE TABLE IF NOT EXISTS contatos (id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT, email TEXT, telefone TEXT, localizacao TEXT, imagem TEXT)")
    Return True
End Sub

Sub GeraDadosAleatorios(quantidade As Int) As ResumableSub
  
    Wait for (CriaTabela) Complete (success As Boolean)
  
    Dim qtd As Int = SQL1.ExecQuerySingleResult("SELECT count(*) FROM contatos")
  
    If qtd > 0 Then
        Return True
    End If
  
    Dim nome, sobrenome, email, telefone, cidade, estado, localizacao, imagem As String
    Dim i As Int
  
    SQL1.BeginTransaction
    Try
        For i = 1 To quantidade
            nome = Nomes.Get(Rnd(0, Nomes.Size))
            sobrenome = Sobrenomes.Get(Rnd(0, Sobrenomes.Size))
            email = nome.ToLowerCase & "." & sobrenome.ToLowerCase & "@exemplo.com"
            telefone = GeraTelefone
            cidade = Cidades.Get(Rnd(0, Cidades.Size))
            estado = Estados.Get(Rnd(0, Estados.Size))
            localizacao = cidade & " - " & estado
            imagem = bitmaps.Get(Rnd(0, bitmaps.Size))
          
            SQL1.ExecNonQuery2("INSERT INTO contatos (nome, email, telefone, localizacao, imagem) VALUES (?, ?, ?, ?, ?)", Array As String(nome & " " & sobrenome, email, telefone, localizacao, imagem))
        Next
        SQL1.TransactionSuccessful
    Catch
        Log(LastException)
        #if b4j or b4i
            SQL1.RollBack
        #End If
    End Try
  
    #if b4a
        SQL1.EndTransaction
    #End If
    Return True
End Sub

Sub GeraTelefone() As String
    ' Gera um número de telefone realista no formato (XX) XXXXX-XXXX
    Dim ddd As Int = Rnd(11, 99)  ' Código de área entre 11 e 99
    Dim numero As Int = Rnd(10000, 99999)
    Dim sufixo As Int = Rnd(1000, 9999)
    Return $"(${ddd}) ${numero}-${sufixo}"$
End Sub


Public Sub abrirProgresso(texto As String)
    #if b4a
    ProgressDialogShow2(texto, False)
    #else if b4i
    hud1.ProgressDialogShow(texto)
    #End If
End Sub

Public Sub fecharProgresso
    #if b4a
    ProgressDialogHide
    #else if b4i
    hud1.ProgressDialogHide
    #End If
End Sub

Public Sub abrirToast(texto As String, demorar As Boolean)
    #if b4a
    ToastMessageShow(texto, demorar)
    #else if b4i
    hud1.ToastMessageShow(texto, demorar)
    #End If
End Sub

#End Region


pageCLVReachEnd
B4X:
Sub Class_Globals
    Private Root As B4XView 'ignore
    Private xui As XUI 'ignore
 
    Private CLV1 As CustomListView
    Private lblID As B4XView
    Private lblNome As B4XView
    Private lblEndereco As B4XView
    Private lblTelefone As B4XView
    Private lblEmail As B4XView
    Private imvImagem As B4XImageView
 
    Private xOFFSET As Int = 0
    Private xCARREGANDO As Boolean = False
End Sub

'You can add more parameters here.
Public Sub Initialize As Object
    Return Me
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("1")
 
    Dim n As Long = DateTime.Now
    CarregarDados(xOFFSET, 50)
    Log("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms")
 
    B4XPages.MainPage.abrirToast("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms", False)
End Sub

Private Sub CLV1_ReachEnd
    If xCARREGANDO Then
        B4XPages.MainPage.abrirToast("carregando, aguarde...", False)
        Return
    End If
    ' Carrega o próximo bloco de 50 registros ao chegar ao final
    CarregarDados(xOFFSET, 50)
End Sub

Sub CarregarDados(offset As Int, limite As Int)
    xCARREGANDO = True
 
    B4XPages.MainPage.abrirProgresso("carregando, aguarde...")
 
    Sleep(10)
 
    Dim rs As ResultSet = B4XPages.MainPage.SQL1.ExecQuery2("SELECT * FROM contatos LIMIT ? OFFSET ?", Array As String(limite, offset))
    Do While rs.NextRow
        Dim id As String = NumberFormat2(rs.GetString("id"), 5, 0, 0, False)
        Dim nome As String = rs.GetString("nome")
        Dim email As String = rs.GetString("email")
        Dim telefone As String = rs.GetString("telefone")
        Dim localizacao As String = rs.GetString("localizacao")
        Dim imagem As String = rs.GetString("imagem")
    
        ' Adicione o registro ao CustomListView
        AdicionarItem(id, nome, email, telefone, localizacao, imagem)
    
        'Adiciona um incremento no offset para saber até onde ele já carregou
        xOFFSET = xOFFSET + 1
    Loop
    rs.Close
 
    B4XPages.MainPage.fecharProgresso
 
    xCARREGANDO = False
End Sub

Sub AdicionarItem(id As String, nome As String, email As String, telefone As String, localizacao As String, imagem As String)
    Dim pnl As B4XView = xui.CreatePanel("")
    pnl.SetLayoutAnimated(0, 0, 0, CLV1.AsView.Width, 100dip)
    pnl.LoadLayout("card1") ' Certifique-se de ter um layout 'ItemLayout' configurado

    lblID.Text = "#" & id
    lblNome.Text = nome
    lblEndereco.Text = localizacao
    lblEmail.Text = email
    lblTelefone.Text = telefone
    imvImagem.Load(File.DirAssets, imagem)

    CLV1.Add(pnl, nome) ' Adiciona o painel ao CustomListView
End Sub

Private Sub CLV1_ItemClick (Index As Int, Value As Object)
    xui.MsgboxAsync(Value, "Nome")
End Sub

pageCLVVisibleRangeChanged
B4X:
Sub Class_Globals
    Private Root As B4XView 'ignore
    Private xui As XUI 'ignore
 
    Private CLV1 As CustomListView
    Private lblID As B4XView
    Private lblNome As B4XView
    Private lblEndereco As B4XView
    Private lblTelefone As B4XView
    Private lblEmail As B4XView
    Private imvImagem As B4XImageView
End Sub

'You can add more parameters here.
Public Sub Initialize As Object
    Return Me
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("1")
 
    Dim n As Long = DateTime.Now
    carregarTodosDados
    Log("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms")
 
    B4XPages.MainPage.abrirToast("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms", False)
End Sub

Private Sub CLV1_VisibleRangeChanged (FirstIndex As Int, LastIndex As Int)
    Dim extra1 As Int = 10
    For i = Max(0, FirstIndex) To CLV1.Size - 1
        Dim p As B4XView  = CLV1.GetPanel(i)
        If (i > FirstIndex - extra1) And (i < LastIndex  + extra1) Then
            If p.NumberOfViews = 0 Then
            
                p.LoadLayout("card1") ' Certifique-se de ter um layout 'ItemLayout' configurado
            
                Dim dadosContato As Map = CLV1.GetValue(i)
                lblID.Text = "#" & dadosContato.Get("id")
                lblNome.Text = dadosContato.Get("nome")
                lblEndereco.Text = dadosContato.Get("localizacao")
                lblEmail.Text = dadosContato.Get("email")
                lblTelefone.Text = dadosContato.Get("telefone")
                imvImagem.Load(File.DirAssets, dadosContato.Get("imagem"))
            
            End If
        Else
            If p.NumberOfViews > 0 Then
                p.RemoveAllViews
            End If
        End If
    Next
End Sub

Sub carregarTodosDados
    B4XPages.MainPage.abrirProgresso("carregando, aguarde...")
 
    Sleep(10)
 
    Dim rs As ResultSet = B4XPages.MainPage.SQL1.ExecQuery("SELECT * FROM contatos")
    Do While rs.NextRow
        Dim id As String = NumberFormat2(rs.GetString("id"), 5, 0, 0, False)
        Dim nome As String = rs.GetString("nome")
        Dim email As String = rs.GetString("email")
        Dim telefone As String = rs.GetString("telefone")
        Dim localizacao As String = rs.GetString("localizacao")
        Dim imagem As String = rs.GetString("imagem")
 
        ' Adicione o registro ao CustomListView
        AdicionarItemEmBranco(id, nome, email, telefone, localizacao, imagem)
    Loop
    rs.Close
 
    B4XPages.MainPage.fecharProgresso
End Sub

Sub AdicionarItemEmBranco(id As String, nome As String, email As String, telefone As String, localizacao As String, imagem As String)
    Dim pnl As B4XView = xui.CreatePanel("")
    pnl.SetLayoutAnimated(0, 0, 0, CLV1.AsView.Width, 100dip)
 
    Dim dadosContato As Map
    dadosContato.initialize
    dadosContato.put("id", id)
    dadosContato.put("nome", nome)
    dadosContato.put("localizacao", localizacao)
    dadosContato.put("email", email)
    dadosContato.put("telefone", telefone)
    dadosContato.put("imagem", imagem)
 
    CLV1.Add(pnl, dadosContato) ' Adiciona o painel vazio ao CustomListView
End Sub

Private Sub CLV1_ItemClick (Index As Int, Value As Object)
    xui.MsgboxAsync(Value, "Nome")
End Sub


pageCLVCombinado
B4X:
Sub Class_Globals
    Private Root As B4XView 'ignore
    Private xui As XUI 'ignore
  
    Private CLV1 As CustomListView
    Private lblID As B4XView
    Private lblNome As B4XView
    Private lblEndereco As B4XView
    Private lblTelefone As B4XView
    Private lblEmail As B4XView
    Private imvImagem As B4XImageView
  
    Private xOFFSET As Int = 0
    Private xCARREGANDO As Boolean = False
End Sub

'You can add more parameters here.
Public Sub Initialize As Object
    Return Me
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("1")
  
    Dim n As Long = DateTime.Now
    CarregarDados(xOFFSET, 50)
    Log("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms")
  
    B4XPages.MainPage.abrirToast("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms", False)
End Sub

Private Sub CLV1_VisibleRangeChanged (FirstIndex As Int, LastIndex As Int)
    Dim extra1 As Int = 10
    For i = Max(0, FirstIndex) To CLV1.Size - 1
        Dim p As B4XView  = CLV1.GetPanel(i)
        If (i > FirstIndex - extra1) And (i < LastIndex  + extra1) Then
            If p.NumberOfViews = 0 Then
              
                p.LoadLayout("card1") ' Certifique-se de ter um layout 'ItemLayout' configurado
              
                Dim dadosContato As Map = CLV1.GetValue(i)
                lblID.Text = "#" & dadosContato.Get("id")
                lblNome.Text = dadosContato.Get("nome")
                lblEndereco.Text = dadosContato.Get("localizacao")
                lblEmail.Text = dadosContato.Get("email")
                lblTelefone.Text = dadosContato.Get("telefone")
                imvImagem.Load(File.DirAssets, dadosContato.Get("imagem"))
              
            End If
        Else
            If p.NumberOfViews > 0 Then
                p.RemoveAllViews
            End If
        End If
    Next
End Sub

Private Sub CLV1_ReachEnd
    If xCARREGANDO Then
        B4XPages.MainPage.abrirToast("carregando, aguarde...", False)
        Return
    End If
    ' Carrega o próximo bloco de 50 registros ao chegar ao final
    CarregarDados(xOFFSET, 50)
End Sub

Sub CarregarDados(offset As Int, limite As Int)
    xCARREGANDO = True
  
    B4XPages.MainPage.abrirProgresso("carregando, aguarde...")
  
    Sleep(10)
  
    Dim rs As ResultSet = B4XPages.MainPage.SQL1.ExecQuery2("SELECT * FROM contatos LIMIT ? OFFSET ?", Array As String(limite, offset))
    Do While rs.NextRow
        Dim id As String = NumberFormat2(rs.GetString("id"), 5, 0, 0, False)
        Dim nome As String = rs.GetString("nome")
        Dim email As String = rs.GetString("email")
        Dim telefone As String = rs.GetString("telefone")
        Dim localizacao As String = rs.GetString("localizacao")
        Dim imagem As String = rs.GetString("imagem")
      
        ' Adicione o registro ao CustomListView
        AdicionarItemEmBranco(id, nome, email, telefone, localizacao, imagem)
      
        'Adiciona um incremento no offset para saber até onde ele já carregou
        xOFFSET = xOFFSET + 1
    Loop
    rs.Close
  
    B4XPages.MainPage.fecharProgresso
  
    xCARREGANDO = False
End Sub

Sub AdicionarItemEmBranco(id As String, nome As String, email As String, telefone As String, localizacao As String, imagem As String)
    Dim pnl As B4XView = xui.CreatePanel("")
    pnl.SetLayoutAnimated(0, 0, 0, CLV1.AsView.Width, 100dip)
  
    Dim dadosContato As Map
    dadosContato.initialize
    dadosContato.put("id", id)
    dadosContato.put("nome", nome)
    dadosContato.put("localizacao", localizacao)
    dadosContato.put("email", email)
    dadosContato.put("telefone", telefone)
    dadosContato.put("imagem", imagem)
  
    CLV1.Add(pnl, dadosContato) ' Adiciona o painel vazio ao CustomListView
End Sub

Private Sub CLV1_ItemClick (Index As Int, Value As Object)
    xui.MsgboxAsync(Value, "Nome")
End Sub
 

Attachments

  • exemploLista50RegistrosETodosVisiveisECombinado.zip
    126.9 KB · Views: 7
Last edited:
Upvote 0

Lucas Siqueira

Active Member
Licensed User
Longtime User
Lucas Siqueira, thanks for your input, but I would like to use Lazy loading.

The simplest way is as i sent you using VisibleRangeChanged in your CLV


But you can use [B4X] PreoptimizedCLV - Lazy loading extension for xCustomListView

or [B4X] [XUI] CustomListView - lazy loading / virtualization
 
Upvote 0

Sergey_New

Well-Known Member
Licensed User
Longtime User
At first I wanted to use PreoptimizedCLV, but I haven't figured out how to do it yet.
 
Upvote 0
Top