Android Open Source - InfinitePager Infinite View Pager






From Project

Back to project page InfinitePager.

License

The source code is released under:

Apache License

If you think the Android project InfinitePager listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2013 Adam Parr/* w  w w .j a  v  a  2  s  .  c o m*/
 *
 * 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.
 */

package com.github.paradam.infinitepager;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;

/**
 * <p>A ViewPager that handles simulating an infinite scrolling list of pages when used in conjunction
 * with an InfinitePagerAdapter. This implementation will only accept an instance of
 * {@link com.github.paradam.infinitepager.InfinitePagerAdapter} as any other instance of PagerAdapter
 * could have undefined behaviour.</p>
 *
 * <p>This implementation works by listening for {@link android.support.v4.view.ViewPager.OnPageChangeListener}
 * events before passing the call onto any attached OnPageChangeListener objects. Upon receiving an
 * event it checks to see if the page being changed to is beyond the limits of the total number of
 * pages as provided by {@link com.github.paradam.infinitepager.InfinitePagerAdapter#getRelativeCount()},
 * if the page is outside of this range, then the InfiniteViewPager will switch to the correct position
 * within the ViewPager once the animation has finished, or when the user begins another drag event
 * (whichever comes first).</p>
 *
 * <p>InfiniteViewPager is similar to a normal ViewPager except it is limited to the type of
 * PagerAdapter it can accept and for interacting with an InfinitePagerAdapter the use of {@link
 * #getRelativeCurrentItem()}, {@link #setRelativeCurrentItem(int)} and {@link
 * #setRelativeCurrentItem(int, boolean)} is recommended over using their respective methods found
 * in {@link android.support.v4.view.ViewPager}. Otherwise use like for like to keep consistency between an
 * InfiniteViewPager and InfinitePagerAdapter.</p>
 *
 * <p>Layout manager that allows the user to flip left and right through pages of data. You supply
 * an implementation of a {@link com.github.paradam.infinitepager.InfinitePagerAdapter} to generate
 * the pages that the view shows.</p>
 *
 * <p>InfiniteViewPager is most often used in conjunction with {@link android.app.Fragment}, which
 * is a convenient way to supply and manage the lifecycle of each page. There are standard adapters
 * implemented for using fragments with the InfiniteViewPager, which cover the most common use
 * cases.  These are {@link InfiniteFragmentPagerAdapter} and
 * {@link InfiniteFragmentStatePagerAdapter} for applications
 * targeting API 13 and greater or for applications using the v4 support package {@link
 * com.github.paradam.support.v4.infinitepager.InfiniteFragmentPagerAdapter} and {@link
 * com.github.paradam.support.v4.infinitepager.InfiniteFragmentStatePagerAdapter};</p>
 *
 * @author Adam Parr
 */
public class InfiniteViewPager extends ViewPager {

    /**
     * A reference to the infinitePageAdapter passed to the super class.
     */
    private InfinitePagerAdapter infinitePageAdapter;

    /**
     * The external OnPageChangeListener
     */
    private OnPageChangeListener mOnPageChangeListener;

    /**
     * For the use of PagerTitleStrip and other similar classes.
     */
    private OnPageChangeListener mInternalPageChangeListener;

    /**
     * Constructor that accepts the context in which the ViewPager resides in.
     *
     * @param context The Context.
     */
    public InfiniteViewPager(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor that accepts the context in which the ViewPager resides in and any attributes.
     *
     * @param context The Context.
     * @param attrs   AttributeSet
     */
    public InfiniteViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * Initialise the InfiniteViewPager.
     */
    private void init() {
        /*
         * Set up listener to listen for the moments when the current selected page has been changed
         * to a page less than InfinitePageAdapter#MARGIN, or larger than
         * InfinitePageAdapter#getRelativeCount()+ InfinitePageAdapter#MARGIN
         */
        super.setOnPageChangeListener(new OnPageChangeListener() {
            /**
             * The page which the {@link #pageScrollRunner} will scroll to when the thread is run.<br>
             * A value of -1 means no page has been set since the last time #pageScrollRunner was run.
             */
            private int toPage = -1;

            /**
             * A Runnable that will set the Page Adapter {@link com.github.paradam.infinitepager.InfiniteViewPager#infinitePageAdapter}
             * to the page identified in {@link #toPage}, before then setting toPage to <code>-1</code>.
             */
            private final Runnable pageScrollRunner = new Runnable() {
                public synchronized void run() {
                    if (toPage >= 0) {
                        /*
                         * Switch to the real page, don't animate transition to make it
                         * seem that the page is not changed.
                         */
                        setRelativeCurrentItem(toPage, false);
                        toPage = -1;
                    }
                }
            };

            @Override
            public void onPageScrollStateChanged(int state) {
                switch (state) {
                    case ViewPager.SCROLL_STATE_DRAGGING:
                        /*
                         *  User has started dragging, quickly change the page to the correct one,
                         *  this may cause the view to jump.
                         */
                    case ViewPager.SCROLL_STATE_IDLE: // Animation has finished, switch now.
                        if (toPage >= 0) {
                            post(pageScrollRunner);
                        }
                }
                if (mInternalPageChangeListener != null) {
                    mInternalPageChangeListener.onPageScrollStateChanged(state);
                }
                if (mOnPageChangeListener != null) {
                    mOnPageChangeListener.onPageScrollStateChanged(state);
                }
            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                if (mInternalPageChangeListener != null) {
                    mInternalPageChangeListener.onPageScrolled(calculateRelative(position), positionOffset, positionOffsetPixels);
                }
                if (mOnPageChangeListener != null) {
                    mOnPageChangeListener.onPageScrolled(calculateRelative(position), positionOffset, positionOffsetPixels);
                }
            }

            @Override
            public void onPageSelected(int position) {
                int calcPos = calculateRelative(position);
                if (position - infinitePageAdapter.getMargin() != calcPos) {
                    /*
                     *  If the given position differs from the real position calculated.
                     *  Set #toPage to the page it should be on (if position is one that is
                     *  less than InfiniteViewPager#getMargin()
                     *  or larger than (InfinitePageAdapter#getRelativeCount() +
                     *  InfinitePageAdapter#getMargin()), then store the correct page
                     *  it should change to when the animation stops to make as smooth
                     *  transition as possible.
                     */
                    toPage = calcPos;
                }

                if (mInternalPageChangeListener != null) {
                    mInternalPageChangeListener.onPageSelected(calcPos);
                }
                if (mOnPageChangeListener != null) {
                    mOnPageChangeListener.onPageSelected(calcPos);
                }
            }
        });
    }

    /**
     * Set a separate OnPageChangeListener for internal use by the support library.
     *
     * @param listener Listener to set
     * @return The old listener that was set, if any.
     */
    protected OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
        OnPageChangeListener oldListener = mInternalPageChangeListener;
        mInternalPageChangeListener = listener;
        return oldListener;
    }

    /**
     * Calculates the relative position of the current page, where page at <code>{@link
     * com.github.paradam.infinitepager.InfinitePagerAdapter#getMargin()}</code> maps to the relative
     * position of <code>0</code>, and page at
     * <code>{@link com.github.paradam.infinitepager.InfinitePagerAdapter#getCount()} - {@link com.github.paradam.infinitepager.InfinitePagerAdapter#getMargin()}</code>
     * will map to the relative position of <code>{@link com.github.paradam.infinitepager.InfinitePagerAdapter#getRelativeCount()} -1</code>.
     *
     * @param position The absolute position of the page.
     * @return The relative position of the page.
     */
    private int calculateRelative(int position) {
        // Get the number of real pages.
        int length = infinitePageAdapter.getRelativeCount();
        // Calculate the real page the given position should be.
        return length > 0 ? (position - infinitePageAdapter.getMargin() + length) % length : 0;
    }

    /**
     * Get the index of the currently selected page.
     *
     * @return An integer of the current pages position.
     */
    public int getRelativeCurrentItem() {
        return calculateRelative(getCurrentItem());
    }

    /**
     * Change the page of the ViewPager, changes to a item between <code>0</code> and {@link
     * com.github.paradam.infinitepager.InfinitePagerAdapter#getRelativeCount()}.
     *
     * @param item The item to switch to.
     */
    public void setRelativeCurrentItem(int item) {
        setRelativeCurrentItem(item, true);
    }

    /**
     * Change the page of the ViewPager, changes to an item between <code>0</code> and {@link
     * com.github.paradam.infinitepager.InfinitePagerAdapter#getRelativeCount()}.
     *
     * @param item         The item to switch to.
     * @param smoothScroll <tt>true</tt> to animate the scrolling to the page, <tt>false</tt> to
     *                     switch directly to the page.
     */
    public void setRelativeCurrentItem(int item, boolean smoothScroll) {
        int count = infinitePageAdapter.getRelativeCount();
        int margin = infinitePageAdapter.getMargin();
        int current = getRelativeCurrentItem();
        if (margin > 0) {
            if (current == 0 && item == count - 1) {
                super.setCurrentItem(current + margin - 1, smoothScroll);
                return;
            } else if(current == count -1 && item == 0) {
                super.setCurrentItem(count + margin, smoothScroll);
                return;
            }
        }
        super.setCurrentItem((count > 0 ? (item % count) + margin : 0), smoothScroll);
    }

    /**
     * Set a PagerAdapter that will supply views for this pager as needed.
     *
     * @param infinitePagerAdapter Adapter to use
     */
    public void setAdapter(InfinitePagerAdapter infinitePagerAdapter) {
        setAdapter(infinitePagerAdapter, 0); // Set the pager adapter to the real page 0.
        // If the ViewPager behaves as an infinite scrolling list, then what would
        // be considered the 0 page is not in the 0 index position.
    }

    /**
     * Set a PagerAdapter that will supply views for this pager as needed.
     *
     * @param infinitePagerAdapter Adapter to use
     * @param initialItem          The initial item to show.
     */
    public void setAdapter(InfinitePagerAdapter infinitePagerAdapter, int initialItem) {
        infinitePageAdapter = infinitePagerAdapter;
        super.setAdapter(infinitePageAdapter);
        setRelativeCurrentItem(initialItem, false);
    }

    /**
     * Set a PagerAdapter that will supply views for this pager as needed.
     *
     * @param pagerAdapter Adapter to use
     * @see #setAdapter(com.github.paradam.infinitepager.InfinitePagerAdapter)
     * @deprecated A {@link com.github.paradam.infinitepager.InfinitePagerAdapter} or sub-class of is required for this class to work
     * as intended.
     */
    @Override
    public void setAdapter(android.support.v4.view.PagerAdapter pagerAdapter) {
        setAdapter((InfinitePagerAdapter) pagerAdapter);
    }

    /**
     * Set a listener that will be invoked whenever the page changes or is incrementally scrolled.
     * See {@link android.support.v4.view.ViewPager.OnPageChangeListener}.
     *
     * @param listener Listener to set
     */
    @Override
    public void setOnPageChangeListener(OnPageChangeListener listener) {
        mOnPageChangeListener = listener;
    }

    /**
     * <p>Get the index position of the current visible item. <p/>
     *
     * <p>Use {@link #getRelativeCurrentItem()} to get the position expected by sub-classes.</p>
     *
     * @see #getRelativeCurrentItem()
     */
    @Override
    public int getCurrentItem() {
        return super.getCurrentItem();
    }

    /**
     * <p>Set the currently selected page. If the ViewPager has already been through its first layout
     * with its current adapter there will be a smooth animated transition between the current item
     * and the specified item. <p/>
     *
     * <p>Use {@link #setRelativeCurrentItem(int)} to set the currently visible position as expected
     * from sub-classes.</p>
     *
     * @param item Item index to select
     * @see #setRelativeCurrentItem(int)
     */
    @Override
    public void setCurrentItem(int item) {
        setCurrentItem(item, true);
    }

    /**
     * <p>Set the currently selected page.<p/>
     *
     * <p>Use {@link #setRelativeCurrentItem(int, boolean)} to set the currently visible position as
     * expected from sub-classes.</p>
     *
     * @param item         Item index to select
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
     * @see #setRelativeCurrentItem(int, boolean)
     */
    @Override
    public void setCurrentItem(int item, boolean smoothScroll) {
        super.setCurrentItem(item, smoothScroll);
    }
}




Java Source Code List

android.support.v4.view.SlidablePagerTitle.java
android.support.v4.view.SlidingTabLayout.java
android.support.v4.view.SlidingTabStrip.java
com.github.paradam.infinitepager.InfiniteFragmentPagerAdapter.java
com.github.paradam.infinitepager.InfiniteFragmentStatePagerAdapter.java
com.github.paradam.infinitepager.InfinitePagerAdapter.java
com.github.paradam.infinitepager.InfiniteViewPager.java
com.github.paradam.infinitepager.demo.v4.InfiniteFragmentStatePagerActivity.java
com.github.paradam.support.v4.infinitepager.InfiniteFragmentPagerAdapter.java
com.github.paradam.support.v4.infinitepager.InfiniteFragmentStatePagerAdapter.java