viewpager - tabs android studio
¿Cómo implemento el mecanismo de reciclaje de vistas para PagerAdapter? (3)
Tengo un adaptador de buscapersonas que supone inflar una vista compleja que representa un calendario.
Toma alrededor de ~ 350 ms para inflarse cada año del calendario.
Para mejorar el rendimiento, me gustaría implementar el mismo mecanismo que existe en el adaptador de matriz ListView
de reciclar vistas (parámetro getView()
en getView()
).
Aquí está mi getView()
actual del adaptador.
@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;
}
¿Alguna sugerencia o dirección para implementar tal comportamiento? En particular, ¿cómo puedo saber en el adaptador que uno de los DateTileGridViews
está pasando de la pantalla para poder guardarlo en la memoria y reutilizarlo la próxima vez?
Así que lo he descubierto.
- sobrescribir
destroyItem(ViewGroup container, int position, Object view)
ans save you cached view - cree un método diferente para ver si hay alguna posibilidad de usar una vista reciclada o si debe inflar una nueva.
- recuerde eliminar la vista reciclada de la memoria caché una vez que se haya utilizado para evitar que la misma vista adhiera la misma vista al localizador.
aquí está el código. Usé una pila de vistas para almacenar en caché todas las vistas eliminadas de mi busca
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());
}
no te olvides de crear una instancia de la pila en el constructor del adaptador.
Lo hice así ... primero creo una clase abstracta de 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;
}
ahora heredamos de SoftCache para que podamos implementar el método 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);
}
}
luego en su constructor crea una instancia de esta manera si quiere que sea para una clase View por ejemplo (pero puede funcionar para cualquier tipo de clase):
SoftCache<View> myViewCache = new ViewCache(View.class);
ahora en destroyItem guarda la vista en el caché:
@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
}
ahora el método instantiateItem hace uso de él simplemente así:
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
View MyPageView=myViewCache.get();
}
actualización: si desea utilizar la memoria caché para diferentes diseños o no desea extenderla, se me ocurrió una solución donde puede usar la misma memoria caché para diseños múltiples donde recuperaría el diseño que puso usando la ID de diseño:
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);
}
}
}
Lo resolví definiendo un RecycleCache
, como este
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;
}
}
Mira mi código de muestra aquí RecyclerPagerAdapter