29

Ich habe einen Pager-Adapter, der eine komplexe Ansicht, die einen Kalender darstellt, aufbläht.Wie implementiere ich View-Recycling-Mechanismus für PagerAdapter?

Es dauert etwa ~ 350 ms, um jedes Jahr des Kalenders aufzublasen.

Um die Leistung zu verbessern, möchte ich den gleichen Mechanismus implementieren, der in der ListView Array-Adapter von Recycling-Ansichten (convertView Parameter in getView()) existiert.

Hier ist meine aktuelle getView() aus dem Adapter.

@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; 
} 

Irgendwelche Zeiger oder Anweisungen für die Implementierung eines solchen Verhaltens? Insbesondere, wie kann ich im Adapter wissen, dass einer der DateTileGridViews wird über den Bildschirm geklaut, so dass ich es im Speicher speichern konnte, um es beim nächsten Mal wieder zu verwenden.

Antwort

31

Also habe ich es herausgefunden.

  1. überschreiben destroyItem(ViewGroup container, int position, Object view) ans speichern Sie
  2. erstellen Sie eine separate Methode Ansicht gecached dort zu sehen, ob die Möglichkeit besteht, eine Recycling-Ansicht zu verwenden oder sollten Sie einen neuen aufzublasen.
  3. Denken Sie daran, die wiederverwendbare Ansicht aus dem Cache zu entfernen, sobald sie verwendet wurde, um zu vermeiden, dass dieselbe Ansicht die gleiche Ansicht an den Pager anfügt.

hier ist der Code .. ich einen Stapel Ansicht verwendet, um alle entfernt Ansichten von meinem Pager vergessen

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()); 
} 

nicht cachen, den Stapel in dem Adapter Konstruktor zu instanziiert.

+0

Vielen Dank! – penguin86

3

ich habe es so gemacht ..erste abstrakte softCache Klasse erstellen:

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; 
    } 

jetzt von SoftCache erben so können wir die runWhenCacheEmpty Methode implementieren:

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); 
      } 
     } 

dann in Ihrem Konstruktor es so instanziiert, wenn Sie es für eine View-Klasse sein wollen zum Beispiel (aber es kann für jede Art von Klasse arbeiten):

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

jetzt in destroyItem den Blick auf den Cache speichern:

@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 


    } 

jetzt Verfahren instantiateItem davon Gebrauch machen einfach so:

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

} 

Update: Wenn Sie den Cache für verschiedene Layouts verwenden wollen oder nicht, wie es zu verlängern i mit einer Lösung kam, wo Sie können die gleichen Cache für mehrere Layouts verwenden, in denen Sie das Layout abrufen würden Sie bei der Verwendung des Layouts ID setzen:

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

Eine kleine Änderung in SoftViewCache.get (int ID) ersetzt SoftReference softRef = multiMap.get (id) .get (0); mit SoftReference softRef = multiMap.remove (id) .get (0); aus dem Cache entfernen, da es nicht mehr für die Wiederverwendung verfügbar ist. –

2

ich dieses Problem gelöst, indem ein RecycleCache definieren, wie diese

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; 
    } 
} 

überprüfen mein Beispielcode aus hier RecyclerPagerAdapter

+0

Perfekt, danke! – georgehardcore

+0

Dies ist eigentlich eine sehr gute Bibliothek. Aber es ist nicht derselbe Code wie hier. Woher? –

+0

Der Code hatte einige Verbesserungen nach dem Posten hier. –

Verwandte Themen