├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── cn
│ │ └── modificator
│ │ └── sample
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── cn
│ │ │ └── modificator
│ │ │ └── sample
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── cn
│ └── modificator
│ └── sample
│ └── ExampleUnitTest.java
├── build.gradle
├── circleindicator
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── cn
│ │ └── modificator
│ │ └── circleindicator
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── cn
│ │ │ └── modificator
│ │ │ └── circleindicator
│ │ │ └── CircleIndicator.java
│ └── res
│ │ └── values
│ │ └── values.xml
│ └── test
│ └── java
│ └── cn
│ └── modificator
│ └── circleindicator
│ └── ExampleUnitTest.java
├── picture.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | /.idea
10 | /gradle.properties
11 | /gradle
12 | /gradlew
13 | /gradlew.bat
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Modificator
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## CircleIndicator
2 | ==========
3 | Indicator for ViewPager
4 |
5 | layout
6 | ```xml
7 |
14 |
15 |
16 |
24 | ```
25 | Java
26 | ```java
27 | //after viewpager setAdapter
28 | CircleIndicator.setViewPager()
29 | ```
30 |
31 | ##### Enjoy it!
32 | 
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/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.modificator.sample"
9 | minSdkVersion 14
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 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.3.0'
26 | compile project(':circleindicator')
27 | }
28 |
--------------------------------------------------------------------------------
/app/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 /home/mod/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/modificator/sample/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.sample;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/modificator/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.view.PagerAdapter;
5 | import android.support.v4.view.ViewPager;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.TextView;
10 |
11 | import cn.modificator.circleindicator.CircleIndicator;
12 |
13 | public class MainActivity extends AppCompatActivity {
14 | ViewPager mViewPager;
15 | CircleIndicator mTabView;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_main);
21 |
22 | mViewPager = (ViewPager) findViewById(R.id.mViewPager);
23 | mTabView = (CircleIndicator) findViewById(R.id.mTabView);
24 |
25 | mViewPager.setAdapter(new PagerAdapter() {
26 | @Override
27 | public int getCount() {
28 | return 4;
29 | }
30 |
31 | @Override
32 | public boolean isViewFromObject(View view, Object object) {
33 | return view == object;
34 | }
35 |
36 | @Override
37 | public void destroyItem(ViewGroup container, int position, Object object) {
38 | // super.destroyItem(container, position, object);
39 | container.removeView((View) object);
40 | }
41 |
42 | @Override
43 | public Object instantiateItem(ViewGroup container, int position) {
44 | TextView textView = new TextView(container.getContext());
45 | textView.setText("page " + position);
46 | container.addView(textView);
47 | return textView;
48 | }
49 | });
50 | mTabView.setViewPager(mViewPager);
51 |
52 | ((CircleIndicator) findViewById(R.id.indicator1)).setViewPager(mViewPager);
53 | ((CircleIndicator) findViewById(R.id.indicator2)).setViewPager(mViewPager);
54 | ((CircleIndicator) findViewById(R.id.indicator3)).setViewPager(mViewPager);
55 | ((CircleIndicator) findViewById(R.id.indicator4)).setViewPager(mViewPager);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
27 |
38 |
49 |
60 |
61 |
65 |
66 |
69 |
70 |
74 |
75 |
81 |
82 |
86 |
87 |
92 |
93 |
94 |
100 |
101 |
105 |
106 |
111 |
112 |
113 |
119 |
120 |
124 |
125 |
130 |
131 |
132 |
138 |
139 |
143 |
144 |
149 |
150 |
151 |
152 |
153 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Sample
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/modificator/sample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.sample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/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.1.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/circleindicator/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/circleindicator/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | provided 'com.android.support:support-v4:23.3.0'
24 | }
25 |
--------------------------------------------------------------------------------
/circleindicator/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 /home/mod/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 |
--------------------------------------------------------------------------------
/circleindicator/src/androidTest/java/cn/modificator/circleindicator/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.circleindicator;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/circleindicator/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/circleindicator/src/main/java/cn/modificator/circleindicator/CircleIndicator.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.circleindicator;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.database.DataSetObserver;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.RectF;
10 | import android.support.annotation.ColorInt;
11 | import android.support.annotation.FloatRange;
12 | import android.support.annotation.IntDef;
13 | import android.support.annotation.IntRange;
14 | import android.support.v4.view.ViewPager;
15 | import android.util.AttributeSet;
16 | import android.view.View;
17 |
18 | import java.lang.annotation.Retention;
19 | import java.lang.annotation.RetentionPolicy;
20 |
21 | /**
22 | * Created by modificator on 16-5-11.
23 | */
24 | public class CircleIndicator extends View implements ViewPager.OnPageChangeListener {
25 |
26 | public static final int GRAVITY_FILL = 0;
27 | public static final int GRAVITY_CENTER = 1;
28 | public static final int GRAVITY_LEFT = 2;
29 | public static final int GRAVITY_RIGHT = 3;
30 |
31 | private ViewPager mViewPager;
32 | //point 对齐方式
33 | // @PointGravity
34 | private int pointGravity = 0;
35 | //圆环宽度百分比
36 | private float ringWidth = 0.2f;
37 | //点的背景色
38 | private int pointBgColor = 0xffaaaaaa;
39 | //圆环前景色
40 | private int ringColor = 0xff000000;
41 | //viewpager的页码
42 | private int position = 0;
43 | //Value from [0, 1) indicating the offset from the page at position.
44 | float positionOffset = 0;
45 | //画笔
46 | private Paint paint;
47 | //观察viewpager 页面数变化
48 | DataSetObserver dataSetObserver;
49 |
50 | @IntDef(flag = false, value = {GRAVITY_FILL, GRAVITY_CENTER, GRAVITY_LEFT, GRAVITY_RIGHT})
51 | @Retention(RetentionPolicy.SOURCE)
52 | public @interface PointGravity {
53 | }
54 |
55 | public CircleIndicator(Context context) {
56 | this(context, null);
57 | }
58 |
59 | public CircleIndicator(Context context, AttributeSet attrs) {
60 | this(context, attrs, 0);
61 | }
62 |
63 | public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
64 | super(context, attrs, defStyleAttr);
65 | //获取属性
66 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.indicator, defStyleAttr, 0);
67 | pointGravity = ta.getInt(R.styleable.indicator_pointGravity, pointGravity);
68 | ringWidth = ta.getFraction(R.styleable.indicator_ringWidth, 1, 1, ringWidth);
69 | pointBgColor = ta.getColor(R.styleable.indicator_pointBgColor, pointBgColor);
70 | ringColor = ta.getColor(R.styleable.indicator_ringColor, ringColor);
71 | ta.recycle();
72 | init();
73 | }
74 |
75 | /**
76 | * 初始化画笔
77 | */
78 | private void init() {
79 | paint = new Paint();
80 | paint.setStyle(Paint.Style.STROKE);
81 | //当viewpager 页面删除或增加的时候实时改变指示器
82 | dataSetObserver = new DataSetObserver() {
83 | @Override
84 | public void onChanged() {
85 | super.onChanged();
86 | invalidate();
87 | }
88 |
89 | @Override
90 | public void onInvalidated() {
91 | super.onInvalidated();
92 | invalidate();
93 | }
94 | };
95 | }
96 |
97 | @Override
98 | protected void onDraw(Canvas canvas) {
99 | super.onDraw(canvas);
100 | if (mViewPager == null || mViewPager.getAdapter() == null || mViewPager.getAdapter().getCount() == 0)
101 | return;
102 | //绘制个数
103 | int pointCount = mViewPager.getAdapter().getCount();
104 | //获取直径
105 | float pointSize = Math.min(getWidth() * 1f / pointCount, getHeight());
106 | //设置画笔大小
107 | paint.setStrokeWidth(pointSize * ringWidth / 2f);
108 | //计算绘制矩形宽度,如果大于View宽度则设置为填充 pointCount + 1 是为两边留出空位
109 | if ((pointCount * pointSize + (pointCount + 1) * pointSize / 3) > getWidth())
110 | pointGravity = GRAVITY_FILL;
111 |
112 | drawBgPoint(canvas, pointCount, pointSize);
113 | drawRingLine(canvas, pointCount, pointSize);
114 | }
115 |
116 | /**
117 | * 绘制背景
118 | *
119 | * @param canvas
120 | * @param pointCount
121 | * @param pointSize
122 | */
123 | private void drawBgPoint(Canvas canvas, int pointCount, float pointSize) {
124 | //设置画笔样式为填充
125 | paint.setStyle(Paint.Style.FILL);
126 | //设置背景色
127 | paint.setColor(pointBgColor);
128 | if (pointGravity == GRAVITY_FILL) {
129 | float boxWidth = getWidth() * 1f / pointCount;
130 | for (int i = 0; i < pointCount; i++) {
131 | float circleCenterX = boxWidth * i + (boxWidth / 2f);
132 | float circleCenterY = getHeight() / 2f;
133 | canvas.drawCircle(circleCenterX, circleCenterY, pointSize / 2f, paint);
134 | }
135 | } else {
136 | //这个变量现在的值是 第一个点到view左边的距离
137 | float drawLeft = getWidth() - (pointCount * pointSize + (pointCount - 1) * pointSize / 3);
138 | if (pointGravity == GRAVITY_CENTER) {
139 | //居中,so 一边一半
140 | drawLeft = drawLeft / 2;
141 | } else if (pointGravity == GRAVITY_RIGHT) {
142 | //右对齐,最右边留下点距离
143 | drawLeft = drawLeft - pointSize / 3;
144 | } else {
145 | //最对齐,
146 | drawLeft = pointSize / 3;
147 | }
148 |
149 | //背景圆的中心点
150 | float circleCenterY = getHeight() / 2f;
151 | float circleCenterX = 0;
152 | for (int i = 0; i < pointCount; i++) {
153 | circleCenterX = drawLeft + pointSize / 2;// + pointSize * 13 / 10 * (i - 1);//* i + pointSize / 3 * (i - 1) + pointSize * i;
154 | drawLeft = drawLeft + pointSize * 13 / 10;
155 | canvas.drawCircle(circleCenterX, circleCenterY, pointSize / 2f, paint);
156 | }
157 | }
158 | }
159 |
160 | private void drawRingLine(Canvas canvas, int pointCount, float pointSize) {
161 | paint.setStyle(Paint.Style.STROKE);
162 | paint.setColor(ringColor);
163 | //计算运动路径长度
164 | double pathLength = pointSize * Math.PI + (pointGravity == GRAVITY_FILL ? (getWidth() * 1f / pointCount) : (pointSize * 13 / 10));
165 | //计算最第一个点左侧x坐标
166 | float drawLeft = getWidth() - (pointCount * pointSize + (pointCount - 1) * pointSize / 3);
167 |
168 | //position * pointSize * 13 / 10 这个是点的宽度和中间间距的长度
169 | if (pointGravity == GRAVITY_CENTER) {
170 | drawLeft = drawLeft / 2 + position * pointSize * 13 / 10;
171 | } else if (pointGravity == GRAVITY_RIGHT) {
172 | drawLeft = drawLeft - pointSize / 3 + position * pointSize * 13 / 10;
173 | } else {
174 | drawLeft = pointSize / 3 + position * pointSize * 13 / 10;
175 | }
176 | //fill的时候确定中心点
177 | float boxWidth = getWidth() * 1f / pointCount;
178 | //画笔宽度恩,要把圈环画到背景里面需要用到
179 | float paintWidth = paint.getStrokeWidth();
180 |
181 | //计算第一个圆环的绘制范围
182 | RectF lastRectF = new RectF();
183 | float lastCenterX = pointGravity == GRAVITY_FILL ? (boxWidth * position + (boxWidth / 2)) : (drawLeft + pointSize / 2);
184 | float lastCenterY = getHeight() / 2f;
185 | lastRectF.left = lastCenterX - pointSize / 2 + paintWidth / 2;
186 | lastRectF.top = lastCenterY - pointSize / 2 + paintWidth / 2;
187 | lastRectF.right = lastCenterX + pointSize / 2 - paintWidth / 2;
188 | lastRectF.bottom = lastCenterY + pointSize / 2 - paintWidth / 2;
189 | //计算第一个圆环绘制角度,保证第二个圆环和下面的线联动
190 | float lastAngle = (float) (360 * ((pointSize * Math.PI / pathLength - positionOffset) / (pointSize * Math.PI / pathLength)));
191 | //避免圆环继续往回画
192 | lastAngle = lastAngle > 0 ? lastAngle : 0;
193 | canvas.drawArc(lastRectF, 90.0f, lastAngle, false, paint);
194 |
195 | //计算第二个圆环的绘制范围
196 | RectF nextRectF = new RectF();
197 | float nextCenterX = pointGravity == GRAVITY_FILL ? (boxWidth * (position + 1) + (boxWidth / 2)) : (drawLeft + pointSize / 2 + pointSize * 13 / 10);
198 | float nextCenterY = getHeight() / 2f;
199 | nextRectF.left = nextCenterX - pointSize / 2 + paintWidth / 2;
200 | nextRectF.top = nextCenterY - pointSize / 2 + paintWidth / 2;
201 | nextRectF.right = nextCenterX + pointSize / 2 - paintWidth / 2;
202 | nextRectF.bottom = nextCenterY + pointSize / 2 - paintWidth / 2;
203 | float nextAngle = (float) (360 * (((1 - positionOffset) - pointSize * Math.PI / pathLength) / (pointSize * Math.PI / pathLength)));
204 | nextAngle = nextAngle < 0 ? nextAngle : 0;
205 | canvas.drawArc(nextRectF, 90.0f, nextAngle, false, paint);
206 |
207 | //计算底下的线的绘制范围
208 | float lineTop = nextRectF.bottom - paint.getStrokeWidth() / 2;
209 | float lineRight = Double.valueOf(lastCenterX + pathLength * positionOffset).floatValue();
210 | float lineLeft = Double.valueOf(lineRight - pointSize * Math.PI).floatValue();
211 | float lineBottom = lineTop + paint.getStrokeWidth();
212 | //避免绘制超出两个圆心
213 | lineLeft = lineLeft > nextCenterX ? nextCenterX : lineLeft < lastCenterX ? lastCenterX : lineLeft;
214 | lineRight = lineRight > nextCenterX ? nextCenterX : lineRight;
215 |
216 | //别问我为啥用path , line根本满足不了好伐
217 | Path linePath = new Path();
218 | linePath.moveTo(lineLeft, lineTop);
219 | linePath.lineTo(lineRight, lineTop);
220 | linePath.lineTo(lineRight, lineBottom);
221 | linePath.lineTo(lineLeft, lineBottom);
222 |
223 | //将画笔影响降到最低,不然会超出,虽然还是会超出1像素
224 | paint.setStrokeWidth(1);
225 | paint.setStyle(Paint.Style.FILL);
226 | canvas.drawPath(linePath, paint);
227 | }
228 |
229 | /**
230 | * 设置viewpager滑动监听
231 | * @param viewpager
232 | */
233 | public void setViewPager(ViewPager viewpager) {
234 | this.mViewPager = viewpager;
235 | mViewPager.addOnPageChangeListener(this);
236 | mViewPager.getAdapter().registerDataSetObserver(dataSetObserver);
237 | }
238 |
239 | @Override
240 | protected void onVisibilityChanged(View changedView, int visibility) {
241 | super.onVisibilityChanged(changedView, visibility);
242 | if (mViewPager != null && mViewPager.getAdapter() != null)
243 | if (visibility == VISIBLE) {
244 | mViewPager.addOnPageChangeListener(this);
245 | try {//上面设置观察者后在设置会挂掉
246 | mViewPager.getAdapter().registerDataSetObserver(dataSetObserver);
247 | } catch (Exception e) {
248 | }
249 | } else {
250 | mViewPager.removeOnPageChangeListener(this);
251 | mViewPager.getAdapter().unregisterDataSetObserver(dataSetObserver);
252 | }
253 | }
254 |
255 | /**
256 | * 画笔宽度百分比
257 | *
258 | * @param ringWidth
259 | */
260 | public void setRingWidth(@FloatRange(from = 0, to = 1)
261 | float ringWidth) {
262 | this.ringWidth = ringWidth;
263 | invalidate();
264 | }
265 |
266 | /**
267 | * 设置对齐方式
268 | *
269 | * @param pointGravity
270 | */
271 | public void setPointGravity(@PointGravity
272 | int pointGravity) {
273 | this.pointGravity = pointGravity;
274 | invalidate();
275 | }
276 |
277 | /**
278 | * 设置圆环颜色
279 | *
280 | * @param ringColor 0x00000000 - 0xffffffff
281 | */
282 | public void setRingColor(@ColorInt
283 | @IntRange(from = 0x00000000, to = 0xffffffff)
284 | int ringColor) {
285 | this.ringColor = ringColor;
286 | }
287 |
288 | /**
289 | * 设置point 背景色
290 | *
291 | * @param pointBgColor 0x00000000 - 0xffffffff
292 | */
293 | public void setPointBgColor(@ColorInt
294 | @IntRange(from = 0x00000000, to = 0xffffffff)
295 | int pointBgColor) {
296 | this.pointBgColor = pointBgColor;
297 | }
298 |
299 | public float getRingWidth() {
300 | return ringWidth;
301 | }
302 |
303 | public int getPointGravity() {
304 | return pointGravity;
305 | }
306 |
307 | public int getRingColor() {
308 | return ringColor;
309 | }
310 |
311 | public int getPointBgColor() {
312 | return pointBgColor;
313 | }
314 |
315 | @Override
316 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
317 | this.position = position;
318 | this.positionOffset = positionOffset;
319 | this.invalidate();
320 | }
321 |
322 | @Override
323 | public void onPageSelected(int position) {
324 |
325 | }
326 |
327 | @Override
328 | public void onPageScrollStateChanged(int state) {
329 |
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/circleindicator/src/main/res/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CircleIndicator
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/circleindicator/src/test/java/cn/modificator/circleindicator/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.modificator.circleindicator;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/picture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Modificator/CircleIndicator/0965329822e466d1957a96494418bd9570cef05e/picture.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':circleindicator'
2 |
--------------------------------------------------------------------------------