imageIdList) {
@Override
public int getCount() {
- return Integer.MAX_VALUE;// ListUtils.getSize(imageIdList);
+ // Infinite loop
+ return Integer.MAX_VALUE;
}
@Override
- public boolean isViewFromObject(View view, Object object) {
- return (view == object);
+ public View getView(int position, View view, ViewGroup container) {
+ ViewHolder holder;
+ if (view == null) {
+ holder = new ViewHolder();
+ view = holder.imageView = new ImageView(context);
+ view.setTag(holder);
+ } else {
+ holder = (ViewHolder)view.getTag();
+ }
+ holder.imageView.setImageResource(imageIdList.get(position % size));
+ return view;
}
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- ImageView imageView = new ImageView(context);
- imageView.setImageResource(imageIdList.get(position % size));
- ((ViewPager)container).addView(imageView, 0);
- return imageView;
- }
+ private static class ViewHolder {
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- ((ViewPager)container).removeView((ImageView)object);
+ ImageView imageView;
}
}
diff --git a/src/com/jakewharton/salvage/RecycleBin.java b/src/com/jakewharton/salvage/RecycleBin.java
new file mode 100644
index 0000000..953801d
--- /dev/null
+++ b/src/com/jakewharton/salvage/RecycleBin.java
@@ -0,0 +1,152 @@
+package com.jakewharton.salvage;
+
+import android.os.Build;
+import android.util.SparseArray;
+import android.view.View;
+
+/**
+ * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
+ * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
+ * start of a layout. By construction, they are displaying current information. At the end of
+ * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
+ * could potentially be used by the adapter to avoid allocating views unnecessarily.
+ *
+ * This class was taken from Android's implementation of {@link android.widget.AbsListView} which
+ * is copyrighted 2006 The Android Open Source Project.
+ */
+public class RecycleBin {
+ /**
+ * Views that were on screen at the start of layout. This array is populated at the start of
+ * layout, and at the end of layout all view in activeViews are moved to scrapViews.
+ * Views in activeViews represent a contiguous range of Views, with position of the first
+ * view store in mFirstActivePosition.
+ */
+ private View[] activeViews = new View[0];
+ private int[] activeViewTypes = new int[0];
+
+ /** Unsorted views that can be used by the adapter as a convert view. */
+ private SparseArray[] scrapViews;
+
+ private int viewTypeCount;
+
+ private SparseArray currentScrapViews;
+
+ public void setViewTypeCount(int viewTypeCount) {
+ if (viewTypeCount < 1) {
+ throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
+ }
+ //noinspection unchecked
+ SparseArray[] scrapViews = new SparseArray[viewTypeCount];
+ for (int i = 0; i < viewTypeCount; i++) {
+ scrapViews[i] = new SparseArray();
+ }
+ this.viewTypeCount = viewTypeCount;
+ currentScrapViews = scrapViews[0];
+ this.scrapViews = scrapViews;
+ }
+
+ protected boolean shouldRecycleViewType(int viewType) {
+ return viewType >= 0;
+ }
+
+ /** @return A view from the ScrapViews collection. These are unordered. */
+ View getScrapView(int position, int viewType) {
+ if (viewTypeCount == 1) {
+ return retrieveFromScrap(currentScrapViews, position);
+ } else if (viewType >= 0 && viewType < scrapViews.length) {
+ return retrieveFromScrap(scrapViews[viewType], position);
+ }
+ return null;
+ }
+
+ /**
+ * Put a view into the ScrapViews list. These views are unordered.
+ *
+ * @param scrap The view to add
+ */
+ void addScrapView(View scrap, int position, int viewType) {
+ if (viewTypeCount == 1) {
+ currentScrapViews.put(position, scrap);
+ } else {
+ scrapViews[viewType].put(position, scrap);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ scrap.setAccessibilityDelegate(null);
+ }
+ }
+
+ /** Move all views remaining in activeViews to scrapViews. */
+ void scrapActiveViews() {
+ final View[] activeViews = this.activeViews;
+ final int[] activeViewTypes = this.activeViewTypes;
+ final boolean multipleScraps = viewTypeCount > 1;
+
+ SparseArray scrapViews = currentScrapViews;
+ final int count = activeViews.length;
+ for (int i = count - 1; i >= 0; i--) {
+ final View victim = activeViews[i];
+ if (victim != null) {
+ int whichScrap = activeViewTypes[i];
+
+ activeViews[i] = null;
+ activeViewTypes[i] = -1;
+
+ if (!shouldRecycleViewType(whichScrap)) {
+ continue;
+ }
+
+ if (multipleScraps) {
+ scrapViews = this.scrapViews[whichScrap];
+ }
+ scrapViews.put(i, victim);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ victim.setAccessibilityDelegate(null);
+ }
+ }
+ }
+
+ pruneScrapViews();
+ }
+
+ /**
+ * Makes sure that the size of scrapViews does not exceed the size of activeViews.
+ * (This can happen if an adapter does not recycle its views).
+ */
+ private void pruneScrapViews() {
+ final int maxViews = activeViews.length;
+ final int viewTypeCount = this.viewTypeCount;
+ final SparseArray[] scrapViews = this.scrapViews;
+ for (int i = 0; i < viewTypeCount; ++i) {
+ final SparseArray scrapPile = scrapViews[i];
+ int size = scrapPile.size();
+ final int extras = size - maxViews;
+ size--;
+ for (int j = 0; j < extras; j++) {
+ scrapPile.remove(scrapPile.keyAt(size--));
+ }
+ }
+ }
+
+ static View retrieveFromScrap(SparseArray scrapViews, int position) {
+ int size = scrapViews.size();
+ if (size > 0) {
+ // See if we still have a view for this position.
+ for (int i = 0; i < size; i++) {
+ int fromPosition = scrapViews.keyAt(i);
+ View view = scrapViews.get(fromPosition);
+ if (fromPosition == position) {
+ scrapViews.remove(fromPosition);
+ return view;
+ }
+ }
+ int index = size - 1;
+ View r = scrapViews.valueAt(index);
+ scrapViews.remove(scrapViews.keyAt(index));
+ return r;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/com/jakewharton/salvage/RecyclingPagerAdapter.java b/src/com/jakewharton/salvage/RecyclingPagerAdapter.java
new file mode 100644
index 0000000..b1f10c6
--- /dev/null
+++ b/src/com/jakewharton/salvage/RecyclingPagerAdapter.java
@@ -0,0 +1,108 @@
+package com.jakewharton.salvage;
+
+import android.support.v4.view.PagerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+/**
+ * A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and
+ * view recycling.
+ */
+public abstract class RecyclingPagerAdapter extends PagerAdapter {
+ static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
+
+ private final RecycleBin recycleBin;
+
+ public RecyclingPagerAdapter() {
+ this(new RecycleBin());
+ }
+
+ RecyclingPagerAdapter(RecycleBin recycleBin) {
+ this.recycleBin = recycleBin;
+ recycleBin.setViewTypeCount(getViewTypeCount());
+ }
+
+ @Override public void notifyDataSetChanged() {
+ recycleBin.scrapActiveViews();
+ super.notifyDataSetChanged();
+ }
+
+ @Override public final Object instantiateItem(ViewGroup container, int position) {
+ int viewType = getItemViewType(position);
+ View view = null;
+ if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+ view = recycleBin.getScrapView(position, viewType);
+ }
+ view = getView(position, view, container);
+ container.addView(view);
+ return view;
+ }
+
+ @Override public final void destroyItem(ViewGroup container, int position, Object object) {
+ View view = (View) object;
+ container.removeView(view);
+ int viewType = getItemViewType(position);
+ if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+ recycleBin.addScrapView(view, position, viewType);
+ }
+ }
+
+ @Override public final boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ /**
+ *
+ * Returns the number of types of Views that will be created by
+ * {@link #getView}. Each type represents a set of views that can be
+ * converted in {@link #getView}. If the adapter always returns the same
+ * type of View for all items, this method should return 1.
+ *
+ *
+ * This method will only be called when when the adapter is set on the
+ * the {@link AdapterView}.
+ *
+ *
+ * @return The number of types of Views that will be created by this adapter
+ */
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ /**
+ * Get the type of View that will be created by {@link #getView} for the specified item.
+ *
+ * @param position The position of the item within the adapter's data set whose view type we
+ * want.
+ * @return An integer representing the type of View. Two views should share the same type if one
+ * can be converted to the other in {@link #getView}. Note: Integers must be in the
+ * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
+ * also be returned.
+ * @see #IGNORE_ITEM_VIEW_TYPE
+ */
+ @SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ /**
+ * Get a View that displays the data at the specified position in the data set. You can either
+ * create a View manually or inflate it from an XML layout file. When the View is inflated, the
+ * parent View (GridView, ListView...) will apply default layout parameters unless you use
+ * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
+ * to specify a root view and to prevent attachment to the root.
+ *
+ * @param position The position of the item within the adapter's data set of the item whose view
+ * we want.
+ * @param convertView The old view to reuse, if possible. Note: You should check that this view
+ * is non-null and of an appropriate type before using. If it is not possible to convert
+ * this view to display the correct data, this method can create a new view.
+ * Heterogeneous lists can specify their number of view types, so that this View is
+ * always of the right type (see {@link #getViewTypeCount()} and
+ * {@link #getItemViewType(int)}).
+ * @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 container);
+}