├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── demo
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── antonyt
│ │ └── infiniteviewpager
│ │ ├── ColourFragment.java
│ │ ├── InfiniteViewPager2Activity.java
│ │ └── InfiniteViewPagerActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── icon.png
│ ├── drawable-ldpi
│ └── icon.png
│ ├── drawable-mdpi
│ └── icon.png
│ ├── layout
│ └── main.xml
│ └── values
│ └── strings.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── antonyt
│ └── infiniteviewpager
│ ├── InfinitePagerAdapter.java
│ ├── InfiniteViewPager.java
│ └── MinFragmentPagerAdapter.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | gen/
3 | .settings
4 | target/
5 | build/
6 | local.properties
7 | .gradle
8 | *.iml
9 | .idea
10 | .classpath
11 | .project
12 |
13 | *.DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Antony Tran
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Infinite View Pager
2 | ===============
3 |
4 | Augment Android's ViewPager with wrap-around functionality. Original StackOverflow question: http://stackoverflow.com/questions/7546224/viewpager-as-a-circular-queue-wrapping
5 |
6 | ## Problem
7 | With a normal ViewPager, you can only scroll from the first page to second page (and so forth), from left-to-right. Once you reach the last page, your only option is to scroll backwards, right-to-left. In other words, 'wrap-around scrolling' (going from first-to-last, and last-to-first) is not possible.
8 |
9 | ## Solution
10 | Use the `InfiniteViewPager` in your Activity/Fragment layout:
11 |
12 | ``` xml
13 |
17 | ```
18 |
19 | Wrap your existing `PagerAdapter` with the `InfinitePagerAdapter`:
20 |
21 | ``` java
22 | PagerAdapter wrappedAdapter = new InfinitePagerAdapter(adapter);
23 | viewPager.setAdapter(wrappedAdapter);
24 | ```
25 |
26 |
27 | ### Gradle build
28 |
29 | To install the demo application to your device run the following task:
30 |
31 | ```
32 | $ ./gradlew installDebug
33 | ```
34 |
35 | To deploy the library to your local Maven repository run the following task:
36 |
37 | ```
38 | $ ./gradlew install
39 | ```
40 |
41 | Then, to use the library in your project add the following to your `build.gradle`:
42 |
43 | ```groovy
44 | dependencies {
45 | compile 'com.antonyt.infiniteviewpager:library:1.0.0'
46 | }
47 | ```
48 |
49 | Wrapped scrolling should now be possible with your `ViewPager`. The pages you see are not duplicates - each page from your `PagerAdapter` is only created once and then reused. This means you do not have to worry about managing multiple instances of the same `Fragment`.
50 |
51 | ## Caveats
52 | It is only possible to achieve wrapping when you have at least 4 pages. This is because of the way the `ViewPager` creates, destroys, and displays the pages. No fix for the general case has been found.
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | google()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:3.1.1'
8 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | mavenCentral()
15 | jcenter()
16 | google()
17 | }
18 |
19 | version = VERSION_NAME
20 | }
21 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 |
6 | defaultConfig {
7 | targetSdkVersion 19
8 | minSdkVersion 14
9 | versionName project.VERSION_NAME
10 | versionCode Integer.parseInt(project.VERSION_CODE)
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_1_7
15 | targetCompatibility JavaVersion.VERSION_1_7
16 | }
17 |
18 | }
19 |
20 | dependencies {
21 | compile 'com.android.support:support-v4:27.1.1'
22 | compile project(':library')
23 | }
24 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/antonyt/infiniteviewpager/ColourFragment.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.view.Gravity;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.TextView;
11 |
12 | /**
13 | * Example Fragment class that shows an identifier inside a TextView.
14 | */
15 | public class ColourFragment extends Fragment {
16 |
17 | private int identifier;
18 | private int colour;
19 |
20 | @Override
21 | public void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | Bundle args = getArguments();
24 | colour = args.getInt("colour");
25 | identifier = args.getInt("identifier");
26 | }
27 |
28 | @Override
29 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
30 | TextView v = new TextView(getActivity());
31 | v.setGravity(Gravity.CENTER);
32 | v.setTextSize(40);
33 | v.setTextColor(Color.BLACK);
34 | v.setBackgroundColor(colour);
35 | v.setText("Fragment ID: " + identifier);
36 | return v;
37 | }
38 |
39 | @Override
40 | public void onSaveInstanceState(Bundle outState) {
41 | super.onSaveInstanceState(outState);
42 | outState.putBoolean("dummy", true);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/antonyt/infiniteviewpager/InfiniteViewPager2Activity.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentActivity;
7 | import android.support.v4.app.FragmentPagerAdapter;
8 | import android.support.v4.view.PagerAdapter;
9 | import android.support.v4.view.ViewPager;
10 |
11 | /**
12 | * Sample for {@link com.antonyt.infiniteviewpager.MinFragmentPagerAdapter} (support for less than
13 | * 4 pages). Duplicate instances of pages will be created to fulfill the min case.
14 | */
15 | public class InfiniteViewPager2Activity extends FragmentActivity {
16 |
17 | @Override
18 | public void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.main);
21 |
22 | FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
23 | int[] colours = new int[]{Color.CYAN, Color.MAGENTA};
24 |
25 | @Override
26 | public int getCount() {
27 | return colours.length;
28 | }
29 |
30 | @Override
31 | public Fragment getItem(int position) {
32 | Fragment fragment = new ColourFragment();
33 | Bundle args = new Bundle();
34 | args.putInt("colour", colours[position]);
35 | args.putInt("identifier", position);
36 | fragment.setArguments(args);
37 | return fragment;
38 | }
39 |
40 | };
41 |
42 | // wrap pager to provide a minimum of 4 pages
43 | MinFragmentPagerAdapter wrappedMinAdapter = new MinFragmentPagerAdapter(getSupportFragmentManager());
44 | wrappedMinAdapter.setAdapter(adapter);
45 |
46 | // wrap pager to provide infinite paging with wrap-around
47 | PagerAdapter wrappedAdapter = new InfinitePagerAdapter(wrappedMinAdapter);
48 |
49 | // actually an InfiniteViewPager
50 | ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
51 | viewPager.setAdapter(wrappedAdapter);
52 |
53 | }
54 | }
--------------------------------------------------------------------------------
/demo/src/main/java/com/antonyt/infiniteviewpager/InfiniteViewPagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentActivity;
7 | import android.support.v4.app.FragmentPagerAdapter;
8 | import android.support.v4.view.PagerAdapter;
9 | import android.support.v4.view.ViewPager;
10 |
11 | public class InfiniteViewPagerActivity extends FragmentActivity {
12 |
13 | @Override
14 | public void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.main);
17 |
18 | PagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
19 | int[] colours = new int[]{Color.CYAN, Color.GRAY, Color.MAGENTA, Color.LTGRAY, Color.GREEN, Color.WHITE,
20 | Color.YELLOW};
21 |
22 | @Override
23 | public int getCount() {
24 | return colours.length;
25 | }
26 |
27 | @Override
28 | public Fragment getItem(int position) {
29 | Fragment fragment = new ColourFragment();
30 | Bundle args = new Bundle();
31 | args.putInt("colour", colours[position]);
32 | args.putInt("identifier", position);
33 | fragment.setArguments(args);
34 | return fragment;
35 | }
36 | };
37 |
38 | // wrap pager to provide infinite paging with wrap-around
39 | PagerAdapter wrappedAdapter = new InfinitePagerAdapter(adapter);
40 |
41 | // actually an InfiniteViewPager
42 | ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
43 | viewPager.setAdapter(wrappedAdapter);
44 |
45 | }
46 | }
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antonyt/InfiniteViewPager/d25ae598124bc4e8afe39ac1debd55105a5d36aa/demo/src/main/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antonyt/InfiniteViewPager/d25ae598124bc4e8afe39ac1debd55105a5d36aa/demo/src/main/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antonyt/InfiniteViewPager/d25ae598124bc4e8afe39ac1debd55105a5d36aa/demo/src/main/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/demo/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello World, InfiniteViewPagerActivity!
4 | InfiniteViewPager
5 |
6 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=1.0.0
2 | VERSION_CODE=1
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antonyt/InfiniteViewPager/d25ae598124bc4e8afe39ac1debd55105a5d36aa/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'android-maven'
3 |
4 | group 'com.antonyt.infiniteviewpager'
5 |
6 | android {
7 | compileSdkVersion 27
8 |
9 | defaultConfig {
10 | targetSdkVersion 19
11 | minSdkVersion 14
12 | versionName project.VERSION_NAME
13 | versionCode Integer.parseInt(project.VERSION_CODE)
14 | }
15 |
16 | compileOptions {
17 | sourceCompatibility JavaVersion.VERSION_1_7
18 | targetCompatibility JavaVersion.VERSION_1_7
19 | }
20 | }
21 |
22 | dependencies {
23 | compile 'com.android.support:support-core-ui:27.1.1'
24 | compile 'com.android.support:support-fragment:27.1.1'
25 | }
26 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/library/src/main/java/com/antonyt/infiniteviewpager/InfinitePagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 | import android.database.DataSetObserver;
4 | import android.os.Parcelable;
5 | import android.support.v4.view.PagerAdapter;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | /**
11 | * A PagerAdapter that wraps around another PagerAdapter to handle paging wrap-around.
12 | */
13 | public class InfinitePagerAdapter extends PagerAdapter {
14 |
15 | private static final String TAG = "InfinitePagerAdapter";
16 | private static final boolean DEBUG = false;
17 |
18 | private PagerAdapter adapter;
19 |
20 | public InfinitePagerAdapter(PagerAdapter adapter) {
21 | this.adapter = adapter;
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | if (getRealCount() == 0) {
27 | return 0;
28 | }
29 | // warning: scrolling to very high values (1,000,000+) results in
30 | // strange drawing behaviour
31 | return Integer.MAX_VALUE;
32 | }
33 |
34 | /**
35 | * @return the {@link #getCount()} result of the wrapped adapter
36 | */
37 | public int getRealCount() {
38 | return adapter.getCount();
39 | }
40 |
41 | @Override
42 | public Object instantiateItem(ViewGroup container, int position) {
43 | int virtualPosition = position % getRealCount();
44 | debug("instantiateItem: real position: " + position);
45 | debug("instantiateItem: virtual position: " + virtualPosition);
46 |
47 | // only expose virtual position to the inner adapter
48 | return adapter.instantiateItem(container, virtualPosition);
49 | }
50 |
51 | @Override
52 | public void destroyItem(ViewGroup container, int position, Object object) {
53 | int virtualPosition = position % getRealCount();
54 | debug("destroyItem: real position: " + position);
55 | debug("destroyItem: virtual position: " + virtualPosition);
56 |
57 | // only expose virtual position to the inner adapter
58 | adapter.destroyItem(container, virtualPosition, object);
59 | }
60 |
61 | /*
62 | * Delegate rest of methods directly to the inner adapter.
63 | */
64 |
65 | @Override
66 | public void finishUpdate(ViewGroup container) {
67 | adapter.finishUpdate(container);
68 | }
69 |
70 | @Override
71 | public boolean isViewFromObject(View view, Object object) {
72 | return adapter.isViewFromObject(view, object);
73 | }
74 |
75 | @Override
76 | public void restoreState(Parcelable bundle, ClassLoader classLoader) {
77 | adapter.restoreState(bundle, classLoader);
78 | }
79 |
80 | @Override
81 | public Parcelable saveState() {
82 | return adapter.saveState();
83 | }
84 |
85 | @Override
86 | public void startUpdate(ViewGroup container) {
87 | adapter.startUpdate(container);
88 | }
89 |
90 | @Override
91 | public CharSequence getPageTitle(int position) {
92 | int virtualPosition = position % getRealCount();
93 | return adapter.getPageTitle(virtualPosition);
94 | }
95 |
96 | @Override
97 | public float getPageWidth(int position) {
98 | return adapter.getPageWidth(position);
99 | }
100 |
101 | @Override
102 | public void setPrimaryItem(ViewGroup container, int position, Object object) {
103 | adapter.setPrimaryItem(container, position, object);
104 | }
105 |
106 | @Override
107 | public void unregisterDataSetObserver(DataSetObserver observer) {
108 | adapter.unregisterDataSetObserver(observer);
109 | }
110 |
111 | @Override
112 | public void registerDataSetObserver(DataSetObserver observer) {
113 | adapter.registerDataSetObserver(observer);
114 | }
115 |
116 | @Override
117 | public void notifyDataSetChanged() {
118 | adapter.notifyDataSetChanged();
119 | }
120 |
121 | @Override
122 | public int getItemPosition(Object object) {
123 | return adapter.getItemPosition(object);
124 | }
125 |
126 | /*
127 | * End delegation
128 | */
129 |
130 | private void debug(String message) {
131 | if (DEBUG) {
132 | Log.d(TAG, message);
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/library/src/main/java/com/antonyt/infiniteviewpager/InfiniteViewPager.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 |
4 | import android.content.Context;
5 | import android.support.v4.view.PagerAdapter;
6 | import android.support.v4.view.ViewPager;
7 | import android.util.AttributeSet;
8 |
9 | /**
10 | * A {@link ViewPager} that allows pseudo-infinite paging with a wrap-around effect. Should be used with an {@link
11 | * InfinitePagerAdapter}.
12 | */
13 | public class InfiniteViewPager extends ViewPager {
14 |
15 | public InfiniteViewPager(Context context) {
16 | super(context);
17 | }
18 |
19 | public InfiniteViewPager(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | @Override
24 | public void setAdapter(PagerAdapter adapter) {
25 | super.setAdapter(adapter);
26 | // offset first element so that we can scroll to the left
27 | setCurrentItem(0);
28 | }
29 |
30 | @Override
31 | public void setCurrentItem(int item) {
32 | // offset the current item to ensure there is space to scroll
33 | setCurrentItem(item, false);
34 | }
35 |
36 | @Override
37 | public void setCurrentItem(int item, boolean smoothScroll) {
38 | if (getAdapter().getCount() == 0) {
39 | super.setCurrentItem(item, smoothScroll);
40 | return;
41 | }
42 | item = getOffsetAmount() + (item % getAdapter().getCount());
43 | super.setCurrentItem(item, smoothScroll);
44 | }
45 |
46 | @Override
47 | public int getCurrentItem() {
48 | if (getAdapter().getCount() == 0) {
49 | return super.getCurrentItem();
50 | }
51 | int position = super.getCurrentItem();
52 | if (getAdapter() instanceof InfinitePagerAdapter) {
53 | InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();
54 | // Return the actual item position in the data backing InfinitePagerAdapter
55 | return (position % infAdapter.getRealCount());
56 | } else {
57 | return super.getCurrentItem();
58 | }
59 | }
60 |
61 | private int getOffsetAmount() {
62 | if (getAdapter().getCount() == 0) {
63 | return 0;
64 | }
65 | if (getAdapter() instanceof InfinitePagerAdapter) {
66 | InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();
67 | // allow for 100 back cycles from the beginning
68 | // should be enough to create an illusion of infinity
69 | // warning: scrolling to very high values (1,000,000+) results in
70 | // strange drawing behaviour
71 | return infAdapter.getRealCount() * 100;
72 | } else {
73 | return 0;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/library/src/main/java/com/antonyt/infiniteviewpager/MinFragmentPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.antonyt.infiniteviewpager;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentPagerAdapter;
6 | import android.view.View;
7 |
8 | /**
9 | * A FragmentPagerAdapter that can be used to achieve paging wrap-around when you have less than 4
10 | * pages. Duplicate instances of pages will be created to fulfill the min case.
11 | */
12 | public class MinFragmentPagerAdapter extends FragmentPagerAdapter {
13 |
14 | private FragmentPagerAdapter adapter;
15 |
16 | public MinFragmentPagerAdapter(FragmentManager fm) {
17 | super(fm);
18 | }
19 |
20 | public void setAdapter(FragmentPagerAdapter adapter) {
21 | this.adapter = adapter;
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | int realCount = adapter.getCount();
27 | if (realCount == 1) {
28 | return 4;
29 | } else if (realCount == 2 || realCount == 3) {
30 | return realCount * 2;
31 | } else {
32 | return realCount;
33 | }
34 | }
35 |
36 | @Override
37 | public boolean isViewFromObject(View view, Object object) {
38 | return adapter.isViewFromObject(view, object);
39 | }
40 |
41 | /**
42 | * Warning: If you only have 1-3 real pages, this method will create multiple, duplicate
43 | * instances of your Fragments to ensure wrap-around is possible. This may be a problem if you
44 | * have editable fields or transient state (they will not be in sync).
45 | *
46 | * @param position
47 | * @return
48 | */
49 | @Override
50 | public Fragment getItem(int position) {
51 | int realCount = adapter.getCount();
52 | if (realCount == 1) {
53 | return adapter.getItem(0);
54 | } else if (realCount == 2 || realCount == 3) {
55 | return adapter.getItem(position % realCount);
56 | } else {
57 | return adapter.getItem(position);
58 | }
59 | }
60 |
61 |
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 | include ':demo'
--------------------------------------------------------------------------------