638 messaggi dal 08 aprile 2009
Anche se su questo forum si parla poco di questa struttura provo a porvi una problematica che più che altro è logica.
Xamarin.Form è una struttura su Visual Studio che permette di sviluppare in nativo app per Android, iOS e UWP.
Nello sviluppo della mia app ho una lista di prodotti che vengono presentati all'interno di una listview. Per ogni prodotto devo visualizzare e recuperare delle informazioni che è complicato recuperare in una unica query per cui l'approccio è quello di fare una query totale per recuperare tutti i prodotti e poi per ogni prodotti recuperare, tramite molteplici query, tutte le informazioni restanti compresa anche l'immagine.
Premetto che questa app l'avevo sviluppata prima per Android sempre utilizzando Xamarin ma specifica solo per questa piattaforma.
L'approccio era quello di recuperare le informazioni aggiuntive solo per le righe che vengono visualizzate in quel momento e solo quando lo stato dello scroll è fermo. Questo vuol dire che se l'utente è in scroll il recupero di queste informazioni non avviene.

Essendo un approccio funzionante correttamente (l'app è installata da 5 anni con 2000 installazioni) ho provato a risviluppare lo stesso approccio anche sulla nuova app.
Il problema sta nel fatto che nella listview di Xamarin.Forms non ho lo stato della scroll e quindi non posso sapere se l'utente ha lo scroll attivo o fermo.
Ho però l'evento scroll change.
Quindi la mia intenzione era quella di inventarmi un modo per sapere quando lo scroll si ferma e potendo recuperare gli item che al momento sono visualizzati recuperare le informazioni di dettaglio.

C'è qualche buona anima che vuole aiutarmi?
Ditemi le informazioni che possono servirvi per cominciare sulla logica....
112 messaggi dal 22 gennaio 2017
Contributi
Ciao,
Ho fatto qualcosina su Forms ma non ho mai avuto l'esigenza dell'infinite scroll.
Hai provato a dare una occhiata qui:
http://www.codenutz.com/lac09-xamarin-forms-infinite-scrolling-listview/
638 messaggi dal 08 aprile 2009
Lo scroll incrementale e quindi il caricamento delle righe incrementali non c'è nessun problema.
L'ho implementato e funziona correttamente.

Il problema è leggere questi dati aggiuntivi solo quando lo scroll si ferma e delle sole righe che visualizzo.

Al momento nell'evento scroll change ho approcciato questa tecnica anche se non è sicura al 100%:

Prendo l'indice dell'ultima riga che sto visualizzando al momento.
Attendo mezzo secondo
Se passato mezzo secondo l'indice dell'ultima riga che sto attualmente visualizzando è uguale all'indice dell'ultima riga che mi sono salvata inizialmente vuol dire che lo scroll si è fermato e e quindi procedo al caricamento dei dati aggiuntivi (tramite query molteplici sul db) delle sole righe che sto attualmente visualizzando.
10.265 messaggi dal 09 febbraio 2002
Contributi
Ciao Laura, la soluzione che stai usando ora non è male... del resto hai dovuto risolvere il problema con quello che avevi a disposizione.
In alternativa, sei disposta a scriverti dei renderer per ogni piattaforma pur di guadagnare altri informazioni sullo scroll?

... ma prima pensa a se/come un renderer migliorerebbe l'esperienza dell'utente rispetto a quanto hai già fatto. Potresti aver raggiunto già un risultato accettabile. Per migliorarlo ulteriormente potresti invece concentrarti su altri aspetti, tipo:
  • Essere in grado di cancellare le richieste ancora in corso quando l'utente riprende lo scroll;
  • Imparare dall'utente qual è il tempo di attesa migliore. Infatti, se hai un utente che tipicamente attende 550ms prima di riprendere lo scroll, allora l'attesa di mezzo secondo potrebbe essere controproducente;
  • Magari dai un feedback visivo all'utente quando inizi il caricamento dei dati (uno spinner), così che lo induca ad attendere il caricamento dei dati.


ciao,
Moreno

Enjoy learning and just keep making
638 messaggi dal 08 aprile 2009
Ciao Moreno,
effettivamento già sto utilizzando dei componenti esterni di Syncfusion in quanto Xamarin.Forms di per se non da molte possibilità di disegno per applicazioni un pò più complesse.
Riscrivere ogni volta il rendering di ogni oggetto non è complicato ma costoso nella produzione e quindi abbiamo optato per questa libreria (fornisce componenti anche per applicazioni windows e asp).

La mia esperienza nello sviluppo di app è consolidata in Android e non avrei problemi ad implementare lo stato dello scroll in questa piattaforma. Ma poi dovrei scriverla anche in UWP e iOS.

Mi interessano molto i punti che mi hai elencato e soprattutto per il primo e quindi cancellare le richieste che ho fatto partire quando lo scroll riprende.
Con i Task però ho delle lacune...potresti aiutarmi?
Al momento ti elenco un pò di codice.
Nell'ento scroll change se presumo che lo scroll è fermo faccio partire un loop sulle righe che sto visualizzando al momento e per ognuna faccio partire un Task per la lettura:

 var rows = scrollRows.GetVisibleLines();
                try
                {
                    for (int i = 0; i < rows.Count; i++)
                    {
                        var row = rows[i];
                        ARTICOLO_BIND_LISTE bind = ViewModel.MagartItems[row.LineIndex];
                        if (bind.SET_ALL_DATO == false)
                        {
                            System.Diagnostics.Debug.WriteLine("APPERING SCROLL " + bind.IndexDisplay + "->" + bind.Descrizione);
                            ViewModel.LoadItemComplete(row.LineIndex);
                        }
                    }
                }
                catch { }


Task che richiamo per l'aggiornamento del dato bindato:
        public async Task LoadItemComplete(int index)
        {
            try
            {
                if (MagartItems[index].SET_ALL_DATO == false)
                {
                    int indice_update = index;
                    MagartItems[indice_update].SET_ALL_DATO = true;
                    MagartItems[indice_update].magart = await manager_articoli_helper.GetArticoloListaAllDatoAsync(MagartItems[indice_update].magart, true);
                    MagartItems[indice_update].UpdateBind();
                }

            }
            catch (Exception ex)
            {
                MagartItems[index].SET_ALL_DATO = false;


            }


        }


Task di lettura:
 public async Task<MAGART> GetArticoloListaAllDatoAsync(MAGART articolo, bool get_prezzo_sconto = true)
            {
                return await Task.Run(() => GetArticoloListaAllDato(articolo, get_prezzo_sconto));
            }


GetArticoloListaAllDato è un metodo che restituisce il tracciato facendo delle letture su DB.

Ineffetti, testando la procedura, se mi fermo con lo scroll e partono le letture e subito dopo riprendo lo scroll mentre le letture (ad esempio di 20 righe) sono ancora in piedi lo scroll inizialmente non risulta fluido.
10.265 messaggi dal 09 febbraio 2002
Contributi
Ciao Laura,
per supportare la cancellazione di operazioni di lunga durata è utile usare l'oggetto CancellationTokenSource che vedremo adesso.


Nell'evento scroll change se presumo che lo scroll è fermo faccio partire un loop sulle righe che sto visualizzando al momento e per ognuna faccio partire un Task per la lettura:

Ok, potresti mettere il loop in un proprio metodo asincrono che richiami dal gestore dell'evento ScrollChanged. Puoi anche non usare l'await in quell'invocazione, tanto non ci interessa attenderne il completamento. Ci interessa invece cancellare l'eventuale esecuzione precedente e questo lo fai per mezzo di un CancellationTokenSource.
Ad ogni iterazione del loop, verifica se è stata richiesta o no la cancellazione. Se è stata richiesta, esci dal loop ed eventualmente interrompi l'esecuzione dell'operazione corrente.

Per interrompere l'esecuzione dell'operazione corrente, è necessario però che tu stia usando un'operazione asincrona che supporti il CancellationTokenSource. Per esempio, se accedi al db Sqlite usando il pacchetto Microsoft.Data.Sqlite, hai il metodo ExecuteReaderAsync che lo supporta. Eccolo nel sorgente:
https://github.com/aspnet/Microsoft.Data.Sqlite/blob/dev/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs#L422


GetArticoloListaAllDato

Questa dovrebbe essere asincrona ed accettare il CancellationToken, altrimenti non riuscirai a cancellarla. Se non riesci a renderla asincrona, pazienza. Vuol dire che l'interruzione avverrà appena questa si è completata.

Per farti capire meglio, ti condivido un'app dimostrativa a questo indirizzo:
https://1drv.ms/u/s!Ao4rhSdtDw8rgelKn_1ukS-t2uAenA

Io non ho i controlli di SyncFusion quindi ho dovuto usare una normale ListView. Guarda in particolar modo la view MainPage.xaml (e il suo codefile) dove ho messo la ListView e la gestione dei suoi eventi ItemAppearing e ItemDisappearing. Ho dovuto usare questi al posto di ScrollChanged perché nella ListView di Xamarin.Forms non c'è. Comunque è un approccio equivalente al tuo: ho anch'io una lista ma la tengo aggiornata ogni volta che un elemento appare o scompare.

La logica "interessante" è nella classe MainPageViewModel dove trovi un timer che invoca il metodo LoadAdditionalInfo se sono trascorsi più di 500 millisecondi dall'ultimo movimento.

Questa funzione (LoadAdditionalInfo, in cui si trova il loop) passa in rassegna la lista degli oggetti visibili e, per ciascuno, carica i dati aggiuntivi. Il caricamento viene subito interrotto se l'utente ricomincia a scrollare, quindi lo scroll risulterà immediatamente fluido.

ciao,
Moreno

Enjoy learning and just keep making
638 messaggi dal 08 aprile 2009
Magnifico!
Funziona perfettamente.
Non iesco a lanciare il comando sql in async perchè utilizzo la libreria di SQLite-net che è quella consigliata.

Ho scritto anche al supporto e, almeno da quello che dicono, implementeranno lo stato dello scroll nella prossima release.

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.