Autore Topic: [ViewPager (con TabHost)] Fragment GetView viene chiamato più volte  (Letto 1127 volte)

Offline Link_88

  • Nuovo arrivato
  • *
  • Post: 10
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Note II
  • Sistema operativo:
    Windows 7
Ho cercato per ora dei post interessanti per il mio specifico problema senza successo.

Per questo tipo di problema, tutti i post parlano di settare il layout della ListView a "Fill_Parent".
Nel mio codice io non uso ListView esplicite.

Di seguito il codice:

TabsAdapter:

Codice (Java): [Seleziona]
    public class TabsAdapter extends FragmentPagerAdapter implements
                TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener, IconPagerAdapter {
    .
    .
         // Questo GetItem viene chiamato il numero corretto di volte
         @Override
        public Fragment getItem(int position) {
            TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext, info.clss.getName(),
                info.args);
        }
    .
    .

ArrayAdapter:

Codice (Java): [Seleziona]
    //Questo GetView viene chiamato innumerevoli volte di più del dovuto
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = null;
        if (convertView == null) {
                LayoutInflater inflator = context.getLayoutInflater();
                view = inflator.inflate(
                                R.layout.list_activity_segnalazioni_archivio_riga, null);
                final ViewHolder viewHolder = new ViewHolder();
                viewHolder.codiceEsterno = (TextView) view
                                .findViewById(R.id.list_activity_segnalazione_archivio_riga_textview_codice_esterno);
                viewHolder.data = (TextView) view
                                .findViewById(R.id.list_activity_segnalazione_archivio_riga_textview_data);
                viewHolder.posizione = (TextView) view
                                .findViewById(R.id.list_activity_segnalazione_archivio_riga_textview_posizione);
                viewHolder.codiceEsterno.setFocusable(false);
                viewHolder.codiceEsterno.setFocusableInTouchMode(false);
                viewHolder.data.setFocusable(false);
                viewHolder.data.setFocusableInTouchMode(false);
                viewHolder.posizione.setFocusable(false);
                viewHolder.posizione.setFocusableInTouchMode(false);
   
   
                viewHolder.removeButton = (ImageButton) view.findViewById(R.id.list_activity_segnalazione_archivio_riga_imagebutton_remove);
                viewHolder.removeButton.setFocusable(false);
                viewHolder.removeButton.setFocusableInTouchMode(false);
                viewHolder.previewImage = (ImageView) view
                                .findViewById(R.id.row_image);
   
                .
                .
                .
                view.setTag(viewHolder);
        } else {
                view = convertView;
        }
   
        ViewHolder holder = (ViewHolder) view.getTag();
        try {
                holder.codiceEsterno.setText(segnalazioni.get(position).getCodiceEsterno());
                holder.data.setText(DateUtils.formatToHumanDate(segnalazioni.get(
                                position).getDataInizio()));
   
                String address = segnalazioni.get(position).getToponimo();
                if (segnalazioni.get(position).getCivico() != null
                                && !segnalazioni.get(position).getCivico().equals("")) {
                        address = address.concat(", ").concat(
                                        segnalazioni.get(position).getCivico().toString()
                                        .toUpperCase(Locale.ITALY));
                }
                holder.posizione.setText(address);
   
                Map<String, Object> sqlParams = new HashMap<String, Object>();
                sqlParams.put("identita", segnalazioni.get(position).getIdStorico());
               
                // This retrieve a base64 image from the sqlite.
                // Getting called more times that needed gives me an incredible cpu overhead
                **String thumb64 = SegnalamiApplication.abatis.executeForString("getAllFileDatiByIdEntitaForThumb", sqlParams);**
   
                        if(thumb64 != null)
                        {
                                holder.previewImage.setImageBitmap(BitmapUtils.base64ToBitmap(thumb64));
                        }
   
                } catch (NullPointerException e) {
                        e.printStackTrace();
                }
                return view;
        }
 

xml layout (la differenza sostanziale rispetto a tutti gli altri post di "GetView being called multiple times"):

 
Codice (XML): [Seleziona]
   <TabHost
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@android:id/tabhost"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent">
   
        <LinearLayout
           android:orientation="vertical"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent">
   
            <TabWidget
               android:id="@android:id/tabs"
               android:orientation="horizontal"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content" />
   
            <FrameLayout
               android:id="@android:id/tabcontent"
               android:layout_width="0dp"
               android:layout_height="0dp" />
   
            <android.support.v4.view.ViewPager
               android:id="@+id/archivio_pager"
               android:layout_width="fill_parent"
               android:layout_height="fill_parent" />
   
        </LinearLayout>
    </TabHost>

Spero che riusciremo a sistemare questa anomalia, è molto importante per l'app che sto sviluppando

L.F.
« Ultima modifica: 16 Ottobre 2013, 14:43:30 CEST da Link_88 »

Offline matttt

Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #1 il: 16 Ottobre 2013, 18:45:36 CEST »
0
getView() richiamato più volte è normale, il sistema operativo chiede ad ogni view di ridisegnarsi in funzione di vari eventi diversi; per questo conviene cercare di realizzare funzioni getView() il più snelle possibile.
Ho guardato velocemente il tuo codice e vedo che fai diverse cose nel getView... scarichi immagini da remoto?
Ti conviene avere pronte a priori le risorse che userai oppure valutare sistemi diversi... una cache di immagini ad esempio, ma non so se è fattibile nel tuo caso.
Le mie apps su Google Play Store:

Offline Link_88

  • Nuovo arrivato
  • *
  • Post: 10
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Note II
  • Sistema operativo:
    Windows 7
Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #2 il: 18 Ottobre 2013, 09:32:11 CEST »
0
Le immagini sono salvate nel db sqlite in base64.

Faccio una select con Abatis e carico l'immagine dell'imageview, essendo il getview chiamato più volte mi fa più volte la select, con conseguente rallentamento.

Le immagini le devo storicizzare per forza nel sqlite, perché ho necessità di serializzarle successivamente in un maniera specifica.

Il codice da me postato è una list view con un'image view di preview e altri dati testuali. Hai qualche idea su come posso ottimizzare questa operazione ?

Offline GabMarioPower

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 606
  • Respect: +152
    • Github
    • Google+
    • gabrielemariotti
    • GabMarioPower
    • Mostra profilo
  • Play Store ID:
    GAB+MARIO+DEV
  • Sistema operativo:
    Ubuntu 14.04 , Win 10
Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #3 il: 18 Ottobre 2013, 09:49:21 CEST »
0
Le immagini sono salvate nel db sqlite in base64.

Faccio una select con Abatis e carico l'immagine dell'imageview, essendo il getview chiamato più volte mi fa più volte la select, con conseguente rallentamento.

Le immagini le devo storicizzare per forza nel sqlite, perché ho necessità di serializzarle successivamente in un maniera specifica.

Il codice da me postato è una list view con un'image view di preview e altri dati testuali. Hai qualche idea su come posso ottimizzare questa operazione ?

Il metodo getView dell'arrayAdapter viene chiamato per ogni riga che viene visualizzato dalla tua lista.
Per migliorare le prestazioni del tuo caso dovresti usare un async (con tutte le accortezze del caso) per eseguire la query e la successiva visualizzazione dell'immagine
Nella listView cosa hai messo come layout_height?


Offline Link_88

  • Nuovo arrivato
  • *
  • Post: 10
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Note II
  • Sistema operativo:
    Windows 7
Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #4 il: 18 Ottobre 2013, 12:53:01 CEST »
0
Come scritto precedentemente non ho esplicitamente dichiarato la listview. Il codice è quello che vedi nell'xml...

Il problema è che GetView viene chiamato più volte per ogni riga visualizzata..

Offline GabMarioPower

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 606
  • Respect: +152
    • Github
    • Google+
    • gabrielemariotti
    • GabMarioPower
    • Mostra profilo
  • Play Store ID:
    GAB+MARIO+DEV
  • Sistema operativo:
    Ubuntu 14.04 , Win 10
Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #5 il: 18 Ottobre 2013, 15:41:55 CEST »
0
ah scusa
Posta l'Activity dove usi l'arrayAdapter, cosi non riesco a capire.

Offline matttt

Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #6 il: 18 Ottobre 2013, 18:35:48 CEST »
0
Ottimizzare il tutto... butto li un'idea anche se non so quanto possa migliorare la tua situazione e sicuramente non è semplice... potresti provare a creare uno SparseArray di Drawable (usando come ID dello Sparse l'ID della riga dalla tabella del DB), in getView controlli se esiste un Drawable corrispondente a quella riga nello Sparse, se c'è ritorni quello altrimenti te lo carichi dal DB, facendo la decodifica base 64, etc. Una specie di cache insomma...

Solo che se hai molte righe sarebbe il caso anche di eliminare vecchi Drawable non più visibili per evitare di sovraccaricare la memoria e qui la situazione si complica. Se vuoi realizzare anche questo potresti usare invece una coda First In Last Out (quindi una Queue in questo caso) di oggetti col Drawable + l'ID della riga...

Inoltre... le immagini sono molto grandi? Questo potrebbe rallentare ulteriormente il tutto, potresti provare a generare tu delle preview... e anche questa non è una strada semplice se hai tante immagini...

Le immagini sono salvate nel db sqlite in base64.
Faccio una select con Abatis e carico l'immagine dell'imageview, essendo il getview chiamato più volte mi fa più volte la select, con conseguente rallentamento.
Le immagini le devo storicizzare per forza nel sqlite, perché ho necessità di serializzarle successivamente in un maniera specifica.
Il codice da me postato è una list view con un'image view di preview e altri dati testuali. Hai qualche idea su come posso ottimizzare questa operazione ?
Le mie apps su Google Play Store:

Offline Link_88

  • Nuovo arrivato
  • *
  • Post: 10
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy Note II
  • Sistema operativo:
    Windows 7
Re:[ViewPager (con TabHost)] Fragment GetView viene chiamato più volte
« Risposta #7 il: 24 Ottobre 2013, 10:04:36 CEST »
0
Ottimizzare il tutto... butto li un'idea anche se non so quanto possa migliorare la tua situazione e sicuramente non è semplice... potresti provare a creare uno SparseArray di Drawable (usando come ID dello Sparse l'ID della riga dalla tabella del DB), in getView controlli se esiste un Drawable corrispondente a quella riga nello Sparse, se c'è ritorni quello altrimenti te lo carichi dal DB, facendo la decodifica base 64, etc. Una specie di cache insomma...

Solo che se hai molte righe sarebbe il caso anche di eliminare vecchi Drawable non più visibili per evitare di sovraccaricare la memoria e qui la situazione si complica. Se vuoi realizzare anche questo potresti usare invece una coda First In Last Out (quindi una Queue in questo caso) di oggetti col Drawable + l'ID della riga...

Inoltre... le immagini sono molto grandi? Questo potrebbe rallentare ulteriormente il tutto, potresti provare a generare tu delle preview... e anche questa non è una strada semplice se hai tante immagini...

Scusami, se ho capito male ti prego di correggermi... Questa pseudo cache serve per agevolare il caricamento delle immagini dalla seconda volta in poi che vengono usate, in quanto la prima volta che le carico, oltre a visualizzarle nelle imageview carico i draw in "cache". In questo modo la prossima volta in cui ho bisogno delle foto le prendo direttamente da lì...

Il mio problema è a monte del primo caricamento, purtroppo...

ah scusa
Posta l'Activity dove usi l'arrayAdapter, cosi non riesco a capire.

Scusami tanto per il ritardo, sono stato impegnato con altri sviluppi...

Eccoti l'onCreate dell'activity, dove lancio l'array adapter nell'head post!

Codice (Java): [Seleziona]
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.archivio_pager);
                getSupportActionBar().setTitle(Reso.getString(this, R.string.btn_segnalami_archivio));
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);

                mActivity = this;
               
                mTabHost = (TabHost) findViewById(android.R.id.tabhost);
                mTabHost.setup();
                mViewPager = (SwipeableViewPager) findViewById(R.id.archivio_pager);
                mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);

                mViewPager.setSwipeable(false);
                mViewPager.setOnPageChangeListener(onPageChangeListener);
               
                //Archive tabs generation
                mTabsAdapter.addTab(
                                mTabHost.newTabSpec(TAB_DA_INVIARE_TAG).setIndicator("Nuove segnalazioni"),
                                ArchivioActivity.LoaderListFragment.class, null);

                mTabsAdapter.addTab(
                                mTabHost.newTabSpec(TAB_DA_VERIFICARE_TAG).setIndicator("Da verificare"),
                                ArchivioActivity.LoaderListToVerifyFragment.class, null);

                if (savedInstanceState != null) {
                        mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
                }

                user = UserInfo.getInstance().getUser();

                syncUploadService = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
                                .getString("segnalamiService",
                                                getApplicationContext().getString(R.string.config_segnalazioni_service));

                syncDownloadService = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
                                .getString("downloadService",
                                                getApplicationContext().getString(R.string.config_sync_service));

                tenantCode = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
                                .getString("tenantCode",
                                                getApplicationContext().getString(R.string.tenant_code));
        }

Ho pensato che magari il problema si potesse spostare alla fase di decodifica stringa base64 a imageview. Magari si può ottimizzare lì...

L'ideale (ne sarei molto felice) sarebbe far chiamare GetView una sola volta per record e allo stesso tempo ottimizzare il metodo di decodifica che uso!

Le operazione che faccio sono:
base64 -> byte array -> Bitmap

Vi posto il metodo di decodifica:

Metodo che prende in input la stringa in base64 presente sul sqlite
Codice (Java): [Seleziona]
public static Bitmap base64ToBitmap(String base64)
        {
                try {
                        byte[] imageAsBytes = Base64.decode(base64);
                        return BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length);
                } catch (Exception e) {
                        return null;
                }
        }

La classe Base64 è una classe molto interessante che ho trovato e con piacere la condivido con voi.

Grazier per l'aiuto