Skip to content

Instantly share code, notes, and snippets.

@saguinav
Last active August 29, 2015 14:19
Show Gist options
  • Save saguinav/de660c8654f5a361fbf2 to your computer and use it in GitHub Desktop.
Save saguinav/de660c8654f5a361fbf2 to your computer and use it in GitHub Desktop.
This class aims to provide a base implementation for PagerAdapter that includes a mechanism for recycling views.
/*
* Copyright (C) 2015 Samuel Guirado Navarro
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import com.groupon.commonlibs.R;
import java.util.ArrayList;
import java.util.List;
/**
* This class aims to provide a base implementation for
* PagerAdapter that includes a mechanism for recycling views.
*
* NOTE: This implementation uses a ViewHolder pattern
* as a key component of the recycling mechanism.
* That viewholder instance is saved in the view as a
* tag object with a unique key:
*
* view.setTag(TAG_KEY, tag)
*/
public abstract class RecyclerPagerAdapter extends PagerAdapter {
private static final int TAG_KEY = R.id.RECYCLER_PAGER_ADAPTER_TAG_KEY;
private static class InternalItem {
public Object instantiatedObject;
public int viewType;
public int position;
public float pageWidth;
}
final List<InternalItem> instantiatedItems = new ArrayList<InternalItem>();
final List<InternalItem> destroyedItems = new ArrayList<InternalItem>();
@Override
public final void startUpdate(ViewGroup container) {
clearItems();
}
@Override
public final Object instantiateItem(ViewGroup container, int position) {
final InternalItem item = new InternalItem();
item.instantiatedObject = getItem(position);
item.position = position;
item.viewType = getItemViewType(position);
item.pageWidth = getPageWidth(position);
instantiatedItems.add(item);
return item;
}
@Override
public final void destroyItem(ViewGroup container, int position, Object object) {
destroyedItems.add((InternalItem) object);
}
@Override
public final void finishUpdate(ViewGroup container) {
final SparseArrayCompat<List<View>> viewsToRecycle = removeDestroyedItemsViews(container);
// Render views and attach them to the container. Page views are reused
// whenever possible.
for (InternalItem instantiatedItem : instantiatedItems) {
List<View> views = null;
if (viewsToRecycle.size() > 0) {
views = viewsToRecycle.get(instantiatedItem.viewType);
}
final View convertView;
if (views != null && views.size() > 0) {
convertView = views.remove(0);
// Re-add existing view before rendering so that we can make change inside getView()
container.addView(convertView);
getView(instantiatedItem.position,
convertView,
container);
} else {
convertView = getView(instantiatedItem.position,
null,
container);
container.addView(convertView);
}
convertView.setTag(TAG_KEY, instantiatedItem);
}
clearItems();
}
@Override
public final boolean isViewFromObject(View view, Object object) {
return getInternalItem(view) == object;
}
@Override
public final int getItemPosition(Object object) {
final InternalItem item = (InternalItem) object;
if (item.instantiatedObject.equals(getItem(item.position)) &&
item.viewType == getItemViewType(item.position) &&
item.pageWidth == getPageWidth(item.position)) {
return item.position;
} else {
return POSITION_NONE;
}
}
/**
* Get the data item associated with the specified position in the data set.
*
* @param position
* Position of the item whose data we want within the adapter's data set.
* @return The data at the specified position.
* The object must override the {@link Object#equals(Object)} method in order to
* allow comparison between objects.
*/
public abstract Object getItem(int position);
/**
* Get a View that displays the data at the specified position in the data set.
*
* @param position
* The position of the item
* @param convertView
* The view to be reused.
* @param parent
* The parent that this view will eventually be attached to.
* @return A View corresponding to the data at the specified position.
*/
public abstract View getView(int position, View convertView, ViewGroup parent);
public int getItemViewType(int position) {
return 0;
}
/**
* Remove views backing destroyed items from the specified container
* @param container The container that contains the views
* @return The views from destroyed items that have been removed from the specified container
*/
private SparseArrayCompat<List<View>> removeDestroyedItemsViews(ViewGroup container) {
final List<View> viewsToRemove = new ArrayList<View>();
// The key of this SparseArray will be the ViewType
final SparseArrayCompat<List<View>> removedViews = new SparseArrayCompat<List<View>>();
for (int i=0; i<container.getChildCount(); ++i) {
final View view = container.getChildAt(i);
final InternalItem item = getInternalItem(view);
if (destroyedItems.indexOf(item) >= 0) {
viewsToRemove.add(view);
List<View> views = removedViews.get(item.viewType);
if (views == null) {
views = new ArrayList<View>();
removedViews.put(item.viewType, views);
}
views.add(view);
}
}
for (View view : viewsToRemove) {
container.removeView(view);
}
return removedViews;
}
/**
* Clears all the items stored
*/
private void clearItems() {
instantiatedItems.clear();
destroyedItems.clear();
}
private static InternalItem getInternalItem(View view) {
return (InternalItem) view.getTag(TAG_KEY);
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="RECYCLER_PAGER_ADAPTER_TAG_KEY" type="id"/>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment