Как я могу реализовать механизм рециркуляции представлений для PagerAdapter?
У меня есть адаптер пейджера, который предполагает раздувание сложного представления, представляющего календарь.
Для каждого года календаря требуется ~ 350 мс.
Чтобы повысить производительность, я хотел бы реализовать тот же механизм, который существует в адаптере массива ListView
для видов обработки (convertView
параметр getView()
).
Вот мой текущий getView()
из адаптера.
@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;
}
Любые указатели или направление для реализации такого поведения?
В частности, как я могу узнать в адаптере, что один из DateTileGridViews
будет прокручиваться на экране, чтобы я мог сохранить его в памяти, чтобы повторно использовать его в следующий раз.
Ответы
Ответ 1
Итак, я понял это.
- перезаписать
destroyItem(ViewGroup container, int position, Object view)
ans сохранить кешированный вид
- создайте отдельный метод, чтобы увидеть, есть ли возможность использовать переработанное представление или вы должны раздувать новый.
- не забудьте удалить переработанный вид из кеша, как только он был использован, чтобы не иметь тот же вид, что и тот же вид на пейджер.
вот код. Я использовал стек представления для кэширования всех удаленных просмотров из моего пейджера
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());
}
не забудьте создать экземпляр стека в конструкторе адаптера.
Ответ 2
Я сделал это так: сначала создайте абстрактный класс 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;
}
теперь наследуется от SoftCache, поэтому мы можем реализовать метод 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);
}
}
то в вашем конструкторе создайте экземпляр этого типа, если вы хотите, чтобы он был для класса View (например, он может работать для любого типа класса):
SoftCache<View> myViewCache = new ViewCache(View.class);
Теперь в destroyItem сохраните представление в кеше:
@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
}
теперь метод instantiateItem использовать его просто так:
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
View MyPageView=myViewCache.get();
}
update: если вы хотите использовать кеш для разных макетов или не хотите его расширять, я придумал решение, в котором вы можете использовать один и тот же кеш для нескольких макетов, где вы могли бы получить макет, который вы ввели, используя идентификатор макетов
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
Я решил это, определив a RecycleCache
, как этот
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;
}
}
Ознакомьтесь с моим примером здесь RecyclerPagerAdapter