Italian (Risolto) Polpolare clv 50 record per volta

Xfood

Expert
Licensed User
Ciao a tutti ,
volevo chiedrvi, come si puo realizzare una query sql e di conseguenza popolare un clv con 50 record per volta,
mi spiego:
ho un tabella con 5000 record, diciamo con codice ,descrizione,reparto, foto
per evitare una select del tipo Select * from articoli where reparto=1, mi ritornerebbe circa 1000 articoli con relative foto, e quindi vorrei evitare questo flusso di dati cosi impattante, domani potrebbero essere anche 2000, per ogni select

vorrei in sostanza fare un po come l'app di AliExpress e tutte le app di e-commerce,
cioe anche se fai una ricerca, ti popola i prima 20 / 30 articoli trovati, poi mano mano che scorri la sua (clv) appena arriva alla fine e continu a scorrere, sembra che lancia una query delle successiva 30 righe e popola la sua ( clv) e cosi via fino a che trova un risultato

come posso implemetare tale funzione in b4A

se lancio Select * from articoli where reparto=1 limit 50
chiaramente funziona per i primi 50
vorrei che se continuo a scorrere la clv di B4A si potesse lanciare una query del tipo Select * from articoli where reparto=1 limit (da 51 a 100) non so se esiste, e cosi via

Qualche idea in merito?

Grazie e buon Pranzo
 

Xfood

Expert
Licensed User
Grazie @LucaMs
certamente utilizzo' una variabile,
adesso come posso lanciare la query sopra quando l'utente scorre la clv ed e' arrivato alla fine della clv?

la seconda,terza,quarta, ecc. queri dovrebbe essere eseguita quando l'utente arriva alla fine della clv e continua a scorrere la clv, come catturo questo evento?
 

LucaMs

Expert
Licensed User
Longtime User

Lucas Siqueira

Active Member
Licensed User
Longtime User
In allegato ho lasciato un esempio di lista che carica ogni 50 record, all'inizio c'è un codice che inserisce 5.000 record di test nel database sqlite così possiamo testare la nostra lista di caricamento.

background52.jpg


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=exemploLista50Registros.zip

Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
  
    Private 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")

    Private CLV1 As CustomListView
    Private lblNome As B4XView
    Private lblEndereco As B4XView
    Private lblTelefone As B4XView
    Private lblEmail As B4XView
    Private imvImagem As B4XImageView
  
End Sub

Public Sub Initialize
'    B4XPages.GetManager.LogEvents = True
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")
  
    #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)
  
    CarregarDados(CLV1.Size, 50)
  
    Log("O carregamento dos cartões levou: " & (DateTime.Now - n) & "ms")
  
    Log("Dados inseridos com sucesso.")
End Sub

Private Sub CLV1_ReachEnd
    ' Carrega o próximo bloco de 50 registros ao chegar ao final
    CarregarDados(CLV1.Size, 50)
End Sub

Sub CarregarDados(offset As Int, limite As Int)
    Dim rs As ResultSet = SQL1.ExecQuery2("SELECT * FROM contatos LIMIT ? OFFSET ?", Array As String(limite, offset))
    Do While rs.NextRow
        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(nome, email, telefone, localizacao, imagem)
    Loop
    rs.Close
End Sub

Sub AdicionarItem(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

    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

#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 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

#End Region
 

Attachments

  • exemploLista50RegistrosETodosVisiveisECombinado.zip
    126.9 KB · Views: 3
Last edited:

giannimaione

Well-Known Member
Licensed User
Longtime User
ok, l'approccio è buono, ma in ogni caso (ad ogni select nella sub CLV1_ReachEnd) la CustomListView si "riempe" di altri 50 record;
forse sarebbe il caso di pulire la CLV e gestire anche una sorta di "CLV1_ReachFirst" che non esiste;
 

Xfood

Expert
Licensed User
ok, l'approccio è buono, ma in ogni caso (ad ogni select nella sub CLV1_ReachEnd) la CustomListView si "riempe" di altri 50 record;
forse sarebbe il caso di pulire la CLV e gestire anche una sorta di "CLV1_ReachFirst" che non esiste;
E corretto quello che dici, ma quante volte capiterà mai che un utente scorre più di 500 articoli con il dito? Ed a ogni modo li hai caricati pian piano, senza "bloccare " il cell
 

LucaMs

Expert
Licensed User
Longtime User
Buona idea @LucaMs , potrest adattare questo esempio con questa funzione della clv?
Non Zo, semmai potrei "giocare" con l'esempio di Erel. Suppongo, senza averlo "studiato" ma solo capito il suo scopo, che non ci sia molto da lavorarci se non fargli prelevare gruppi di record (e facendo "miracoli", magari fare in modo che la funzione possa ottenerli da un db locale o da internet, a seconda di pochi parametri forniti).

Adesso, però, inizio a cercare un "date picker" che possibilmente sia una custom view B4XLib, creata da Erel ed abbia un evento che mi servirebbe (ma, appunto, se fosse una B4XLib, potrei sempre modificare il sorgente).
 

LucaMs

Expert
Licensed User
Longtime User
Adesso, però, inizio a cercare un "date picker" che possibilmente sia una custom view B4XLib, creata da Erel ed abbia un evento che mi servirebbe (ma, appunto, se fosse una B4XLib, potrei sempre modificare il sorgente).
Ci sarebbe la B4XDialog + B4XDateTemplate, senza eventi che mi servirebbero.

Ho pure scoperto una cosa anomala (e fastidiosa, quando stai "debuggando"): passando il mouse su un Pane (B4J), scatta l'evento Touch! Semmai dovrebbe essere Hover.
Va beh.

Quello che mi servirebbe è che ad ogni cambio mese, prima che tutti i giorni vengano "disegnati", io possa decidere se uno o più debbano avere un aspetto diverso in base a certe condizioni, ovvero renderli "sensibili" o "insensibili" al click, attivi/disattivati.
Lo scrivo nel caso conosceste un date picker B4X (crossplatform) con questa possibilità, mentre tenterò di modificare i sorgenti di Erel (ma adesso devo sospendere).
 

LucaMs

Expert
Licensed User
Longtime User
Ci sarebbe la B4XDialog + B4XDateTemplate, senza eventi che mi servirebbero.
(Appena, appena fuori tema 😁)

♪♫ E lasciami sfogaaare ♫♪ (parafrasando Pappalardo)

Passare al B4XDateTemplate date "valide", ovvero da evidenziare e uniche selezionabili (ad esempio date per le quali in un DB ci siano valori associati, come appuntamenti).

Non pochissima "fatica", non ancora testato per bene.

java_yZskO0ars8.gif
 

Lucas Siqueira

Active Member
Licensed User
Longtime User
ok, l'approccio è buono, ma in ogni caso (ad ogni select nella sub CLV1_ReachEnd) la CustomListView si "riempe" di altri 50 record;
forse sarebbe il caso di pulire la CLV e gestire anche una sorta di "CLV1_ReachFirst" che non esiste;

Per poter gestire CLV1_ReachEnd, con l'esclusione degli item dalla propria CLV, è necessario memorizzare l'ultimo offset caricato in una variabile, per poter caricare i record nel modo migliore ed evitare che l'item venga duplicato.

Il lato positivo di caricare i record ogni 50 è perché l'utente spesso non scorre e si guadagna in termini di prestazioni.

Se desideri utilizzare VisibleRangeChanged come indicato dal nostro amico lucaMs, lascerò un esempio aggiornato.

le differenze tra ReachEnd e VisibleRangeChanged(FirstIndex As Int, LastIndex As Int) sono:

ReachEnd: viene eseguito ogni volta che raggiunge la fine della lista, poi inseriamo il codice per cercare nuovi record, i record esistevano solo all'interno del clv dopo essere stati caricati.

VisibleRangeChanged: viene eseguito ogni volta che si scorre la lista verso l'alto o verso il basso, è necessario caricare prima tutti i record nel clv, ma con i pannelli vuoti per non perdere prestazioni (in caso di molti record), e caricare solo il pannello con i dati all'interno del clv quando viene visualizzato l'elemento clv.

Ho aggiornato il progetto con le correzioni in compensazione per evitare caricamenti duplicati, un controllo per non caricare due volte durante il caricamento una volta.

Ora il progetto ha nuove pagine, dove scegli gli esempi CLV ReachEnd e CLV 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 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)
  
    #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


#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
 

Attachments

  • exemploLista50RegistrosETodosVisiveisECombinado.zip
    126.9 KB · Views: 3
Last edited:

Xfood

Expert
Licensed User
Grazie @Lucas Siqueira
La mia necessità era di poter utilizzare le query a 50 item per volta , quindi pageCLVReachEnd va benissimo,

In effetti un utente @giannimaione ha sollevato un dubbio, se chi utilizza l'app si mette a scorrere e quindi popola la clv con 2000/3000 record, comunque poi diventa lenta, cosi l'idea era di utilizzare enttambi le tecnologie, ma forse non si può fare....???
 

LucaMs

Expert
Licensed User
Longtime User
In effetti un utente @giannimaione ha sollevato un dubbio, se chi utilizza l'app si mette a scorrere e quindi popola la clv con 2000/3000 record, comunque poi diventa lenta, cosi l'idea era di utilizzare enttambi le tecnologie, ma forse non si può fare....???
Lo scopo del "lazy loading" (vedi esempio di Erel) è riutilizzare le view che compongono gli item.
Così, ad esempio, avrai 50/100 elementi fisici in tutto, anche con 10.000 record (ovviamente dovrai caricare i record usando OFFSET e LIMIT).
 

Lucas Siqueira

Active Member
Licensed User
Longtime User
Provo ad unire le due tecniche,
Sarebbe fantastico se funziona, sarebbe una super clv



Ho aggiunto una nuova pagina nell'esempio che combina 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: 3
Top