How can I implement a view recycling mechanism for a PagerAdapter?

I have a pager adapter that involves inflating a complex representation representing a calendar.

Each calendar year requires ~ 350 ms.

To improve performance, I would like to implement the same mechanism that exists in the ListView array adapter for processing types ( convertView getView() parameter).

Here is my current getView() from the 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; } 

Any pointers or direction for implementing this behavior? In particular, how can I find out in the adapter that one of the DateTileGridViews will scroll on the screen so that I can save it in memory so that I can reuse it next time.

+27
android android-viewpager android-pageradapter convertview
Sep 26 '13 at 5:35 on
source share
3 answers

So, I figured it out.

  • overwrite destroyItem(ViewGroup container, int position, Object view) ans save cached view
  • create a separate method to see if it is possible to use a redesigned view or if you need to inflate a new one.
  • Remember to remove the redesigned view from the cache as soon as it has been used, so as not to have the same view as the same view of the pager.

here is the code. I used the view stack to cache all deleted views from my pager

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

Remember to instantiate the stack in the adapter constructor.

+30
Sep 26 '13 at 6:16
source share

I did it like this: first create an abstract softCache class:

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

now inherits from SoftCache, so we can implement the runWhenCacheEmpty method:

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

then in your constructor create an instance of this type if you want it to be for the View class (for example, it can work for any type of class):

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

Now in destroyItem save the view in cache:

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

now the instantiateItem method to use it just like this:

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

update: if you want to use the cache for different layouts or don't want to expand it, I came up with a solution in which you can use the same cache for several layouts, where you could get the layout that you entered using the layout 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); } } } 
+3
Aug 6 '14 at 19:59
source share

I solved this by defining a RecycleCache , like this

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

Check out my example here RecyclerPagerAdapter

+2
Nov 14 '15 at 2:19
source share



All Articles