├── README.md ├── apk ├── demo.apk └── demo.gif ├── build.gradle ├── demo ├── build.gradle ├── libs │ └── universal-image-loader-1.9.5.jar ├── manifest-merger-release-report.txt ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── cn │ │ └── lightsky │ │ │ └── infiniteindicator │ │ │ ├── AnimIndicatorActivity.java │ │ │ ├── DefaultCircleIndicatorActivity.java │ │ │ ├── GlideLoader.java │ │ │ ├── PicassoLoader.java │ │ │ └── UILoader.java │ └── test │ │ ├── AddSliderActivity.java │ │ └── UpdateSliderActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── a_yypd.jpg │ ├── b_yypd.jpg │ ├── c_yypd.jpg │ ├── d_yypd.jpg │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── a.jpg │ ├── b.jpg │ ├── c.jpg │ ├── d.jpg │ └── ic_launcher.png │ ├── drawable │ ├── error.jpg │ └── placeholder.jpg │ ├── layout │ ├── activity_anim_indicator.xml │ ├── activity_default_circle_indicator.xml │ └── layout_custome_page.xml │ ├── menu │ └── menu.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── lightsky │ │ └── infiniteindicator │ │ ├── DurationScroller.java │ │ ├── ImageLoader.java │ │ ├── IndicatorConfiguration.java │ │ ├── InfiniteIndicator.java │ │ ├── OnPageClickListener.java │ │ ├── Page.java │ │ ├── ViewUtils.java │ │ ├── indicator │ │ ├── AnimIndicator.java │ │ ├── CircleIndicator.java │ │ └── PageIndicator.java │ │ └── recycle │ │ ├── BaseViewBinder.java │ │ ├── RecycleBin.java │ │ ├── RecyclingPagerAdapter.java │ │ ├── RecyleAdapter.java │ │ └── ViewBinder.java │ └── res │ ├── animator │ ├── scale_with_alpha.xml │ └── translation_and_rotate.xml │ ├── drawable │ ├── balck_radius_square.xml │ └── white_radius.xml │ ├── layout │ ├── layout_anim_circle_indicator.xml │ ├── layout_anim_line_indicator.xml │ ├── layout_default_indicator.xml │ └── simple_slider_view.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── style.xml │ └── vpi__defaults.xml ├── res ├── a.jpg ├── b.jpg ├── c.jpg └── d.jpg └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | InfiniteIndicator 2 | =========================== 3 | 4 | This project is inspired by the [android-auto-scroll-view-pager](https://github.com/Trinea/android-auto-scroll-view-pager) of [Trinea](https://github.com/Trinea). Use the [salvage](https://github.com/JakeWharton/salvage) lib implement 5 | view recycle adapter.It contains two style.One is CircleIndicator seperated from [Android-ViewPagerIndicator](https://github.com/JakeWharton/Android-ViewPagerIndicator).Another is copy from [CircleIndicator](https://github.com/ongakuer/CircleIndicator.).You can custome style and animation. 6 | 7 | ## Screenshot 8 | Screenshot 9 | 10 | ## Demo Download 11 | APK Donwload 12 | 13 | 14 | ## Setting 15 | You can config all feature in the `IndicatorConfiguration` class.It adopt builder design pattern. 16 | 17 | 18 | - `interval(long)` set interval time of scroll in milliseconds, default is `DEFAULT_INTERVAL`. 19 | - `direction(int)` set scroll direction, default is `RIGHT`. 20 | - `isLoop(boolean)` set whether still scroll when scroll to the end page,default is true 21 | - `isAutoScroll(boolean)` whether start scroll while notiyDataChange. 22 | - `scrollDurationFactor(double)` set the factor of scroll duration 23 | - `isStopWhenTouch(boolean)` whether stop scroll while touching, default is true. 24 | - `position` set the position of indicator.More value,you can reference `IndicatorConfiguration.IndicatorPosition` enum 25 | - `viewBinder` set custom view binder to handle page load logic,default provide 26 | BaseViewBinder, which just support image load,and you must provide a imageloader,if 27 | you want to load complex page ,you can provide a custome viewbinder,and imageloader is useless. 28 | - `onPageChangeListener` set click listener to page 29 | - `imageLoader(ImageLoader)` set the loader engine to load image while page sliding.You can use any image loader library you what,there are several imageloader of Glide ,Picasso and UIL,decide how to load image,is absolutely free. 30 | 31 | 32 | 33 | `indicator_type` 34 | the style of Indicator,you can set `indicator_type` attribute in the xml 35 | layout to change the indicator style. 36 | - `indicator_default` CirCleIndicator 37 | - `indicator_anim_circle` AnimCircleIndicator 38 | - `indicator_anim_line` AnimLineIndicator 39 | 40 | 41 | ## Including In Your Project 42 | 43 | `compile 'cn.lightsky.infiniteindicator:library:1.2.2'` 44 | 45 | ## Usage 46 | 47 | ``` xml 48 | 53 | ``` 54 | 55 | ``` java 56 | class AnimIndicatorActivity extends FragmentActivity implements ViewPager.OnPageChangeListener,OnPageClickListener { 57 | private ArrayList pageViews; 58 | private InfiniteIndicator mAnimCircleIndicator; 59 | 60 | @Override 61 | protected void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | setContentView(R.layout.activity_anim_indicator); 64 | 65 | initData(); 66 | mAnimCircleIndicator = (InfiniteIndicator) findViewById(R.id.infinite_anim_circle); 67 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 68 | .imageLoader(new UILoader()) 69 | .isStopWhileTouch(true) 70 | .onPageChangeListener(this) 71 | .onPageClickListener(this) 72 | .direction(LEFT) 73 | .position(IndicatorConfiguration.IndicatorPosition.Center) 74 | .build(); 75 | mAnimCircleIndicator.init(configuration); 76 | mAnimCircleIndicator.notifyDataChange(pageViews); 77 | 78 | } 79 | 80 | private void initData() { 81 | pageViews = new ArrayList<>(); 82 | pageViews.add(new Page("A", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/a.jpg",this)); 83 | pageViews.add(new Page("B", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/b.jpg",this)); 84 | pageViews.add(new Page("C", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/c.jpg",this)); 85 | pageViews.add(new Page("D", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/d.jpg",this)); 86 | 87 | } 88 | 89 | //To avoid memory leak ,you should release the res 90 | @Override 91 | protected void onPause() { 92 | super.onPause(); 93 | mAnimCircleIndicator.stop(); 94 | } 95 | 96 | @Override 97 | protected void onResume() { 98 | super.onResume(); 99 | mAnimCircleIndicator.start(); 100 | } 101 | 102 | @Override 103 | public void onPageSelected(int position) { 104 | //do something 105 | } 106 | 107 | @Override 108 | public void onPageClick(int position, Page page) { 109 | //do something 110 | } 111 | 112 | @Override 113 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 114 | 115 | } 116 | 117 | @Override 118 | public void onPageScrollStateChanged(int state) { 119 | 120 | } 121 | } 122 | ``` 123 | 124 | Thanks 125 | [android-auto-scroll-view-pager](https://github.com/Trinea/android-auto-scroll-view-pager) 126 | [AndroidImageSlider](https://github.com/daimajia/AndroidImageSlider) 127 | [CircleIndicator](https://github.com/ongakuer/CircleIndicator) 128 | [Android-ViewPagerIndicator](https://github.com/JakeWharton/Android-ViewPagerIndicator) 129 | 130 | **About me** 131 | Weibo: [light_sky](http://www.weibo.com/lightSkyStreet) 132 | Blog: [lightskystreet.com](http://www.lightskystreet.com/)     [light_sky](http://blog.csdn.net/xushuaic) 133 | Email: lightsky.cn@gmail.com 134 | 135 | -------------------------------------------------------------------------------- /apk/demo.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/apk/demo.apk -------------------------------------------------------------------------------- /apk/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/apk/demo.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | } 21 | dependencies { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "cn.lightsky.customeviewdemo" 9 | minSdkVersion 8 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | lintOptions { 22 | abortOnError false 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | // compile 'cn.lightsky.infiniteindicator:library:1.1.0' 29 | compile project(':library') 30 | compile files('libs/universal-image-loader-1.9.5.jar') 31 | compile 'com.squareup.picasso:picasso:2.4.0' 32 | compile 'com.github.bumptech.glide:glide:3.7.0' 33 | } 34 | -------------------------------------------------------------------------------- /demo/libs/universal-image-loader-1.9.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/libs/universal-image-loader-1.9.5.jar -------------------------------------------------------------------------------- /demo/manifest-merger-release-report.txt: -------------------------------------------------------------------------------- 1 | -- Merging decision tree log --- 2 | manifest 3 | ADDED from AndroidManifest.xml:2:1 4 | xmlns:android 5 | ADDED from AndroidManifest.xml:2:11 6 | package 7 | ADDED from AndroidManifest.xml:3:5 8 | INJECTED from AndroidManifest.xml:0:0 9 | INJECTED from AndroidManifest.xml:0:0 10 | android:versionName 11 | INJECTED from AndroidManifest.xml:0:0 12 | INJECTED from AndroidManifest.xml:0:0 13 | android:versionCode 14 | INJECTED from AndroidManifest.xml:0:0 15 | INJECTED from AndroidManifest.xml:0:0 16 | application 17 | ADDED from AndroidManifest.xml:5:5 18 | MERGED from InfiniteIndicator:library:unspecified:14:5 19 | MERGED from com.android.support:appcompat-v7:21.0.0:16:5 20 | MERGED from com.android.support:support-v4:21.0.0:16:5 21 | android:label 22 | ADDED from AndroidManifest.xml:8:9 23 | android:allowBackup 24 | ADDED from AndroidManifest.xml:6:9 25 | android:icon 26 | ADDED from AndroidManifest.xml:7:9 27 | android:theme 28 | ADDED from AndroidManifest.xml:9:9 29 | activity#cn.lightsky.infiniteindicator.AnimIndicatorActivity 30 | ADDED from AndroidManifest.xml:10:9 31 | android:label 32 | ADDED from AndroidManifest.xml:12:13 33 | android:name 34 | ADDED from AndroidManifest.xml:11:13 35 | intent-filter#android.intent.action.MAIN+android.intent.category.LAUNCHER 36 | ADDED from AndroidManifest.xml:13:13 37 | action#android.intent.action.MAIN 38 | ADDED from AndroidManifest.xml:14:17 39 | android:name 40 | ADDED from AndroidManifest.xml:14:25 41 | category#android.intent.category.LAUNCHER 42 | ADDED from AndroidManifest.xml:16:17 43 | android:name 44 | ADDED from AndroidManifest.xml:16:27 45 | activity#cn.lightsky.infiniteindicator.DefaultCircleIndicatorActivity 46 | ADDED from AndroidManifest.xml:20:9 47 | android:name 48 | ADDED from AndroidManifest.xml:20:19 49 | uses-sdk 50 | INJECTED from AndroidManifest.xml:0:0 reason: use-sdk injection requested 51 | MERGED from InfiniteIndicator:library:unspecified:7:5 52 | MERGED from com.android.support:appcompat-v7:21.0.0:15:5 53 | MERGED from com.android.support:support-v4:21.0.0:15:5 54 | android:targetSdkVersion 55 | INJECTED from AndroidManifest.xml:0:0 56 | INJECTED from AndroidManifest.xml:0:0 57 | android:minSdkVersion 58 | INJECTED from AndroidManifest.xml:0:0 59 | INJECTED from AndroidManifest.xml:0:0 60 | uses-permission#android.permission.INTERNET 61 | ADDED from InfiniteIndicator:library:unspecified:11:5 62 | android:name 63 | ADDED from InfiniteIndicator:library:unspecified:11:22 64 | uses-permission#android.permission.WRITE_EXTERNAL_STORAGE 65 | ADDED from InfiniteIndicator:library:unspecified:12:5 66 | android:name 67 | ADDED from InfiniteIndicator:library:unspecified:12:22 68 | android:uses-permission#android.permission.READ_EXTERNAL_STORAGE 69 | IMPLIED from AndroidManifest.xml:2:1 reason: requested WRITE_EXTERNAL_STORAGE 70 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 34 | 35 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lightsky/infiniteindicator/AnimIndicatorActivity.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.FragmentActivity; 6 | import android.support.v4.view.ViewPager; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.Toast; 10 | 11 | import java.util.ArrayList; 12 | 13 | import cn.light.sky.infiniteindicatordemo.R; 14 | 15 | import static cn.lightsky.infiniteindicator.IndicatorConfiguration.LEFT; 16 | 17 | 18 | public class AnimIndicatorActivity extends FragmentActivity implements ViewPager.OnPageChangeListener, OnPageClickListener { 19 | private ArrayList pageViews; 20 | private InfiniteIndicator mAnimCircleIndicator; 21 | private InfiniteIndicator mAnimLineIndicator; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_anim_indicator); 27 | 28 | initData(); 29 | testAnimCircleIndicator(); 30 | testAnimLineIndicator(); 31 | } 32 | 33 | private void initData() { 34 | pageViews = new ArrayList<>(); 35 | // pageViews.add(new Page("A ", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/a.jpg", this)); 36 | // pageViews.add(new Page("B ", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/b.jpg", this)); 37 | // pageViews.add(new Page("C ", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/c.jpg", this)); 38 | // pageViews.add(new Page("D ", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/d.jpg", this)); 39 | 40 | pageViews.add(new Page("A", R.drawable.a, this)); 41 | pageViews.add(new Page("B", R.drawable.b, this)); 42 | pageViews.add(new Page("C", R.drawable.c, this)); 43 | pageViews.add(new Page("D", R.drawable.d, this)); 44 | 45 | } 46 | 47 | //To avoid memory leak ,you should release the res 48 | @Override 49 | protected void onPause() { 50 | super.onPause(); 51 | mAnimCircleIndicator.stop(); 52 | mAnimLineIndicator.stop(); 53 | } 54 | 55 | @Override 56 | protected void onResume() { 57 | super.onResume(); 58 | mAnimCircleIndicator.start(); 59 | mAnimLineIndicator.start(); 60 | } 61 | 62 | @Override 63 | public boolean onCreateOptionsMenu(Menu menu) { 64 | getMenuInflater().inflate(R.menu.menu, menu); 65 | return super.onCreateOptionsMenu(menu); 66 | } 67 | 68 | @Override 69 | public boolean onOptionsItemSelected(MenuItem item) { 70 | Intent intent = new Intent(this, DefaultCircleIndicatorActivity.class); 71 | startActivity(intent); 72 | return true; 73 | } 74 | 75 | private void testAnimCircleIndicator() { 76 | mAnimCircleIndicator = (InfiniteIndicator) findViewById(R.id.infinite_anim_circle); 77 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 78 | .imageLoader(new UILoader()) 79 | .isStopWhileTouch(true) 80 | .onPageChangeListener(this) 81 | .onPageClickListener(this) 82 | .direction(LEFT) 83 | .position(IndicatorConfiguration.IndicatorPosition.Center) 84 | .build(); 85 | mAnimCircleIndicator.init(configuration); 86 | mAnimCircleIndicator.notifyDataChange(pageViews); 87 | mAnimCircleIndicator.setCurrentItem(2); 88 | } 89 | 90 | private void testAnimLineIndicator() { 91 | mAnimLineIndicator = (InfiniteIndicator) findViewById(R.id.infinite_anim_line); 92 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 93 | .imageLoader(new PicassoLoader()) 94 | .isAutoScroll(false) 95 | .isStopWhileTouch(true) 96 | .onPageChangeListener(this) 97 | .position(IndicatorConfiguration.IndicatorPosition.Center) 98 | .build(); 99 | mAnimLineIndicator.init(configuration); 100 | mAnimLineIndicator.notifyDataChange(pageViews); 101 | } 102 | 103 | @Override 104 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 105 | 106 | } 107 | 108 | @Override 109 | public void onPageSelected(int position) { 110 | // Toast.makeText(this,"page selected"+position,Toast.LENGTH_SHORT).show(); 111 | } 112 | 113 | @Override 114 | public void onPageScrollStateChanged(int state) { 115 | 116 | } 117 | 118 | @Override 119 | public void onPageClick(int position, Page page) { 120 | Toast.makeText(this, " click page --- " + position, Toast.LENGTH_SHORT).show(); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lightsky/infiniteindicator/DefaultCircleIndicatorActivity.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | import java.util.ArrayList; 9 | 10 | import cn.light.sky.infiniteindicatordemo.R; 11 | import cn.lightsky.infiniteindicator.indicator.CircleIndicator; 12 | 13 | public class DefaultCircleIndicatorActivity extends FragmentActivity { 14 | private InfiniteIndicator mCustoemCircleIndicator; 15 | private ArrayList pageViews; 16 | private InfiniteIndicator mDefaultIndicator; 17 | private ArrayList rules; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_default_circle_indicator); 23 | 24 | pageViews = new ArrayList(); 25 | // pageViews.add(new Page("Page A", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/a.jpg")); 26 | // pageViews.add(new Page("Page B", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/b.jpg")); 27 | // pageViews.add(new Page("Page C", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/c.jpg")); 28 | // pageViews.add(new Page("Page D", "https://raw.githubusercontent.com/lightSky/InfiniteIndicator/master/res/d.jpg")); 29 | 30 | pageViews.add(new Page("A ", R.drawable.a)); 31 | pageViews.add(new Page("B ", R.drawable.b)); 32 | pageViews.add(new Page("C ", R.drawable.c)); 33 | pageViews.add(new Page("D ", R.drawable.d)); 34 | testCircleIndicator(); 35 | testCustomeCircleIndicator(); 36 | } 37 | 38 | @Override 39 | protected void onPause() { 40 | super.onPause(); 41 | mDefaultIndicator.stop(); 42 | mCustoemCircleIndicator.stop(); 43 | } 44 | 45 | @Override 46 | protected void onResume() { 47 | super.onResume(); 48 | mDefaultIndicator.start(); 49 | mCustoemCircleIndicator.start(); 50 | } 51 | 52 | private void testCircleIndicator() { 53 | mDefaultIndicator = (InfiniteIndicator) findViewById(R.id.indicator_default_circle); 54 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 55 | .imageLoader(new UILoader()) 56 | .position(IndicatorConfiguration.IndicatorPosition.Center_Bottom) 57 | .build(); 58 | mDefaultIndicator.init(configuration); 59 | mDefaultIndicator.notifyDataChange(pageViews); 60 | mDefaultIndicator.setCurrentItem(2); 61 | } 62 | 63 | private void testCustomeCircleIndicator() { 64 | mCustoemCircleIndicator = (InfiniteIndicator) findViewById(R.id.indicator_custome_circle); 65 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 66 | .imageLoader(new PicassoLoader()) 67 | .position(IndicatorConfiguration.IndicatorPosition.Center_Bottom) 68 | .build(); 69 | mCustoemCircleIndicator.init(configuration); 70 | mCustoemCircleIndicator.notifyDataChange(pageViews); 71 | 72 | CircleIndicator circleIndicator = ((CircleIndicator) mCustoemCircleIndicator.getPagerIndicator()); 73 | final float density = getResources().getDisplayMetrics().density; 74 | circleIndicator.setBackgroundColor(0xFFCCCCCC); 75 | circleIndicator.setRadius(5 * density); 76 | circleIndicator.setPageColor(0x880000FF); 77 | circleIndicator.setFillColor(0xFF888888); 78 | circleIndicator.setStrokeColor(0xFF000000); 79 | circleIndicator.setStrokeWidth(2 * density); 80 | } 81 | 82 | @Override 83 | public boolean onCreateOptionsMenu(Menu menu) { 84 | getMenuInflater().inflate(R.menu.menu, menu); 85 | return super.onCreateOptionsMenu(menu); 86 | } 87 | 88 | @Override 89 | public boolean onOptionsItemSelected(MenuItem item) { 90 | this.finish(); 91 | return true; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lightsky/infiniteindicator/GlideLoader.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | 4 | import android.content.Context; 5 | import android.widget.ImageView; 6 | 7 | import com.bumptech.glide.Glide; 8 | 9 | /** 10 | * Created by lightsky on 16/1/31. 11 | */ 12 | public class GlideLoader implements ImageLoader { 13 | 14 | public void initLoader(Context context) { 15 | 16 | } 17 | 18 | @Override 19 | public void load(Context context, ImageView targetView, Object res) { 20 | 21 | if (res instanceof String){ 22 | Glide.with(context) 23 | .load((String) res) 24 | .centerCrop() 25 | // .placeholder(R.drawable.a) 26 | .crossFade() 27 | .into(targetView); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lightsky/infiniteindicator/PicassoLoader.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.squareup.picasso.Picasso; 7 | import com.squareup.picasso.RequestCreator; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * Created by lightsky on 16/1/28. 13 | */ 14 | 15 | public class PicassoLoader implements ImageLoader { 16 | 17 | public PicassoLoader getImageLoader(Context context) { 18 | return new PicassoLoader(); 19 | } 20 | 21 | @Override 22 | public void load(Context context,ImageView targetView, Object res) { 23 | if (res == null) { 24 | return; 25 | } 26 | 27 | Picasso picasso = Picasso.with(context); 28 | RequestCreator requestCreator = null; 29 | 30 | if (res instanceof String) { 31 | requestCreator = picasso.load((String) res); 32 | } else if (res instanceof File) { 33 | requestCreator = picasso.load((File) res); 34 | } else if (res instanceof Integer) { 35 | requestCreator = picasso.load((Integer) res); 36 | } 37 | 38 | requestCreator 39 | .fit() 40 | .tag(context) 41 | .into(targetView); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lightsky/infiniteindicator/UILoader.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.widget.ImageView; 6 | 7 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 8 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 9 | 10 | /** 11 | * Created by lightsky on 16/1/28. 12 | */ 13 | 14 | public class UILoader implements ImageLoader { 15 | 16 | private DisplayImageOptions options; 17 | 18 | private boolean isInited; 19 | 20 | public UILoader getImageLoader(Context context) { 21 | UILoader uilLoader = new UILoader(); 22 | initLoader(context); 23 | return uilLoader; 24 | } 25 | 26 | public void initLoader(Context context) { 27 | com.nostra13.universalimageloader.core.ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context)); 28 | 29 | //显示图片的配置 30 | options = new DisplayImageOptions.Builder() 31 | // .showImageOnLoading(R.drawable.placeholder) 32 | // .showImageOnFail(R.drawable.error) 33 | .cacheInMemory(true) 34 | .cacheOnDisk(true) 35 | .bitmapConfig(Bitmap.Config.RGB_565) 36 | .build(); 37 | 38 | isInited = true; 39 | } 40 | 41 | @Override 42 | public void load(Context context,ImageView targetView, Object res) { 43 | 44 | if (!isInited) 45 | initLoader(context); 46 | 47 | if (res == null) { 48 | return; 49 | } 50 | 51 | if (res instanceof String) { 52 | com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage((String) res, targetView,options); 53 | } else { 54 | com.nostra13.universalimageloader.core.ImageLoader.getInstance() 55 | .displayImage("drawable://" + res, targetView,options); 56 | 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /demo/src/main/java/test/AddSliderActivity.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import android.widget.Toast; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import cn.light.sky.infiniteindicatordemo.R; 11 | import cn.lightsky.infiniteindicator.GlideLoader; 12 | import cn.lightsky.infiniteindicator.IndicatorConfiguration; 13 | import cn.lightsky.infiniteindicator.InfiniteIndicator; 14 | import cn.lightsky.infiniteindicator.OnPageClickListener; 15 | import cn.lightsky.infiniteindicator.Page; 16 | 17 | public class AddSliderActivity extends FragmentActivity implements 18 | OnPageClickListener { 19 | private ArrayList pageViews; 20 | private InfiniteIndicator mAnimCircleIndicator; 21 | private List refreshPageViews = new ArrayList(); 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_anim_indicator); 27 | 28 | addSlidersTest(); 29 | } 30 | 31 | private void addSlidersTest() { 32 | mAnimCircleIndicator = (InfiniteIndicator) findViewById(R.id.infinite_anim_circle); 33 | pageViews = new ArrayList(); 34 | pageViews.add(new Page("Page A", R.drawable.a)); 35 | pageViews.add(new Page("Page B", R.drawable.b)); 36 | pageViews.add(new Page("Page C", R.drawable.c)); 37 | pageViews.add(new Page("Page D", R.drawable.d)); 38 | 39 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 40 | .imageLoader(new GlideLoader()) 41 | .position(IndicatorConfiguration.IndicatorPosition.Center) 42 | .build(); 43 | mAnimCircleIndicator.init(configuration); 44 | mAnimCircleIndicator.notifyDataChange(pageViews); 45 | } 46 | 47 | 48 | //In case memory leak ,you should release the res 49 | @Override 50 | protected void onPause() { 51 | super.onPause(); 52 | mAnimCircleIndicator.stop(); 53 | } 54 | 55 | @Override 56 | protected void onResume() { 57 | super.onResume(); 58 | mAnimCircleIndicator.start(); 59 | } 60 | 61 | @Override 62 | public void onPageClick(int position, Page page) { 63 | Toast.makeText(this, "position = "+position, Toast.LENGTH_SHORT).show(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo/src/main/java/test/UpdateSliderActivity.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import android.widget.Toast; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import cn.light.sky.infiniteindicatordemo.R; 11 | import cn.lightsky.infiniteindicator.IndicatorConfiguration; 12 | import cn.lightsky.infiniteindicator.InfiniteIndicator; 13 | import cn.lightsky.infiniteindicator.PicassoLoader; 14 | import cn.lightsky.infiniteindicator.OnPageClickListener; 15 | import cn.lightsky.infiniteindicator.Page; 16 | 17 | 18 | public class UpdateSliderActivity extends FragmentActivity{ 19 | private InfiniteIndicator mAnimCircleIndicator; 20 | 21 | private List refreshPageViews = new ArrayList(); 22 | private ArrayList pageViews = new ArrayList<>(); 23 | private List updateUrls = new ArrayList<>(); 24 | private OnPageClickListener onPageClickListener; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_anim_indicator); 30 | 31 | updateTest(); 32 | } 33 | 34 | private void updateTest() { 35 | final Page a = new Page("update same size list", R.drawable.a); 36 | final Page b = new Page("update smaller size list", R.drawable.b); 37 | final Page c = new Page("update larger size list", R.drawable.c); 38 | final Page d = new Page("Leave a launcher", R.drawable.d); 39 | 40 | final Page e = new Page("", R.drawable.c_yypd); 41 | final Page f = new Page("", R.drawable.b_yypd); 42 | final Page g = new Page("", R.drawable.c_yypd); 43 | final Page h = new Page("", R.drawable.ic_launcher); 44 | 45 | onPageClickListener = new OnPageClickListener() { 46 | 47 | @Override 48 | public void onPageClick(int position, Page page) { 49 | if (position == 0) { 50 | 51 | // a.res = R.drawable.a_yypd; 52 | // b.res = R.drawable.b_yypd; 53 | // c.res = R.drawable.c_yypd; 54 | // d.res = R.drawable.d_yypd; 55 | 56 | a.res = R.drawable.a; 57 | b.res = R.drawable.b; 58 | c.res = R.drawable.c; 59 | d.res = R.drawable.d; 60 | 61 | pageViews.clear(); 62 | pageViews.add(a); 63 | pageViews.add(b); 64 | pageViews.add(c); 65 | pageViews.add(d); 66 | 67 | mAnimCircleIndicator.notifyDataChange(pageViews); 68 | 69 | Toast.makeText(UpdateSliderActivity.this, page.data + "", 70 | Toast.LENGTH_LONG).show(); 71 | } else if (position == 1){ 72 | a.res = R.drawable.a_yypd; 73 | b.res = R.drawable.b_yypd; 74 | 75 | pageViews.clear(); 76 | pageViews.add(a); 77 | pageViews.add(b); 78 | 79 | mAnimCircleIndicator.notifyDataChange(pageViews); 80 | 81 | Toast.makeText(UpdateSliderActivity.this, page.data + "", 82 | Toast.LENGTH_LONG).show(); 83 | } else if (position == 3) { 84 | pageViews.clear(); 85 | 86 | a.res = R.drawable.a; 87 | b.res = R.drawable.b; 88 | c.res = R.drawable.c; 89 | d.res = R.drawable.d; 90 | 91 | pageViews.add(a); 92 | pageViews.add(e); 93 | pageViews.add(b); 94 | pageViews.add(f); 95 | pageViews.add(c); 96 | pageViews.add(g); 97 | pageViews.add(d); 98 | pageViews.add(h); 99 | 100 | mAnimCircleIndicator.notifyDataChange(pageViews); 101 | Toast.makeText(UpdateSliderActivity.this, page.data + "", 102 | Toast.LENGTH_LONG).show(); 103 | } else if (position == 3) { 104 | a.res = R.drawable.ic_launcher; 105 | 106 | pageViews.clear(); 107 | pageViews.add(a); 108 | 109 | mAnimCircleIndicator.notifyDataChange(pageViews); 110 | Toast.makeText(UpdateSliderActivity.this, page.data + "", 111 | Toast.LENGTH_LONG).show(); 112 | } 113 | } 114 | }; 115 | 116 | 117 | pageViews.add(a); 118 | pageViews.add(b); 119 | pageViews.add(c); 120 | pageViews.add(d); 121 | 122 | mAnimCircleIndicator = (InfiniteIndicator) findViewById(R.id.infinite_anim_circle); 123 | IndicatorConfiguration configuration = new IndicatorConfiguration.Builder() 124 | .imageLoader(new PicassoLoader()) 125 | .onPageClickListener(onPageClickListener) 126 | .position(IndicatorConfiguration.IndicatorPosition.Center_Bottom) 127 | .build(); 128 | mAnimCircleIndicator.init(configuration); 129 | mAnimCircleIndicator.notifyDataChange(pageViews); 130 | } 131 | 132 | 133 | //To save resource ,you should release the res 134 | @Override 135 | protected void onPause() { 136 | super.onPause(); 137 | mAnimCircleIndicator.stop(); 138 | } 139 | 140 | @Override 141 | protected void onResume() { 142 | super.onResume(); 143 | mAnimCircleIndicator.start(); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xhdpi/a_yypd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xhdpi/a_yypd.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xhdpi/b_yypd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xhdpi/b_yypd.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xhdpi/c_yypd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xhdpi/c_yypd.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xhdpi/d_yypd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xhdpi/d_yypd.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xxhdpi/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xxhdpi/a.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xxhdpi/b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xxhdpi/b.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xxhdpi/c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xxhdpi/c.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xxhdpi/d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xxhdpi/d.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/drawable/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable/error.jpg -------------------------------------------------------------------------------- /demo/src/main/res/drawable/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/demo/src/main/res/drawable/placeholder.jpg -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_anim_indicator.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_default_circle_indicator.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/layout_custome_page.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/menu.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 450dp 7 | 150dp 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | InfiniteIndicator 5 | Hello world! 6 | switch_style 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 29 09:21:04 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | 6 | version = "1.2.2" 7 | 8 | android { 9 | compileSdkVersion 23 10 | buildToolsVersion "23.0.2" 11 | 12 | defaultConfig { 13 | resourcePrefix "lightsky" 14 | minSdkVersion 8 15 | targetSdkVersion 23 16 | versionCode 122 17 | versionName "1.2.2" 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile 'com.nineoldandroids:library:2.4.0' 29 | compile 'com.android.support:support-v4:23.1.1' 30 | } 31 | 32 | android { 33 | lintOptions { 34 | abortOnError false 35 | } 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------------------------ 40 | 41 | 42 | 43 | def siteUrl = 'https://github.com/lightSky/InfiniteIndicator' // 项目的主页 44 | 45 | def gitUrl = 'https://github.com/lightSky/InfiniteIndicator.git' // Git仓库的url 46 | 47 | group = "cn.lightsky.infiniteindicator" // Maven Group ID for the artifact,一般填你唯一的包名 48 | 49 | install { 50 | 51 | repositories.mavenInstaller { 52 | 53 | // This generates POM.xml with proper parameters 54 | 55 | pom { 56 | 57 | project { 58 | 59 | packaging 'aar' 60 | 61 | // Add your description here 62 | 63 | name 'This lib can be used for viewpager infinite loop with indicator easily.' //项目描述 64 | 65 | url siteUrl 66 | 67 | // Set your license 68 | 69 | licenses { 70 | 71 | license { 72 | 73 | name 'The Apache Software License, Version 2.0' 74 | 75 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 76 | 77 | } 78 | 79 | } 80 | 81 | developers { 82 | 83 | developer { 84 | 85 | id 'lightsky' //填写的一些基本信息 86 | 87 | name 'lightsky' 88 | 89 | email 'lightsky.cn@gmail.com' 90 | 91 | } 92 | 93 | } 94 | 95 | scm { 96 | 97 | connection gitUrl 98 | 99 | developerConnection gitUrl 100 | 101 | url siteUrl 102 | 103 | } 104 | 105 | } 106 | 107 | } 108 | 109 | } 110 | 111 | } 112 | 113 | task sourcesJar(type: Jar) { 114 | 115 | from android.sourceSets.main.java.srcDirs 116 | 117 | classifier = 'sources' 118 | 119 | } 120 | 121 | task javadoc(type: Javadoc) { 122 | 123 | source = android.sourceSets.main.java.srcDirs 124 | 125 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 126 | 127 | } 128 | 129 | task javadocJar(type: Jar, dependsOn: javadoc) { 130 | 131 | classifier = 'javadoc' 132 | 133 | from javadoc.destinationDir 134 | 135 | } 136 | 137 | artifacts { 138 | 139 | // archives javadocJar 140 | 141 | archives sourcesJar 142 | 143 | } 144 | 145 | Properties properties = new Properties() 146 | 147 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 148 | 149 | bintray { 150 | 151 | user = properties.getProperty("bintray.user") 152 | 153 | key = properties.getProperty("bintray.apikey") 154 | 155 | configurations = ['archives'] 156 | 157 | pkg { 158 | 159 | repo = "maven" 160 | 161 | name = "InfiniteIndicator" //发布到JCenter上的项目名字 162 | 163 | websiteUrl = siteUrl 164 | 165 | vcsUrl = gitUrl 166 | 167 | licenses = ["Apache-2.0"] 168 | 169 | publish = true 170 | 171 | } 172 | 173 | } -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:/Android/SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/DurationScroller.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | import android.view.animation.Interpolator; 5 | import android.widget.Scroller; 6 | 7 | /** 8 | * CustomDurationScroller 9 | */ 10 | public class DurationScroller extends Scroller { 11 | 12 | private double scrollFactor = 1.2; 13 | 14 | public DurationScroller(Context context) { 15 | super(context); 16 | } 17 | 18 | public DurationScroller(Context context, Interpolator interpolator) { 19 | super(context, interpolator); 20 | } 21 | 22 | /** 23 | * Set the factor by which the duration will change 24 | */ 25 | public void setScrollDurationFactor(double scrollFactor) { 26 | this.scrollFactor = scrollFactor; 27 | } 28 | 29 | @Override 30 | public void startScroll(int startX, int startY, int dx, int dy, int duration) { 31 | super.startScroll(startX, startY, dx, dy, (int)(duration * scrollFactor)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/ImageLoader.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | /** 7 | * Created by lightsky on 16/1/28. 8 | */ 9 | public interface ImageLoader { 10 | 11 | void load(Context context,ImageView targetView, Object res); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/IndicatorConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.support.v4.view.ViewPager; 4 | import android.view.View; 5 | 6 | import cn.lightsky.infiniteindicator.recycle.BaseViewBinder; 7 | import cn.lightsky.infiniteindicator.recycle.ViewBinder; 8 | 9 | 10 | /** 11 | * Created by lightsky on 2016/10/29. 12 | */ 13 | 14 | public class IndicatorConfiguration { 15 | 16 | public static final int DEFAULT_INTERVAL = 2500; 17 | public static final double DEFAULT_SCROLL_FACTOR = 1; 18 | public static final int LEFT = 0; 19 | public static final int RIGHT = 1; 20 | 21 | public enum IndicatorPosition { 22 | Center("Center_Bottom", R.id.default_center_indicator), 23 | Center_Bottom("Center_Bottom", R.id.default_center_bottom_indicator), 24 | Right_Bottom("Right_Bottom", R.id.default_bottom_right_indicator), 25 | Left_Bottom("Left_Bottom", R.id.default_bottom_left_indicator), 26 | Center_Top("Center_Top", R.id.default_center_top_indicator), 27 | Right_Top("Right_Top", R.id.default_center_top_right_indicator), 28 | Left_Top("Left_Top", R.id.default_center_top_left_indicator); 29 | 30 | private final String name; 31 | private final int id; 32 | 33 | private IndicatorPosition(String name, int id) { 34 | this.name = name; 35 | this.id = id; 36 | } 37 | 38 | public String toString() { 39 | return name; 40 | } 41 | 42 | public int getResourceId() { 43 | return id; 44 | } 45 | } 46 | 47 | private View indicator; 48 | private double scrollFactor; 49 | private boolean isAutoScroll; 50 | private boolean isLoop; 51 | private boolean isDrawIndicator; 52 | private boolean isStopScrollWhenTouch; 53 | private int direction; 54 | private long interval; 55 | private final ViewBinder viewBinder; 56 | private final ImageLoader imageLoader; 57 | private final ViewPager.OnPageChangeListener onPageChangeListener; 58 | private final OnPageClickListener mOnPageClickListener; 59 | private final IndicatorPosition presentIndicator; 60 | 61 | private IndicatorConfiguration(Builder builder) { 62 | imageLoader = builder.imageLoader; 63 | isLoop = builder.isLoop; 64 | interval = builder.interval; 65 | direction = builder.direction; 66 | isDrawIndicator = builder.isDrawIndicator; 67 | isAutoScroll = builder.isAutoScroll; 68 | scrollFactor = builder.scrollFactor; 69 | presentIndicator = builder.indicatorPosition; 70 | indicator = builder.indicator; 71 | viewBinder = builder.viewBinder; 72 | mOnPageClickListener = builder.onPageClickListener; 73 | isStopScrollWhenTouch = builder.isStopWhileTouch; 74 | onPageChangeListener = builder.onPageChangeListener; 75 | } 76 | 77 | public ImageLoader getImageLoader() { 78 | if (imageLoader == null) { 79 | throw new RuntimeException("You should set ImageLoader first"); 80 | } 81 | return imageLoader; 82 | } 83 | 84 | public int getDirection() { 85 | return direction; 86 | } 87 | 88 | public boolean isLoop() { 89 | return isLoop; 90 | } 91 | 92 | public boolean isDrawIndicator() { 93 | return isDrawIndicator; 94 | } 95 | 96 | public long getInterval() { 97 | return interval; 98 | } 99 | 100 | public boolean isStopWhenTouch() { 101 | return isStopScrollWhenTouch; 102 | } 103 | 104 | public boolean isAutoScroll() { 105 | return isAutoScroll; 106 | } 107 | 108 | public double getScrollFactor() { 109 | return scrollFactor; 110 | } 111 | 112 | public IndicatorPosition getPageIndicator() { 113 | return presentIndicator; 114 | } 115 | 116 | public ViewPager.OnPageChangeListener getOnPageChangeListener() { 117 | return onPageChangeListener; 118 | } 119 | 120 | public OnPageClickListener getOnPageClickListener() { 121 | return mOnPageClickListener; 122 | } 123 | 124 | public ViewBinder getViewBinder() { 125 | return viewBinder; 126 | } 127 | 128 | public static class Builder{ 129 | private ImageLoader imageLoader; 130 | private boolean isLoop = true; 131 | private boolean isAutoScroll = true; 132 | private boolean isStopWhileTouch = true; 133 | private boolean isDrawIndicator = true; 134 | private View indicator; 135 | private ViewBinder viewBinder = new BaseViewBinder(); 136 | private OnPageClickListener onPageClickListener; 137 | private ViewPager.OnPageChangeListener onPageChangeListener; 138 | private int direction = RIGHT; 139 | private long interval = DEFAULT_INTERVAL; 140 | private double scrollFactor = DEFAULT_SCROLL_FACTOR; 141 | private IndicatorPosition indicatorPosition = IndicatorPosition.Center_Bottom; 142 | 143 | public Builder imageLoader(ImageLoader imageLoader) { 144 | this.imageLoader = imageLoader; 145 | return this; 146 | } 147 | 148 | /** 149 | * set whether is loop when reaching the last or first item, default is true 150 | * 151 | * @param isLoop 152 | */ 153 | public Builder isLoop(boolean isLoop) { 154 | this.isLoop = isLoop; 155 | return this; 156 | } 157 | 158 | public Builder isDrawIndicator(boolean isDrawIndicator) { 159 | this.isDrawIndicator = isDrawIndicator; 160 | return this; 161 | } 162 | 163 | /** 164 | * auto scroll direction, default is {@link #RIGHT} * 165 | */ 166 | public Builder direction(int direction) { 167 | this.direction = direction; 168 | return this; 169 | } 170 | 171 | /** 172 | * auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL} * 173 | */ 174 | public Builder internal(long interval) { 175 | this.interval = interval; 176 | return this; 177 | } 178 | 179 | /** 180 | * whether stop auto scroll while touching, default is true * 181 | */ 182 | public Builder isStopWhileTouch(boolean isStopScrollWhenTouch) { 183 | this.isStopWhileTouch = isStopScrollWhenTouch; 184 | return this; 185 | } 186 | 187 | /** 188 | * whether auto scroll when initialize done * 189 | */ 190 | public Builder isAutoScroll(boolean isAutoScroll) { 191 | this.isAutoScroll = isAutoScroll; 192 | return this; 193 | } 194 | 195 | /** 196 | * set the factor by which the duration of sliding animation will change 197 | */ 198 | public Builder scrollDurationFactor(double scrollFactor) { 199 | this.scrollFactor = scrollFactor; 200 | return this; 201 | } 202 | 203 | public Builder position(IndicatorPosition indicatorPosition) { 204 | this.indicatorPosition = indicatorPosition; 205 | return this; 206 | } 207 | 208 | /** 209 | * set page click listener for responsing 210 | * @param onPageClickListener 211 | * @return 212 | */ 213 | public Builder onPageClickListener(OnPageClickListener onPageClickListener) { 214 | this.onPageClickListener = onPageClickListener; 215 | return this; 216 | } 217 | 218 | /** 219 | * set custome view binder 220 | * @param viewBinder 221 | * @return 222 | */ 223 | public Builder viewBinder(ViewBinder viewBinder) { 224 | this.viewBinder = viewBinder; 225 | return this; 226 | } 227 | 228 | /** 229 | * set the indicator for your page,default will not draw,or you can use 230 | * default indicator ,and you should set isDrawIndicator true 231 | * @param indicator 232 | * @return 233 | */ 234 | private Builder indicator(View indicator) { 235 | this.indicator = indicator; 236 | return this; 237 | } 238 | 239 | /** 240 | * set page change listener 241 | * @param onPageChangeListener 242 | * @return 243 | */ 244 | public Builder onPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) { 245 | this.onPageChangeListener = onPageChangeListener; 246 | return this; 247 | } 248 | 249 | public IndicatorConfiguration build() { 250 | return new IndicatorConfiguration(this); 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/InfiniteIndicator.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.support.v4.view.MotionEventCompat; 8 | import android.support.v4.view.PagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.util.AttributeSet; 11 | import android.view.LayoutInflater; 12 | import android.view.MotionEvent; 13 | import android.view.animation.Interpolator; 14 | import android.widget.RelativeLayout; 15 | 16 | import java.lang.ref.WeakReference; 17 | import java.lang.reflect.Field; 18 | import java.util.List; 19 | 20 | import cn.lightsky.infiniteindicator.indicator.PageIndicator; 21 | import cn.lightsky.infiniteindicator.recycle.RecyclingPagerAdapter; 22 | import cn.lightsky.infiniteindicator.recycle.RecyleAdapter; 23 | 24 | 25 | /** 26 | * Created by lightSky on 2014/12/22. 27 | */ 28 | public class InfiniteIndicator extends RelativeLayout implements 29 | RecyclingPagerAdapter.DataChangeListener, ViewPager.OnPageChangeListener { 30 | 31 | private Context mContext; 32 | private ViewPager mViewPager; 33 | private PageIndicator mIndicator; 34 | private RecyleAdapter mRecyleAdapter; 35 | private DurationScroller scroller; 36 | private final ScrollHandler handler; 37 | private boolean isScrolling; 38 | private boolean isStopByTouch; 39 | private float downX = 0f; 40 | private float touchX = 0f; 41 | public static final int MSG_SCROLL = 1000; 42 | public static final int PAGE_COUNT_FACTOR = 100; 43 | private IndicatorConfiguration configuration; 44 | 45 | public InfiniteIndicator(Context context) { 46 | this(context, null); 47 | } 48 | 49 | public InfiniteIndicator(Context context, AttributeSet attrs) { 50 | this(context, attrs, 0); 51 | } 52 | 53 | public InfiniteIndicator(Context context, AttributeSet attrs, int defStyleAttr) { 54 | super(context, attrs, defStyleAttr); 55 | mContext = context; 56 | 57 | final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.InfiniteIndicator, 0, 0); 58 | int indicatorType = attributes.getInt(R.styleable.InfiniteIndicator_indicator_type, 0); 59 | 60 | if (indicatorType == 0) { 61 | LayoutInflater.from(context).inflate(R.layout.layout_default_indicator, this, true); 62 | } else if (indicatorType == 1) { 63 | LayoutInflater.from(context).inflate(R.layout.layout_anim_circle_indicator, this, true); 64 | } else { 65 | LayoutInflater.from(context).inflate(R.layout.layout_anim_line_indicator, this, true); 66 | } 67 | 68 | attributes.recycle(); 69 | mViewPager = (ViewPager) findViewById(R.id.view_pager); 70 | handler = new ScrollHandler(this); 71 | } 72 | 73 | public void init(IndicatorConfiguration configuration) { 74 | this.configuration = configuration; 75 | mRecyleAdapter = new RecyleAdapter(mContext 76 | ,configuration.getViewBinder() 77 | ,configuration.getOnPageClickListener()); 78 | mRecyleAdapter.setDataChangeListener(this); 79 | mViewPager.setAdapter(mRecyleAdapter); 80 | mViewPager.addOnPageChangeListener(this); 81 | mRecyleAdapter.setIsLoop(configuration.isLoop()); 82 | mRecyleAdapter.setImageLoader(configuration.getImageLoader()); 83 | setScroller(); 84 | initIndicator(); 85 | } 86 | 87 | public void notifyDataChange(List pages) { 88 | if (pages != null && !pages.isEmpty()) { 89 | mRecyleAdapter.setPages(pages); 90 | } 91 | 92 | scrollToIndex(0); 93 | if (configuration.isAutoScroll()) { 94 | start(); 95 | } 96 | 97 | } 98 | 99 | public void initIndicator() { 100 | if (configuration.isDrawIndicator()) { 101 | mIndicator = (PageIndicator) findViewById(configuration.getPageIndicator().getResourceId()); 102 | mIndicator.setViewPager(mViewPager); 103 | } 104 | } 105 | 106 | /** 107 | * return PageIndicator for develop to custome indicator 108 | * 109 | * @return 110 | */ 111 | public PageIndicator getPagerIndicator() { 112 | return mIndicator; 113 | } 114 | 115 | /** 116 | * scroll to given index page by loop and offset 117 | * page to display 118 | */ 119 | private void scrollToIndex(int offset) { 120 | if (configuration.isLoop() && getRealCount() > 1) { 121 | mViewPager.setCurrentItem(getIndex(offset)); 122 | } else { 123 | mViewPager.setCurrentItem(offset); 124 | } 125 | if (mIndicator != null) { 126 | mIndicator.setCurrentItem(offset); 127 | } 128 | } 129 | 130 | /** 131 | * get the item index of viewpager by given offset 132 | * 133 | * @param offset the real index of pages 134 | * @return 135 | */ 136 | private int getIndex(int offset) { 137 | return getRealCount() * PAGE_COUNT_FACTOR / 2 - 138 | (getRealCount() * PAGE_COUNT_FACTOR / 2 % getRealCount 139 | ()) + offset; 140 | } 141 | 142 | /** 143 | * start auto scroll 144 | * 145 | * @param delayTimeInMills first scroll delay time,default is 2500ms 146 | */ 147 | public void start() { 148 | start(configuration.getInterval()); 149 | } 150 | 151 | public void start(long delayTimeInMills) { 152 | if (configuration == null) { 153 | throw new RuntimeException("You should init a configuration first"); 154 | } 155 | 156 | if (getRealCount() > 1 157 | && isScrolling == false 158 | && configuration.isLoop()) { 159 | isScrolling = true; 160 | sendScrollMessage(delayTimeInMills); 161 | } 162 | } 163 | 164 | public void stop() { 165 | isScrolling = false; 166 | handler.removeMessages(MSG_SCROLL); 167 | } 168 | 169 | 170 | private void sendScrollMessage() { 171 | sendScrollMessage(configuration.getInterval()); 172 | } 173 | 174 | /** 175 | * remove messages before, keeps one message is running at most 176 | **/ 177 | private void sendScrollMessage(long delayTimeInMills) { 178 | handler.removeMessages(MSG_SCROLL); 179 | handler.sendEmptyMessageDelayed(MSG_SCROLL, delayTimeInMills); 180 | } 181 | 182 | /** 183 | * modify duration of ViewPager 184 | */ 185 | private void setScroller() { 186 | try { 187 | Field scrollerField = ViewPager.class.getDeclaredField("mScroller"); 188 | scrollerField.setAccessible(true); 189 | Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator"); 190 | interpolatorField.setAccessible(true); 191 | scroller = new DurationScroller(getContext(), (Interpolator) interpolatorField.get(null)); 192 | scrollerField.set(mViewPager, scroller); 193 | scroller.setScrollDurationFactor(configuration.getScrollFactor()); 194 | } catch (Exception e) { 195 | e.printStackTrace(); 196 | } 197 | } 198 | 199 | private int getRealCount() { 200 | return mRecyleAdapter.getRealCount(); 201 | } 202 | 203 | private int getRealPosition(int position) { 204 | return mRecyleAdapter.getRealPosition(position); 205 | } 206 | 207 | /** 208 | * scroll only once 209 | */ 210 | public void scrollOnce() { 211 | PagerAdapter adapter = mViewPager.getAdapter(); 212 | int currentItem = mViewPager.getCurrentItem(); 213 | 214 | int totalCount; 215 | if (adapter == null || (totalCount = adapter.getCount()) <= 1) { 216 | return; 217 | } 218 | 219 | int nextItem = (configuration.getDirection() == IndicatorConfiguration.LEFT) 220 | ? --currentItem 221 | : ++currentItem; 222 | 223 | if (nextItem < 0) { 224 | if (configuration.isLoop()) { 225 | mViewPager.setCurrentItem(totalCount - 1); 226 | } 227 | } else if (nextItem == totalCount) { 228 | if (configuration.isLoop()) { 229 | mViewPager.setCurrentItem(0); 230 | } 231 | } else { 232 | mViewPager.setCurrentItem(nextItem, true); 233 | } 234 | } 235 | 236 | @Override 237 | public boolean dispatchTouchEvent(MotionEvent ev) { 238 | if (configuration == null) { 239 | return super.dispatchTouchEvent(ev); 240 | } 241 | 242 | int action = MotionEventCompat.getActionMasked(ev); 243 | if (configuration.isStopWhenTouch()) { 244 | if ((action == MotionEvent.ACTION_DOWN) && isScrolling) { 245 | isStopByTouch = true; 246 | stop(); 247 | } else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) { 248 | start(); 249 | } 250 | } 251 | 252 | return super.dispatchTouchEvent(ev); 253 | } 254 | 255 | @Override 256 | public void notifyDataChange() { 257 | if (mIndicator != null) { 258 | mIndicator.notifyDataSetChanged(); 259 | } 260 | } 261 | 262 | public static class ScrollHandler extends Handler { 263 | 264 | public WeakReference mWeakReference; 265 | 266 | public ScrollHandler(InfiniteIndicator infiniteIndicatorLayout) { 267 | mWeakReference = new WeakReference(infiniteIndicatorLayout); 268 | } 269 | 270 | @Override 271 | public void handleMessage(Message msg) { 272 | super.handleMessage(msg); 273 | 274 | InfiniteIndicator infiniteIndicatorLayout = mWeakReference.get(); 275 | if (infiniteIndicatorLayout != null) { 276 | switch (msg.what) { 277 | case MSG_SCROLL: 278 | infiniteIndicatorLayout.scrollOnce(); 279 | infiniteIndicatorLayout.sendScrollMessage(); 280 | default: 281 | break; 282 | } 283 | } 284 | } 285 | } 286 | 287 | @Override 288 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 289 | if (mIndicator != null) { 290 | mIndicator.onPageScrolled(getRealPosition(position), positionOffset, positionOffsetPixels); 291 | } 292 | 293 | if (configuration.getOnPageChangeListener() != null) { 294 | configuration.getOnPageChangeListener().onPageScrolled( 295 | getRealPosition(position), positionOffset, positionOffsetPixels); 296 | } 297 | } 298 | 299 | @Override 300 | public void onPageSelected(int position) { 301 | if (mIndicator != null) { 302 | mIndicator.onPageSelected(getRealPosition(position)); 303 | } 304 | if (configuration.getOnPageChangeListener() != null) { 305 | configuration.getOnPageChangeListener().onPageSelected(getRealPosition(position)); 306 | } 307 | } 308 | 309 | @Override 310 | public void onPageScrollStateChanged(int state) { 311 | if (mIndicator != null) { 312 | mIndicator.onPageScrollStateChanged(state); 313 | } 314 | if (configuration.getOnPageChangeListener() != null) { 315 | configuration.getOnPageChangeListener().onPageScrollStateChanged(state); 316 | } 317 | } 318 | 319 | public void setCurrentItem(int index) { 320 | if (index > getRealCount() - 1) { 321 | throw new IndexOutOfBoundsException("index is " + index + "current " + 322 | "list size is " + getRealCount()); 323 | } 324 | scrollToIndex(index); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/OnPageClickListener.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import cn.lightsky.infiniteindicator.Page; 4 | 5 | /** 6 | * Created by lightsky on 16/1/31. 7 | */ 8 | public interface OnPageClickListener { 9 | 10 | void onPageClick(int position, Page page); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/Page.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | /** 4 | * Created by xushuai on 2014/12/25. 5 | */ 6 | public class Page { 7 | public String data = ""; 8 | public Object res; 9 | 10 | public Page(Object res) { 11 | this.res = res; 12 | } 13 | 14 | public Page(String data, Object res) { 15 | this.data = data; 16 | this.res = res; 17 | } 18 | 19 | public Page(String data, Object res, OnPageClickListener listener) { 20 | this.data = data; 21 | this.res = res; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/ViewUtils.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by lightsky on 2016/11/1. 7 | */ 8 | 9 | public class ViewUtils { 10 | /** 11 | * @param context 12 | * @param dipValue 13 | * @return 14 | */ 15 | public static int dip2px(Context context, float dipValue) { 16 | if (context == null) { 17 | return (int) dipValue; 18 | } 19 | final float scale = context.getResources().getDisplayMetrics().density; 20 | return (int) (dipValue * scale + 0.5f); 21 | } 22 | 23 | public static int px2dip(Context context, float pxValue) { 24 | if (context == null) { 25 | return (int) pxValue; 26 | } 27 | final float scale = context.getResources().getDisplayMetrics().density; 28 | return (int) (pxValue / scale + 0.5f); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/indicator/AnimIndicator.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.indicator; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.provider.Settings; 6 | import android.support.v4.view.ViewPager; 7 | import android.util.AttributeSet; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import android.view.animation.Interpolator; 11 | import android.view.animation.LinearInterpolator; 12 | import android.widget.LinearLayout; 13 | 14 | import com.nineoldandroids.animation.AnimatorInflater; 15 | import com.nineoldandroids.animation.AnimatorSet; 16 | 17 | import cn.lightsky.infiniteindicator.R; 18 | import cn.lightsky.infiniteindicator.recycle.RecyleAdapter; 19 | 20 | public class AnimIndicator extends LinearLayout implements PageIndicator { 21 | 22 | private final static int DEFAULT_INDICATOR_WIDTH = 5; 23 | private int mIndicatorMargin; 24 | private int mIndicatorWidth; 25 | private int mIndicatorHeight; 26 | private int mCurrentPage = 0; 27 | private RecyleAdapter mRecyleAdapter; 28 | private int mAnimatorResId = R.animator.scale_with_alpha; 29 | private int mIndicatorBackground = R.drawable.white_radius; 30 | private AnimatorSet mAnimationOut; 31 | private AnimatorSet mAnimationIn; 32 | 33 | public AnimIndicator(Context context) { 34 | super(context); 35 | init(context, null); 36 | } 37 | 38 | public AnimIndicator(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | init(context, attrs); 41 | } 42 | 43 | private void init(Context context, AttributeSet attrs) { 44 | setOrientation(LinearLayout.HORIZONTAL); 45 | setGravity(Gravity.CENTER); 46 | handleTypedArray(context, attrs); 47 | mAnimationOut = (AnimatorSet) AnimatorInflater.loadAnimator(context, mAnimatorResId); 48 | mAnimationOut.setInterpolator(new LinearInterpolator()); 49 | mAnimationIn = (AnimatorSet) AnimatorInflater.loadAnimator(context, mAnimatorResId); 50 | mAnimationIn.setInterpolator(new ReverseInterpolator()); 51 | } 52 | 53 | private void handleTypedArray(Context context, AttributeSet attrs) { 54 | if (attrs != null) { 55 | TypedArray typedArray = 56 | context.obtainStyledAttributes(attrs, R.styleable.AnimIndicator); 57 | 58 | mIndicatorWidth = 59 | typedArray.getDimensionPixelSize(R.styleable.AnimIndicator_ci_width, -1); 60 | 61 | mIndicatorHeight = 62 | typedArray.getDimensionPixelSize(R.styleable.AnimIndicator_ci_height, -1); 63 | 64 | mIndicatorMargin = 65 | typedArray.getDimensionPixelSize(R.styleable.AnimIndicator_ci_margin, -1); 66 | 67 | mAnimatorResId = typedArray.getResourceId(R.styleable.AnimIndicator_ci_animator, 68 | R.animator.scale_with_alpha); 69 | 70 | mIndicatorBackground = typedArray.getResourceId(R.styleable.AnimIndicator_ci_drawable, 71 | R.drawable.white_radius); 72 | 73 | typedArray.recycle(); 74 | } 75 | 76 | mIndicatorWidth = 77 | (mIndicatorWidth == -1) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorWidth; 78 | 79 | mIndicatorHeight = 80 | (mIndicatorHeight == -1) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorHeight; 81 | 82 | mIndicatorMargin = 83 | (mIndicatorMargin == -1) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorMargin; 84 | } 85 | 86 | public void setViewPager(ViewPager viewPager) { 87 | mRecyleAdapter = (RecyleAdapter) viewPager.getAdapter(); 88 | invalidIndicators(); 89 | } 90 | 91 | @Override 92 | public void setCurrentItem(int item) { 93 | mCurrentPage = item; 94 | invalidIndicators(); 95 | } 96 | 97 | @Override 98 | public void notifyDataSetChanged() { 99 | mCurrentPage = 0; 100 | invalidIndicators(); 101 | } 102 | 103 | @Override 104 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 105 | 106 | } 107 | 108 | @Override 109 | public void onPageSelected(int position) { 110 | if (getChildAt(mCurrentPage) == null) 111 | return; 112 | 113 | mAnimationIn.setTarget(getChildAt(mCurrentPage)); 114 | mAnimationIn.start(); 115 | mAnimationOut.setTarget(getChildAt(position)); 116 | mAnimationOut.start(); 117 | 118 | mCurrentPage = position; 119 | } 120 | 121 | @Override 122 | public void onPageScrollStateChanged(int state) { 123 | 124 | } 125 | 126 | private void invalidIndicators() { 127 | removeAllViews(); 128 | 129 | if (mRecyleAdapter == null) { 130 | return; 131 | } 132 | 133 | int count = mRecyleAdapter.getRealCount(); 134 | if (count < 2) { 135 | return; 136 | } 137 | 138 | for (int i = 0; i < count; i++) { 139 | View indicator = new View(getContext()); 140 | indicator.setBackgroundResource(mIndicatorBackground); 141 | addView(indicator, mIndicatorWidth, mIndicatorHeight); 142 | LayoutParams lp = (LayoutParams) indicator.getLayoutParams(); 143 | lp.leftMargin = mIndicatorMargin; 144 | lp.rightMargin = mIndicatorMargin; 145 | indicator.setLayoutParams(lp); 146 | 147 | mAnimationOut.setTarget(indicator); 148 | mAnimationOut.start(); 149 | } 150 | 151 | mAnimationOut.setTarget(getChildAt(mCurrentPage)); 152 | mAnimationOut.start(); 153 | } 154 | 155 | private class ReverseInterpolator implements Interpolator { 156 | @Override 157 | public float getInterpolation(float value) { 158 | return Math.abs(1.0f - value); 159 | } 160 | } 161 | 162 | public int dip2px(float dpValue) { 163 | final float scale = getResources().getDisplayMetrics().density; 164 | return (int) (dpValue * scale + 0.5f); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/indicator/CircleIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Patrik Akerfeldt 3 | * Copyright (C) 2011 Jake Wharton 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.lightsky.infiniteindicator.indicator; 18 | 19 | import android.content.Context; 20 | import android.content.res.Resources; 21 | import android.content.res.TypedArray; 22 | import android.graphics.Canvas; 23 | import android.graphics.Paint; 24 | import android.graphics.Paint.Style; 25 | import android.graphics.drawable.Drawable; 26 | import android.support.v4.view.MotionEventCompat; 27 | import android.support.v4.view.ViewConfigurationCompat; 28 | import android.support.v4.view.ViewPager; 29 | import android.util.AttributeSet; 30 | import android.view.MotionEvent; 31 | import android.view.View; 32 | import android.view.ViewConfiguration; 33 | 34 | import cn.lightsky.infiniteindicator.R; 35 | import cn.lightsky.infiniteindicator.recycle.RecyleAdapter; 36 | 37 | import static android.graphics.Paint.ANTI_ALIAS_FLAG; 38 | import static android.widget.LinearLayout.HORIZONTAL; 39 | import static android.widget.LinearLayout.VERTICAL; 40 | 41 | /** 42 | * Draws circles (one for each view). The current view position is filled and 43 | * others are only stroked. 44 | * 45 | * Thanks to : https://github.com/JakeWharton/Android-ViewPagerIndicator 46 | */ 47 | public class CircleIndicator extends View implements PageIndicator { 48 | private static final int INVALID_POINTER = -1; 49 | 50 | private float mRadius; 51 | private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); 52 | private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); 53 | private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); 54 | private ViewPager mViewPager; 55 | private int mCurrentPage; 56 | private int mSnapPage; 57 | private float mPageOffset; 58 | private int mScrollState; 59 | private int mOrientation; 60 | private boolean mCentered; 61 | private boolean mSnap = true; 62 | 63 | private int mTouchSlop; 64 | private float mLastMotionX = -1; 65 | private int mActivePointerId = INVALID_POINTER; 66 | private boolean mIsDragging; 67 | private int mRealCount; 68 | private RecyleAdapter mRecyleAdapter; 69 | 70 | 71 | public CircleIndicator(Context context) { 72 | this(context, null); 73 | } 74 | 75 | public CircleIndicator(Context context, AttributeSet attrs) { 76 | this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); 77 | } 78 | 79 | public CircleIndicator(Context context, AttributeSet attrs, int defStyle) { 80 | super(context, attrs, defStyle); 81 | if (isInEditMode()) return; 82 | 83 | //Load defaults from resources 84 | final Resources res = getResources(); 85 | final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); 86 | final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); 87 | final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); 88 | final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); 89 | final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); 90 | final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); 91 | final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); 92 | final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); 93 | 94 | //Retrieve styles attributes 95 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); 96 | 97 | mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); 98 | mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); 99 | mPaintPageFill.setStyle(Style.FILL); 100 | mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); 101 | mPaintStroke.setStyle(Style.STROKE); 102 | mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); 103 | mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); 104 | mPaintFill.setStyle(Style.FILL); 105 | mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); 106 | mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); 107 | 108 | Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); 109 | if (background != null) { 110 | setBackgroundDrawable(background); 111 | } 112 | 113 | a.recycle(); 114 | 115 | final ViewConfiguration configuration = ViewConfiguration.get(context); 116 | mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); 117 | } 118 | 119 | public void setRealViewCount(int viewCount) { 120 | mRealCount = viewCount; 121 | } 122 | 123 | public void setCentered(boolean centered) { 124 | mCentered = centered; 125 | invalidate(); 126 | } 127 | 128 | public boolean isCentered() { 129 | return mCentered; 130 | } 131 | 132 | public void setPageColor(int pageColor) { 133 | mPaintPageFill.setColor(pageColor); 134 | invalidate(); 135 | } 136 | 137 | public int getPageColor() { 138 | return mPaintPageFill.getColor(); 139 | } 140 | 141 | public void setFillColor(int fillColor) { 142 | mPaintFill.setColor(fillColor); 143 | invalidate(); 144 | } 145 | 146 | public int getFillColor() { 147 | return mPaintFill.getColor(); 148 | } 149 | 150 | public void setOrientation(int orientation) { 151 | switch (orientation) { 152 | case HORIZONTAL: 153 | case VERTICAL: 154 | mOrientation = orientation; 155 | requestLayout(); 156 | break; 157 | 158 | default: 159 | throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); 160 | } 161 | } 162 | 163 | public int getOrientation() { 164 | return mOrientation; 165 | } 166 | 167 | public void setStrokeColor(int strokeColor) { 168 | mPaintStroke.setColor(strokeColor); 169 | invalidate(); 170 | } 171 | 172 | public int getStrokeColor() { 173 | return mPaintStroke.getColor(); 174 | } 175 | 176 | public void setStrokeWidth(float strokeWidth) { 177 | mPaintStroke.setStrokeWidth(strokeWidth); 178 | invalidate(); 179 | } 180 | 181 | public float getStrokeWidth() { 182 | return mPaintStroke.getStrokeWidth(); 183 | } 184 | 185 | public void setRadius(float radius) { 186 | mRadius = radius; 187 | invalidate(); 188 | } 189 | 190 | public float getRadius() { 191 | return mRadius; 192 | } 193 | 194 | public void setSnap(boolean snap) { 195 | mSnap = snap; 196 | invalidate(); 197 | } 198 | 199 | public boolean isSnap() { 200 | return mSnap; 201 | } 202 | 203 | @Override 204 | protected void onDraw(Canvas canvas) { 205 | super.onDraw(canvas); 206 | 207 | if (mRecyleAdapter == null) 208 | return; 209 | 210 | final int count = mRecyleAdapter.getRealCount(); 211 | if (count <= 1) { 212 | return; 213 | } 214 | 215 | int longSize; 216 | int longPaddingBefore; 217 | int longPaddingAfter; 218 | int shortPaddingBefore; 219 | if (mOrientation == HORIZONTAL) { 220 | longSize = getWidth(); 221 | longPaddingBefore = getPaddingLeft(); 222 | longPaddingAfter = getPaddingRight(); 223 | shortPaddingBefore = getPaddingTop(); 224 | } else { 225 | longSize = getHeight(); 226 | longPaddingBefore = getPaddingTop(); 227 | longPaddingAfter = getPaddingBottom(); 228 | shortPaddingBefore = getPaddingLeft(); 229 | } 230 | 231 | final float threeRadius = mRadius * 3; 232 | final float shortOffset = shortPaddingBefore + mRadius; 233 | float longOffset = longPaddingBefore + mRadius; 234 | if (mCentered) { 235 | longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) 236 | - ((count * threeRadius) / 2.0f); 237 | } 238 | 239 | float dX; 240 | float dY; 241 | 242 | float pageFillRadius = mRadius; 243 | if (mPaintStroke.getStrokeWidth() > 0) { 244 | pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; 245 | } 246 | 247 | //Draw stroked circles 248 | for (int iLoop = 0; iLoop < count; iLoop++) { 249 | float drawLong = longOffset + (iLoop * threeRadius); 250 | if (mOrientation == HORIZONTAL) { 251 | dX = drawLong; 252 | dY = shortOffset; 253 | } else { 254 | dX = shortOffset; 255 | dY = drawLong; 256 | } 257 | // Only paint fill if not completely transparent 258 | if (mPaintPageFill.getAlpha() > 0) { 259 | canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill); 260 | } 261 | 262 | // Only paint stroke if a stroke width was non-zero 263 | if (pageFillRadius != mRadius) { 264 | canvas.drawCircle(dX, dY, mRadius, mPaintStroke); 265 | } 266 | } 267 | 268 | //Draw the filled circle according to the current scroll 269 | float cx = mSnapPage % count * threeRadius; 270 | if (mOrientation == HORIZONTAL) { 271 | dX = longOffset + cx; 272 | dY = shortOffset; 273 | } else { 274 | dX = shortOffset; 275 | dY = longOffset + cx; 276 | } 277 | canvas.drawCircle(dX, dY, mRadius, mPaintFill); 278 | } 279 | 280 | public boolean onTouchEvent(MotionEvent ev) { 281 | if (mViewPager == null || mRecyleAdapter.getCount() == 0) { 282 | return false; 283 | } 284 | 285 | final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; 286 | switch (action) { 287 | case MotionEvent.ACTION_DOWN: 288 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 289 | mLastMotionX = ev.getX(); 290 | break; 291 | 292 | case MotionEvent.ACTION_MOVE: { 293 | final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 294 | final float x = MotionEventCompat.getX(ev, activePointerIndex); 295 | final float deltaX = x - mLastMotionX; 296 | 297 | if (!mIsDragging) { 298 | if (Math.abs(deltaX) > mTouchSlop) { 299 | mIsDragging = true; 300 | } 301 | } 302 | 303 | if (mIsDragging) { 304 | mLastMotionX = x; 305 | if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { 306 | mViewPager.fakeDragBy(deltaX); 307 | } 308 | } 309 | 310 | break; 311 | } 312 | 313 | case MotionEvent.ACTION_CANCEL: 314 | case MotionEvent.ACTION_UP: 315 | if (!mIsDragging) { 316 | final int count = mViewPager.getAdapter().getCount(); 317 | final int width = getWidth(); 318 | final float halfWidth = width / 2f; 319 | final float sixthWidth = width / 6f; 320 | 321 | if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { 322 | if (action != MotionEvent.ACTION_CANCEL) { 323 | mViewPager.setCurrentItem(mCurrentPage - 1); 324 | } 325 | return true; 326 | } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { 327 | if (action != MotionEvent.ACTION_CANCEL) { 328 | mViewPager.setCurrentItem(mCurrentPage + 1); 329 | } 330 | return true; 331 | } 332 | } 333 | 334 | mIsDragging = false; 335 | mActivePointerId = INVALID_POINTER; 336 | if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); 337 | break; 338 | 339 | case MotionEventCompat.ACTION_POINTER_DOWN: { 340 | final int index = MotionEventCompat.getActionIndex(ev); 341 | mLastMotionX = MotionEventCompat.getX(ev, index); 342 | mActivePointerId = MotionEventCompat.getPointerId(ev, index); 343 | break; 344 | } 345 | 346 | case MotionEventCompat.ACTION_POINTER_UP: 347 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 348 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 349 | if (pointerId == mActivePointerId) { 350 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 351 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 352 | } 353 | mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); 354 | break; 355 | } 356 | 357 | return true; 358 | } 359 | 360 | @Override 361 | public void setViewPager(ViewPager view) { 362 | if (mViewPager == view) { 363 | return; 364 | } 365 | mViewPager = view; 366 | mRecyleAdapter = (RecyleAdapter) mViewPager.getAdapter(); 367 | } 368 | 369 | @Override 370 | public void setCurrentItem(int item) { 371 | mCurrentPage = item; 372 | invalidate(); 373 | } 374 | 375 | @Override 376 | public void notifyDataSetChanged() { 377 | mCurrentPage = 0; 378 | invalidate(); 379 | } 380 | 381 | @Override 382 | public void onPageScrollStateChanged(int state) { 383 | mScrollState = state; 384 | } 385 | 386 | @Override 387 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 388 | mCurrentPage = position; 389 | mPageOffset = positionOffset; 390 | invalidate(); 391 | } 392 | 393 | @Override 394 | public void onPageSelected(int position) { 395 | if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { 396 | mCurrentPage = position; 397 | mSnapPage = position; 398 | invalidate(); 399 | } 400 | } 401 | 402 | 403 | /* 404 | * (non-Javadoc) 405 | * 406 | * @see android.view.View#onMeasure(int, int) 407 | */ 408 | @Override 409 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 410 | if (mOrientation == HORIZONTAL) { 411 | setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); 412 | } else { 413 | setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); 414 | } 415 | } 416 | 417 | /** 418 | * Determines the width of this view 419 | * 420 | * @param measureSpec 421 | * A measureSpec packed into an int 422 | * @return The width of the view, honoring constraints from measureSpec 423 | */ 424 | private int measureLong(int measureSpec) { 425 | int result; 426 | int specMode = MeasureSpec.getMode(measureSpec); 427 | int specSize = MeasureSpec.getSize(measureSpec); 428 | 429 | if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { 430 | //We were told how big to be 431 | result = specSize; 432 | } else { 433 | //Calculate the width according the views count 434 | final int count = mRecyleAdapter.getRealCount(); 435 | result = (int)(getPaddingLeft() + getPaddingRight() 436 | + (count * 2 * mRadius) + (count - 1) * mRadius + 1); 437 | //Respect AT_MOST value if that was what is called for by measureSpec 438 | if (specMode == MeasureSpec.AT_MOST) { 439 | result = Math.min(result, specSize); 440 | } 441 | } 442 | return result; 443 | } 444 | 445 | /** 446 | * Determines the height of this view 447 | * 448 | * @param measureSpec 449 | * A measureSpec packed into an int 450 | * @return The height of the view, honoring constraints from measureSpec 451 | */ 452 | private int measureShort(int measureSpec) { 453 | int result; 454 | int specMode = MeasureSpec.getMode(measureSpec); 455 | int specSize = MeasureSpec.getSize(measureSpec); 456 | 457 | if (specMode == MeasureSpec.EXACTLY) { 458 | //We were told how big to be 459 | result = specSize; 460 | } else { 461 | //Measure the height 462 | result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); 463 | //Respect AT_MOST value if that was what is called for by measureSpec 464 | if (specMode == MeasureSpec.AT_MOST) { 465 | result = Math.min(result, specSize); 466 | } 467 | } 468 | return result; 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/indicator/PageIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Patrik Akerfeldt 3 | * Copyright (C) 2011 Jake Wharton 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package cn.lightsky.infiniteindicator.indicator; 19 | 20 | import android.support.v4.view.ViewPager; 21 | 22 | /** 23 | * A PageIndicator is responsible to show an visual indicator on the total views 24 | * number and the current visible view. 25 | */ 26 | public interface PageIndicator extends ViewPager.OnPageChangeListener { 27 | 28 | /** 29 | * Bind the indicator to a ViewPager. 30 | * 31 | * @param view 32 | */ 33 | void setViewPager(ViewPager view); 34 | 35 | /** 36 | *

Set the current page of indicator.

37 | * 38 | *

This must be used if you need to set the page before 39 | * the views are drawn on screen (e.g., default start page).

40 | * 41 | * @param item 42 | */ 43 | void setCurrentItem(int item); 44 | 45 | /** 46 | * Notify the indicator that the page list has changed. 47 | */ 48 | void notifyDataSetChanged(); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/recycle/BaseViewBinder.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.recycle; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | 9 | import cn.lightsky.infiniteindicator.ImageLoader; 10 | import cn.lightsky.infiniteindicator.IndicatorConfiguration; 11 | import cn.lightsky.infiniteindicator.OnPageClickListener; 12 | import cn.lightsky.infiniteindicator.Page; 13 | import cn.lightsky.infiniteindicator.R; 14 | 15 | /** 16 | * Created by lightsky on 2016/11/18. 17 | */ 18 | 19 | public class BaseViewBinder implements ViewBinder { 20 | 21 | @Override 22 | public View bindView(Context context, 23 | final int position, 24 | final Page page, 25 | ImageLoader imageLoader, 26 | final OnPageClickListener mOnPageClickListener, 27 | View convertView, 28 | ViewGroup container) { 29 | 30 | ViewHolder holder; 31 | if (convertView != null) { 32 | holder = (ViewHolder) convertView.getTag(); 33 | } else { 34 | convertView = LayoutInflater.from(context).inflate(R.layout.simple_slider_view, null); 35 | holder = new ViewHolder(convertView); 36 | convertView.setTag(holder); 37 | } 38 | 39 | if (holder.target != null) { 40 | if (mOnPageClickListener != null) { 41 | holder.target.setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | mOnPageClickListener.onPageClick(position, page); 45 | } 46 | }); 47 | } 48 | 49 | if (imageLoader != null) { 50 | imageLoader.load(context, holder.target, page.res); 51 | } 52 | } 53 | 54 | return convertView; 55 | } 56 | 57 | private static class ViewHolder { 58 | final ImageView target; 59 | 60 | public ViewHolder(View view) { 61 | target = (ImageView) view.findViewById(R.id.slider_image); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/recycle/RecycleBin.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.recycle; 2 | 3 | import android.os.Build; 4 | import android.util.SparseArray; 5 | import android.view.View; 6 | 7 | /** 8 | * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of 9 | * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the 10 | * start of a layout. By construction, they are displaying current information. At the end of 11 | * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that 12 | * could potentially be used by the adapter to avoid allocating views unnecessarily. 13 | *

14 | * This class was taken from Android's implementation of {@link android.widget.AbsListView} which 15 | * is copyrighted 2006 The Android Open Source Project. 16 | */ 17 | public class RecycleBin { 18 | /** 19 | * Views that were on screen at the start of layout. This array is populated at the start of 20 | * layout, and at the end of layout all view in activeViews are moved to scrapViews. 21 | * Views in activeViews represent a contiguous range of Views, with position of the first 22 | * view store in mFirstActivePosition. 23 | */ 24 | private View[] activeViews = new View[0]; 25 | private int[] activeViewTypes = new int[0]; 26 | 27 | /** Unsorted views that can be used by the adapter as a convert view. */ 28 | private SparseArray[] scrapViews; 29 | 30 | private int viewTypeCount; 31 | 32 | private SparseArray currentScrapViews; 33 | 34 | public void setViewTypeCount(int viewTypeCount) { 35 | if (viewTypeCount < 1) { 36 | throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); 37 | } 38 | //noinspection unchecked 39 | SparseArray[] scrapViews = new SparseArray[viewTypeCount]; 40 | for (int i = 0; i < viewTypeCount; i++) { 41 | scrapViews[i] = new SparseArray(); 42 | } 43 | this.viewTypeCount = viewTypeCount; 44 | currentScrapViews = scrapViews[0]; 45 | this.scrapViews = scrapViews; 46 | } 47 | 48 | protected boolean shouldRecycleViewType(int viewType) { 49 | return viewType >= 0; 50 | } 51 | 52 | /** @return A view from the ScrapViews collection. These are unordered. */ 53 | View getScrapView(int position, int viewType) { 54 | if (viewTypeCount == 1) { 55 | return retrieveFromScrap(currentScrapViews, position); 56 | } else if (viewType >= 0 && viewType < scrapViews.length) { 57 | return retrieveFromScrap(scrapViews[viewType], position); 58 | } 59 | return null; 60 | } 61 | 62 | /** 63 | * Put a view into the ScrapViews list. These views are unordered. 64 | * 65 | * @param scrap The view to add 66 | */ 67 | void addScrapView(View scrap, int position, int viewType) { 68 | if (viewTypeCount == 1) { 69 | currentScrapViews.put(position, scrap); 70 | } else { 71 | scrapViews[viewType].put(position, scrap); 72 | } 73 | 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 75 | scrap.setAccessibilityDelegate(null); 76 | } 77 | } 78 | 79 | /** Move all views remaining in activeViews to scrapViews. */ 80 | void scrapActiveViews() { 81 | final View[] activeViews = this.activeViews; 82 | final int[] activeViewTypes = this.activeViewTypes; 83 | final boolean multipleScraps = viewTypeCount > 1; 84 | 85 | SparseArray scrapViews = currentScrapViews; 86 | final int count = activeViews.length; 87 | for (int i = count - 1; i >= 0; i--) { 88 | final View victim = activeViews[i]; 89 | if (victim != null) { 90 | int whichScrap = activeViewTypes[i]; 91 | 92 | activeViews[i] = null; 93 | activeViewTypes[i] = -1; 94 | 95 | if (!shouldRecycleViewType(whichScrap)) { 96 | continue; 97 | } 98 | 99 | if (multipleScraps) { 100 | scrapViews = this.scrapViews[whichScrap]; 101 | } 102 | scrapViews.put(i, victim); 103 | 104 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 105 | victim.setAccessibilityDelegate(null); 106 | } 107 | } 108 | } 109 | 110 | pruneScrapViews(); 111 | } 112 | 113 | /** 114 | * Makes sure that the size of scrapViews does not exceed the size of activeViews. 115 | * (This can happen if an adapter does not recycle its views). 116 | */ 117 | private void pruneScrapViews() { 118 | final int maxViews = activeViews.length; 119 | final int viewTypeCount = this.viewTypeCount; 120 | final SparseArray[] scrapViews = this.scrapViews; 121 | for (int i = 0; i < viewTypeCount; ++i) { 122 | final SparseArray scrapPile = scrapViews[i]; 123 | int size = scrapPile.size(); 124 | final int extras = size - maxViews; 125 | size--; 126 | for (int j = 0; j < extras; j++) { 127 | scrapPile.remove(scrapPile.keyAt(size--)); 128 | } 129 | } 130 | } 131 | 132 | static View retrieveFromScrap(SparseArray scrapViews, int position) { 133 | int size = scrapViews.size(); 134 | if (size > 0) { 135 | // See if we still have a view for this position. 136 | for (int i = 0; i < size; i++) { 137 | int fromPosition = scrapViews.keyAt(i); 138 | View view = scrapViews.get(fromPosition); 139 | if (fromPosition == position) { 140 | scrapViews.remove(fromPosition); 141 | return view; 142 | } 143 | } 144 | int index = size - 1; 145 | View r = scrapViews.valueAt(index); 146 | scrapViews.remove(scrapViews.keyAt(index)); 147 | return r; 148 | } else { 149 | return null; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/recycle/RecyclingPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.recycle; 2 | 3 | import android.support.v4.view.PagerAdapter; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.AdapterView; 7 | 8 | /** 9 | * A {@link android.support.v4.view.PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and 10 | * view recycling. 11 | */ 12 | public abstract class RecyclingPagerAdapter extends PagerAdapter { 13 | static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; 14 | 15 | private final RecycleBin recycleBin; 16 | private DataChangeListener mDataChangeListener; 17 | 18 | public interface DataChangeListener { 19 | void notifyDataChange(); 20 | } 21 | 22 | public DataChangeListener getDataChangeListener() { 23 | return mDataChangeListener; 24 | } 25 | 26 | public void setDataChangeListener(DataChangeListener dataChangeListener) { 27 | this.mDataChangeListener = dataChangeListener; 28 | } 29 | 30 | public RecyclingPagerAdapter() { 31 | this(new RecycleBin()); 32 | } 33 | 34 | RecyclingPagerAdapter(RecycleBin recycleBin) { 35 | this.recycleBin = recycleBin; 36 | recycleBin.setViewTypeCount(getViewTypeCount()); 37 | } 38 | 39 | @Override 40 | public void notifyDataSetChanged() { 41 | recycleBin.scrapActiveViews(); 42 | if (mDataChangeListener != null) { 43 | mDataChangeListener.notifyDataChange(); 44 | } 45 | super.notifyDataSetChanged(); 46 | } 47 | 48 | @Override 49 | public final Object instantiateItem(ViewGroup container, int position) { 50 | int viewType = getItemViewType(position); 51 | View view = null; 52 | if (viewType != IGNORE_ITEM_VIEW_TYPE) { 53 | view = recycleBin.getScrapView(position, viewType); 54 | } 55 | view = getView(position, view, container); 56 | container.addView(view); 57 | return view; 58 | } 59 | 60 | @Override 61 | public final void destroyItem(ViewGroup container, int position, Object object) { 62 | View view = (View) object; 63 | container.removeView(view); 64 | int viewType = getItemViewType(position); 65 | if (viewType != IGNORE_ITEM_VIEW_TYPE) { 66 | recycleBin.addScrapView(view, position, viewType); 67 | } 68 | } 69 | 70 | @Override 71 | public final boolean isViewFromObject(View view, Object object) { 72 | return view == object; 73 | } 74 | 75 | /** 76 | *

77 | * Returns the number of types of Views that will be created by 78 | * {@link #getView}. Each type represents a set of views that can be 79 | * converted in {@link #getView}. If the adapter always returns the same 80 | * type of View for all items, this method should return 1. 81 | *

82 | *

83 | * This method will only be called when when the adapter is set on the 84 | * the {@link android.widget.AdapterView}. 85 | *

86 | * 87 | * @return The number of types of Views that will be created by this adapter 88 | */ 89 | public int getViewTypeCount() { 90 | return 1; 91 | } 92 | 93 | /** 94 | * Get the type of View that will be created by {@link #getView} for the specified item. 95 | * 96 | * @param position The position of the item within the adapter's data set whose view type we 97 | * want. 98 | * @return An integer representing the type of View. Two views should share the same type if one 99 | * can be converted to the other in {@link #getView}. Note: Integers must be in the 100 | * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can 101 | * also be returned. 102 | * @see #IGNORE_ITEM_VIEW_TYPE 103 | */ 104 | @SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses. 105 | public int getItemViewType(int position) { 106 | return 0; 107 | } 108 | 109 | /** 110 | * Get a View that displays the data at the specified position in the data set. You can either 111 | * create a View manually or inflate it from an XML layout file. When the View is inflated, the 112 | * parent View (GridView, ListView...) will apply default layout parameters unless you use 113 | * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} 114 | * to specify a root view and to prevent attachment to the root. 115 | * 116 | * @param position The position of the item within the adapter's data set of the item whose view 117 | * we want. 118 | * @param convertView The old view to reuse, if possible. Note: You should check that this view 119 | * is non-null and of an appropriate type before using. If it is not possible to convert 120 | * this view to display the correct data, this method can create a new view. 121 | * Heterogeneous lists can specify their number of view types, so that this View is 122 | * always of the right type (see {@link #getViewTypeCount()} and 123 | * {@link #getItemViewType(int)}). 124 | * @param container The parent that this view will eventually be attached to 125 | * @return A View corresponding to the data at the specified position. 126 | */ 127 | public abstract View getView(int position, View convertView, ViewGroup container); 128 | } 129 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/recycle/RecyleAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.recycle; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import cn.lightsky.infiniteindicator.ImageLoader; 11 | import cn.lightsky.infiniteindicator.OnPageClickListener; 12 | import cn.lightsky.infiniteindicator.Page; 13 | 14 | import static cn.lightsky.infiniteindicator.InfiniteIndicator.PAGE_COUNT_FACTOR; 15 | 16 | public class RecyleAdapter extends RecyclingPagerAdapter { 17 | 18 | private ViewBinder mViewBinder; 19 | private Context mContext; 20 | private ImageLoader mImageLoader; 21 | private OnPageClickListener mOnPageClickListener; 22 | private List mPageList = new ArrayList<>(); 23 | private boolean isLoop = true; 24 | 25 | public RecyleAdapter(Context context, ViewBinder viewBinder) { 26 | this(context, viewBinder,null); 27 | } 28 | 29 | public RecyleAdapter(Context context, ViewBinder viewBinder, OnPageClickListener onPageClickListener) { 30 | this.mContext = context; 31 | this.mOnPageClickListener = onPageClickListener; 32 | this.mViewBinder = viewBinder; 33 | } 34 | 35 | /** 36 | * get really position 37 | * 38 | * @param position 39 | * @return 40 | */ 41 | public int getRealPosition(int position) { 42 | return isLoop ? position % getRealCount() : position; 43 | } 44 | 45 | @Override 46 | public int getCount() { 47 | return isLoop ? getRealCount() * PAGE_COUNT_FACTOR : getRealCount(); 48 | } 49 | 50 | public int getRealCount() { 51 | return mPageList.size(); 52 | } 53 | 54 | @Override 55 | public View getView(final int position, View convertView, ViewGroup container) { 56 | 57 | final Page page = mPageList.get(getRealPosition(position)); 58 | convertView = mViewBinder.bindView( 59 | mContext, 60 | getRealPosition(position), 61 | page, 62 | mImageLoader, 63 | mOnPageClickListener, 64 | convertView, 65 | container); 66 | 67 | return convertView; 68 | } 69 | 70 | public void setImageLoader(ImageLoader imageLoader) { 71 | mImageLoader = imageLoader; 72 | } 73 | 74 | public void setPages(List pages) { 75 | this.mPageList = pages; 76 | notifyDataSetChanged(); 77 | } 78 | 79 | /** 80 | * @return the is Loop 81 | */ 82 | public boolean isLoop() { 83 | return isLoop; 84 | } 85 | 86 | /** 87 | * @param isLoop the is InfiniteLoop to set 88 | */ 89 | public void setIsLoop(boolean isLoop) { 90 | this.isLoop = isLoop; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /library/src/main/java/cn/lightsky/infiniteindicator/recycle/ViewBinder.java: -------------------------------------------------------------------------------- 1 | package cn.lightsky.infiniteindicator.recycle; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import cn.lightsky.infiniteindicator.ImageLoader; 8 | import cn.lightsky.infiniteindicator.OnPageClickListener; 9 | import cn.lightsky.infiniteindicator.Page; 10 | 11 | /** 12 | * Created by lightsky on 2016/11/18. 13 | * 14 | * extract getView for customing view binding 15 | */ 16 | 17 | public interface ViewBinder { 18 | 19 | View bindView(Context context, 20 | int position, 21 | Page page, 22 | ImageLoader imageLoader, 23 | OnPageClickListener onPageClickListener, 24 | View convertView, 25 | ViewGroup container); 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/res/animator/scale_with_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | 17 | 18 | 19 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/animator/translation_and_rotate.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 18 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/balck_radius_square.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/white_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_anim_circle_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | 46 | -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_anim_line_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 22 | 23 | 28 | 29 | 34 | 35 | 40 | 41 | 46 | 47 | 52 | -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_default_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | 25 | 26 | 30 | 31 | 35 | 36 | 40 | 41 | 45 | -------------------------------------------------------------------------------- /library/src/main/res/layout/simple_slider_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF0000 5 | #3366CC 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 10dp 7 | 8 | 30dp 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/res/values/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 20 | 21 | 28 | 29 | 30 | 31 | 38 | 39 | 46 | 47 | 54 | 55 | 62 | 63 | 74 | -------------------------------------------------------------------------------- /library/src/main/res/values/vpi__defaults.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | true 19 | #FFFFFFFF 20 | #00000000 21 | 0 22 | 3dp 23 | false 24 | #FFDDDDDD 25 | 1dp 26 | 27 | -------------------------------------------------------------------------------- /res/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/res/a.jpg -------------------------------------------------------------------------------- /res/b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/res/b.jpg -------------------------------------------------------------------------------- /res/c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/res/c.jpg -------------------------------------------------------------------------------- /res/d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightSky/InfiniteIndicator/95c4e98b60577a0844a0e402e627f43387c8aa3a/res/d.jpg -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo',':library' 2 | --------------------------------------------------------------------------------