29

Mam adapter pager, który ma nadmuchać złożony widok reprezentujący kalendarz.Jak zaimplementować mechanizm przeglądania widoku dla PagerAdapter?

Potrwa około 350 ms, aby nadmuchać każdy rok kalendarza.

Aby zwiększyć wydajność chciałbym wdrożyć ten sam mechanizm, który istnieje w adapterze ListView tablicy poglądów recyklingu (convertView parametru w getView()).

Oto mój aktualny getView() z adaptera.

@Override 
protected View getView(VerticalViewPager pager, final DateTileGrid currentDataItem, int position) 
{ 
    mInflater = LayoutInflater.from(pager.getContext()); 

     // This is were i would like to understand weather is should use a recycled view or create a new one. 
    View datesGridView = mInflater.inflate(R.layout.fragment_dates_grid_page, pager, false); 


    DateTileGridView datesGrid = (DateTileGridView) datesGridView.findViewById(R.id.datesGridMainGrid); 
    TextView yearTitle = (TextView) datesGridView.findViewById(R.id.datesGridYearTextView); 
    yearTitle.setText(currentDataItem.getCurrentYear() + ""); 
    DateTileView[] tiles = datesGrid.getTiles(); 

    for (int i = 0; i < 12; i++) 
    { 
     String pictureCount = currentDataItem.getTile(i).getPictureCount().toString(); 
     tiles[i].setCenterLabel(pictureCount); 
     final int finalI = i; 
     tiles[i].setOnCheckedChangeListener(new DateTileView.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(DateTileView tileChecked, boolean isChecked) 
      { 
       DateTile tile = currentDataItem.getTile(finalI); 
       tile.isSelected(isChecked); 
      } 
     }); 
    } 

    return datesGridView; 
} 

Jakieś wskazówki czy kierunek wdrożenia takiego zachowania? W szczególności, jak mogę dowiedzieć się w adapterze, że przesuwa się jeden z DateTileGridViews ekranu, aby zapisać go w pamięci, aby ponownie użyć go następnym razem.

Odpowiedz

31

Więc wymyśliłem to.

  1. nadpisywania destroyItem(ViewGroup container, int position, Object view) ans zaoszczędzić buforowane Zobacz
  2. utworzyć oddzielną metodę aby sprawdzić, czy istnieje jakakolwiek szansa, aby użyć widoku lub recyklingu należy napompować nową.
  3. pamiętaj o usunięciu odzyskanego widoku z pamięci podręcznej po jego użyciu, aby uniknąć widoku z tej samej strony do pagera.

oto kod .. Kiedyś stosie celu buforować wszystkie usunięte widoki z mojego pagera

private View inflateOrRecycleView(Context context) 
{ 

    View viewToReturn; 
    mInflater = LayoutInflater.from(context); 
    if (mRecycledViewsList.isEmpty()) 
    { 
     viewToReturn = mInflater.inflate(R.layout.fragment_dates_grid_page, null, false); 
    } 
    else 
    { 
     viewToReturn = mRecycledViewsList.pop(); 
     Log.i(TAG,"Restored recycled view from cache "+ viewToReturn.hashCode()); 
    } 


    return viewToReturn; 
} 

@Override 
public void destroyItem(ViewGroup container, int position, Object view) 
{ 
    VerticalViewPager pager = (VerticalViewPager) container; 
    View recycledView = (View) view; 
    pager.removeView(recycledView); 
    mRecycledViewsList.push(recycledView); 
    Log.i(TAG,"Stored view in cache "+ recycledView.hashCode()); 
} 

Nie zapomnij instancję stos w konstruktora adaptera.

+0

Dziękuję bardzo! – penguin86

3

Zrobiłem to tak ..najpierw utworzyć klasę abstrakcyjną softCache:

public abstract class SoftCache<T> { 
    private Stack<Reference<T>> mRecyclingStack; 
    final Class<T> classType; 

    public SoftCache(Class<T> typeParameterClass) { 
     this.classType = typeParameterClass; 
     mRecyclingStack = new Stack<Reference<T>>(); 
    } 

    /* implement this to create new object of type T if cache is empty */ 
    public abstract T runWhenCacheEmpty(); 

    /* 
    * retrieves last item from cache or creates a new T object if cache is 
    * empty 
    */ 
    public T get() { 
     T itemCached = null; 

     if (mRecyclingStack.isEmpty()) { 
      itemCached = runWhenCacheEmpty(); 

     } else { 
      SoftReference<T> softRef = (SoftReference<T>) mRecyclingStack 
        .pop(); 

      Object obj = softRef.get(); 
      /* 
      * if referent object is empty(due to GC) then create a new 
      * object 
      */ 
      if (obj == null) { 
       itemCached = runWhenCacheEmpty(); 

      } 
      /* 
      * otherwise restore from cache by casting the referent as the 
      * class Type that was passed to constructor 
      */ 
      else { 
       itemCached = (classType.cast(softRef.get())); 
      } 
     } 
     return itemCached; 
    } 

teraz dziedziczą SoftCache więc możemy wdrożyć metodę runWhenCacheEmpty:

public class ViewCache extends SoftCache<View>{ 

      public ViewCache(Class<View> typeParameterClass) { 
       super(typeParameterClass); 
      } 

      @Override 
      public View runWhenCacheEmpty() { 
       return mFragment.getActivity().getLayoutInflater() 
         .inflate(R.layout.mypagelayout, null); 
      } 
     } 

następnie w konstruktorze oznacz ją tak, jeśli ma to być dla widoku klasie na przykład (ale może pracować dla każdego typu klasy):

SoftCache<View> myViewCache = new ViewCache(View.class); 

teraz w destroyItem zapisać widok do pamięci podręcznej:

@Override 
    public void destroyItem(final ViewGroup container, final int position, final Object object) { 
     final View v = (View) object; 

     if(v.getId() == R.id.mypagelayout) 
      myViewCache.put(v); //this saves it 


    } 

teraz metoda instantiateItem niego skorzystać po prostu tak:

@Override 
    public Object instantiateItem(final ViewGroup container, final int position) { 
     View MyPageView=myViewCache.get(); 

} 

aktualizacja: jeśli chcesz korzystać z pamięci podręcznej dla różnych układów lub nie chcesz go przedłużyć wymyśliłem rozwiązanie, w którym można użyć tego samego pamięci podręcznej dla wielu układów, gdzie byś pobrać układ można umieścić w użyciu układów ID:

public class SoftViewCache { 

    private HashMap<Integer,ArrayList<SoftReference<View>>> multiMap; 

    public SoftViewCache() { 
     multiMap= new HashMap<Integer, ArrayList<SoftReference<View>>>();   
    } 

    /* 
    * retrieves cached item or return null if cache is 
    * empty 
    */ 
    public View get(int id) { 
     View itemCached = null; 
     if (!multiMap.containsKey(id)) { 
      return null; 
     } 
     else { 
      /*get the referent object and check if its already been GC if not we re-use*/ 
      SoftReference<View> softRef =multiMap.get(id).get(0); 
      Object obj = softRef.get(); 
      /* 
      * if referent object is empty(due to GC) then caller must create a new 
      * object 
      */ 
      if (null == obj) { 
       return null; 
      } 
      /* 
      * otherwise restore from cache 
      */ 
      else { 
       itemCached = (softRef.get()); 
      } 
     } 
     return itemCached; 
    } 

    /* saves a view object to the cache by reference, we use a multiMap to allow 
    * duplicate IDs*/ 
    public void put(View item) { 
     SoftReference<View> ref = new SoftReference<View>(item); 
     int key = item.getId(); 
      /*check if we already have a reuseable layouts saved if so just add to the list 
      * of reusable layouts*/ 
     if (multiMap.containsKey(key)) { 
      multiMap.get(key).add(ref); 
     } else { 
      /*otherwise we have no reusable layouts lets create a list of reusable layouts 
      * and add it to the multiMap*/ 
      ArrayList<SoftReference<View>> list = new ArrayList<SoftReference<View>>(); 
      list.add(ref); 
      multiMap.put(key, list); 
     } 
    } 
} 
+0

Jedna mała zmiana w SoftViewCache.get (int id) zastąpienie SoftReference softRef = multiMap.get (id) .get (0); z SoftReference softRef = multiMap.remove (id) .get (0); usunąć z pamięci podręcznej, ponieważ nie jest już dostępna do ponownego użycia. –

2

Rozwiązałem to przez zdefiniowanie RecycleCache, jak to

protected static class RecycleCache { 

    private final RecyclerPagerAdapter mAdapter; 

    private final ViewGroup mParent; 

    private final int mViewType; 

    private List<ViewHolder> mCaches; 

    public RecycleCache(RecyclerPagerAdapter adapter, ViewGroup parent, int viewType) { 
    mAdapter = adapter; 
    mParent = parent; 
    mViewType = viewType; 
    mCaches = new ArrayList<>(); 
    } 

    public ViewHolder getFreeViewHolder() { 
    int i = 0; 
    ViewHolder viewHolder; 
    for (int n = mCaches.size(); i < n; i++) { 
     viewHolder = mCaches.get(i); 
     if (!viewHolder.mIsAttached) { 
     return viewHolder; 
     } 
    } 
    viewHolder = mAdapter.onCreateViewHolder(mParent, mViewType); 
    mCaches.add(viewHolder); 
    return viewHolder; 
    } 
} 

Sprawdź mój przykładowy kod tutaj RecyclerPagerAdapter

+0

Doskonale, dziękuję! – georgehardcore

+0

To jest naprawdę bardzo dobra biblioteka. Ale to nie ten sam kod co tutaj. Dlaczego? –

+0

Kod został ulepszony po opublikowaniu tutaj. –