├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jiang │ │ └── android │ │ └── scalingindicator │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── jiang │ │ │ └── android │ │ │ └── scalingindicator │ │ │ ├── MainActivity.java │ │ │ └── fragment │ │ │ └── SimpleFragment.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── tabpageindicator.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 │ └── com │ └── jiang │ └── android │ └── scalingindicator │ └── ExampleUnitTest.java ├── art └── demo.gif ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── indicator ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jiang │ │ └── android │ │ └── indicator │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── jiang │ │ │ └── android │ │ │ └── indicator │ │ │ ├── IcsLinearLayout.java │ │ │ ├── PageIndicator.java │ │ │ ├── TabPageIndicator.java │ │ │ ├── adapter │ │ │ ├── IconPagerAdapter.java │ │ │ └── ModelFragmentPagerAdapter.java │ │ │ └── model │ │ │ └── PageModel.java │ └── res │ │ ├── color │ │ ├── vpi__dark_theme.xml │ │ └── vpi__light_theme.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── vpi__attrs.xml │ │ ├── vpi__colors.xml │ │ ├── vpi__defaults.xml │ │ └── vpi__styles.xml │ └── test │ └── java │ └── com │ └── jiang │ └── android │ └── indicator │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | ### Android template 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScalingIndicator 2 | 3 | [ ![Download](https://api.bintray.com/packages/yuesong/maven/ScalingIndicator/images/download.svg) ](https://bintray.com/yuesong/maven/ScalingIndicator/_latestVersion) 4 | 5 | 可对文字进行放大缩小和展示未读提示的PageIndicator
6 | 7 | 项目基于jake大神的https://github.com/JakeWharton/ViewPagerIndicator
8 | 9 | 对ViewPagerIndicator做了一些修改以适应产品的需求
10 | 11 | 效果图如下:
12 | 13 | ![](https://raw.githubusercontent.com/jiang111/ScalingIndicator/master/art/demo.gif) 14 | 15 | 16 | 使用方法: 17 | * Gradle 18 | 19 | ``` 20 | dependencies { 21 | compile 'com.jiang.android.scalingindicator:indicator:@aar' 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | ### Android template 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | /build 43 | -------------------------------------------------------------------------------- /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 "com.jiang.android.scalingindicator" 9 | minSdkVersion 15 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(':indicator') 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 /Users/jiang/androidsdk/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/com/jiang/android/scalingindicator/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.scalingindicator; 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/com/jiang/android/scalingindicator/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.scalingindicator; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.view.ViewPager; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | 9 | import com.jiang.android.indicator.TabPageIndicator; 10 | import com.jiang.android.indicator.adapter.ModelFragmentPagerAdapter; 11 | import com.jiang.android.indicator.model.PageModel; 12 | import com.jiang.android.scalingindicator.fragment.SimpleFragment; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | private TabPageIndicator mIndicator; 20 | private ViewPager mViewPager; 21 | private List mFragmentLists = new ArrayList<>(); 22 | private List mDatas = new ArrayList<>(); 23 | private ModelFragmentPagerAdapter mAdapter; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | initView(); 30 | initFragments(); 31 | initAdapter(); 32 | setUI(); 33 | } 34 | 35 | private void setUI() { 36 | mViewPager.setAdapter(mAdapter); 37 | mViewPager.setOffscreenPageLimit(mDatas.size()); 38 | mIndicator.setVisibility(View.VISIBLE); 39 | mIndicator.setViewPager(mViewPager, 0); 40 | 41 | } 42 | 43 | private void initAdapter() { 44 | mAdapter = new ModelFragmentPagerAdapter(getSupportFragmentManager()) { 45 | 46 | 47 | @Override 48 | public CharSequence getPageTitle(int position) { 49 | return mDatas.get(position).getName(); 50 | } 51 | 52 | @Override 53 | public PageModel getPageModel(int position) { 54 | return mDatas.get(position); 55 | } 56 | 57 | @Override 58 | public int getCount() { 59 | return mFragmentLists.size(); 60 | } 61 | 62 | @Override 63 | public Fragment getItem(int position) { 64 | return mFragmentLists.get(position); 65 | } 66 | }; 67 | } 68 | 69 | private void initView() { 70 | mIndicator = (TabPageIndicator) findViewById(R.id.indicator); 71 | mViewPager = (ViewPager) findViewById(R.id.viewpager); 72 | 73 | mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { 74 | @Override 75 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 76 | 77 | } 78 | 79 | @Override 80 | public void onPageSelected(int position) { 81 | mDatas.get(position).setCount(0); 82 | if (mAdapter != null && mIndicator != null) { 83 | mAdapter.notifyDataSetChanged(); 84 | mIndicator.notifyDataSetChanged(); 85 | } 86 | } 87 | 88 | @Override 89 | public void onPageScrollStateChanged(int state) { 90 | 91 | } 92 | }); 93 | 94 | } 95 | 96 | private void initFragments() { 97 | mFragmentLists.add(SimpleFragment.newInstance("工厂方法")); 98 | mFragmentLists.add(SimpleFragment.newInstance("抽象工厂")); 99 | mFragmentLists.add(SimpleFragment.newInstance("单例模式")); 100 | mFragmentLists.add(SimpleFragment.newInstance("桥接模式")); 101 | mFragmentLists.add(SimpleFragment.newInstance("组合模式")); 102 | mFragmentLists.add(SimpleFragment.newInstance("外观模式")); 103 | 104 | mDatas.add(new PageModel("工厂方法", 1)); 105 | mDatas.add(new PageModel("抽象工厂", 1)); 106 | mDatas.add(new PageModel("单例模式", 1)); 107 | mDatas.add(new PageModel("桥接模式", 1)); 108 | mDatas.add(new PageModel("组合模式", 1)); 109 | mDatas.add(new PageModel("外观模式", 1)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/jiang/android/scalingindicator/fragment/SimpleFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * created by jiang, 16/2/28 3 | * Copyright (c) 2016, jyuesong@gmail.com All Rights Reserved. 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | */ 28 | 29 | package com.jiang.android.scalingindicator.fragment; 30 | 31 | import android.os.Bundle; 32 | import android.support.v4.app.Fragment; 33 | import android.view.Gravity; 34 | import android.view.LayoutInflater; 35 | import android.view.View; 36 | import android.view.ViewGroup; 37 | import android.widget.TextView; 38 | 39 | /** 40 | * Created by jiang on 16/2/28. 41 | */ 42 | 43 | public class SimpleFragment extends Fragment { 44 | 45 | public static final String BUNDLE_TITLE = "title"; 46 | private String mTitle = "DefaultValue"; 47 | 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 50 | Bundle savedInstanceState) { 51 | Bundle arguments = getArguments(); 52 | if (arguments != null) { 53 | mTitle = arguments.getString(BUNDLE_TITLE); 54 | } 55 | 56 | TextView tv = new TextView(getActivity()); 57 | tv.setText(mTitle); 58 | tv.setGravity(Gravity.CENTER); 59 | 60 | return tv; 61 | } 62 | 63 | public static SimpleFragment newInstance(String title) { 64 | Bundle bundle = new Bundle(); 65 | bundle.putString(BUNDLE_TITLE, title); 66 | SimpleFragment fragment = new SimpleFragment(); 67 | fragment.setArguments(bundle); 68 | return fragment; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tabpageindicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/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 | ScalingIndicator 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 29 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/test/java/com/jiang/android/scalingindicator/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.scalingindicator; 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 | } -------------------------------------------------------------------------------- /art/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiang111/ScalingIndicator/b37625e3173848f68c770aef4654ced22753fb32/art/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:1.5.0' 9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | plugins { 16 | id "com.jfrog.bintray" version "1.6" 17 | } 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 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 -------------------------------------------------------------------------------- /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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /indicator/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | ### Android template 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | 40 | # Keystore files 41 | *.jks 42 | 43 | -------------------------------------------------------------------------------- /indicator/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | // This is the library version used when deploying the artifact 5 | version = "1.0.1" 6 | android { 7 | compileSdkVersion 23 8 | buildToolsVersion "23.0.2" 9 | resourcePrefix "jiang_" 10 | defaultConfig { 11 | minSdkVersion 8 12 | targetSdkVersion 23 13 | versionCode 2 14 | versionName "1.1" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | testCompile 'junit:junit:4.12' 27 | compile 'com.android.support:appcompat-v7:23.3.0' 28 | } 29 | 30 | 31 | def siteUrl = 'https://github.com/jiang111/ScalingIndicator' // 项目的主页 32 | def gitUrl = 'https://github.com/jiang111/ScalingIndicator.git' // Git仓库的url 33 | group = "com.jiang.android.scalingindicator" // Maven Group ID for the artifact,一般填你唯一的包名 34 | install { 35 | repositories.mavenInstaller { 36 | // This generates POM.xml with proper parameters 37 | pom { 38 | project { 39 | packaging 'aar' 40 | // Add your description here 41 | name 'Android ScalingIndicator library' //项目描述 42 | url siteUrl 43 | // Set your license 44 | licenses { 45 | license { 46 | name 'The Apache Software License, Version 2.0' 47 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 48 | } 49 | } 50 | developers { 51 | developer { 52 | id 'jiangyuesong' //填写的一些基本信息 53 | name 'jiangyuesong' 54 | email 'jyuesong@gmail.com' 55 | } 56 | } 57 | scm { 58 | connection gitUrl 59 | developerConnection gitUrl 60 | url siteUrl 61 | } 62 | } 63 | } 64 | } 65 | } 66 | task sourcesJar(type: Jar) { 67 | from android.sourceSets.main.java.srcDirs 68 | classifier = 'sources' 69 | } 70 | 71 | artifacts { 72 | archives sourcesJar 73 | } 74 | Properties properties = new Properties() 75 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 76 | bintray { 77 | user = properties.getProperty("bintray.user") 78 | key = properties.getProperty("bintray.apikey") 79 | configurations = ['archives'] 80 | pkg { 81 | repo = "maven" 82 | name = "ScalingIndicator" //发布到JCenter上的项目名字 83 | websiteUrl = siteUrl 84 | vcsUrl = gitUrl 85 | licenses = ["Apache-2.0"] 86 | publish = true 87 | 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /indicator/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 /Users/jiang/androidsdk/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 | -------------------------------------------------------------------------------- /indicator/src/androidTest/java/com/jiang/android/indicator/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.indicator; 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 | } -------------------------------------------------------------------------------- /indicator/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/IcsLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.indicator; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.drawable.Drawable; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | 10 | /** 11 | * A simple extension of a regular linear layout that supports the divider API 12 | * of Android 4.0+. The dividers are added adjacent to the children by changing 13 | * their layout params. If you need to rely on the margins which fall in the 14 | * same orientation as the layout you should wrap the child in a simple 15 | * {@link android.widget.FrameLayout} so it can receive the margin. 16 | */ 17 | public class IcsLinearLayout extends LinearLayout { 18 | private static final int[] LL = new int[] { 19 | /* 0 */ android.R.attr.divider, 20 | /* 1 */ android.R.attr.showDividers, 21 | /* 2 */ android.R.attr.dividerPadding, 22 | }; 23 | private static final int LL_DIVIDER = 0; 24 | private static final int LL_SHOW_DIVIDER = 1; 25 | private static final int LL_DIVIDER_PADDING = 2; 26 | 27 | private Drawable mDivider; 28 | private int mDividerWidth; 29 | private int mDividerHeight; 30 | private int mShowDividers; 31 | private int mDividerPadding; 32 | 33 | 34 | public IcsLinearLayout(Context context, int themeAttr) { 35 | super(context); 36 | 37 | TypedArray a = context.obtainStyledAttributes(null, LL, themeAttr, 0); 38 | setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER)); 39 | mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0); 40 | mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE); 41 | a.recycle(); 42 | } 43 | 44 | public void setDividerDrawable(Drawable divider) { 45 | if (divider == mDivider) { 46 | return; 47 | } 48 | mDivider = divider; 49 | if (divider != null) { 50 | mDividerWidth = divider.getIntrinsicWidth(); 51 | mDividerHeight = divider.getIntrinsicHeight(); 52 | } else { 53 | mDividerWidth = 0; 54 | mDividerHeight = 0; 55 | } 56 | setWillNotDraw(divider == null); 57 | requestLayout(); 58 | } 59 | 60 | @Override 61 | protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 62 | final int index = indexOfChild(child); 63 | final int orientation = getOrientation(); 64 | final LayoutParams params = (LayoutParams) child.getLayoutParams(); 65 | if (hasDividerBeforeChildAt(index)) { 66 | if (orientation == VERTICAL) { 67 | //Account for the divider by pushing everything up 68 | params.topMargin = mDividerHeight; 69 | } else { 70 | //Account for the divider by pushing everything left 71 | params.leftMargin = mDividerWidth; 72 | } 73 | } 74 | 75 | final int count = getChildCount(); 76 | if (index == count - 1) { 77 | if (hasDividerBeforeChildAt(count)) { 78 | if (orientation == VERTICAL) { 79 | params.bottomMargin = mDividerHeight; 80 | } else { 81 | params.rightMargin = mDividerWidth; 82 | } 83 | } 84 | } 85 | super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); 86 | } 87 | 88 | @Override 89 | protected void onDraw(Canvas canvas) { 90 | if (mDivider != null) { 91 | if (getOrientation() == VERTICAL) { 92 | drawDividersVertical(canvas); 93 | } else { 94 | drawDividersHorizontal(canvas); 95 | } 96 | } 97 | super.onDraw(canvas); 98 | } 99 | 100 | private void drawDividersVertical(Canvas canvas) { 101 | final int count = getChildCount(); 102 | for (int i = 0; i < count; i++) { 103 | final View child = getChildAt(i); 104 | 105 | if (child != null && child.getVisibility() != GONE) { 106 | if (hasDividerBeforeChildAt(i)) { 107 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 108 | final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/; 109 | drawHorizontalDivider(canvas, top); 110 | } 111 | } 112 | } 113 | 114 | if (hasDividerBeforeChildAt(count)) { 115 | final View child = getChildAt(count - 1); 116 | int bottom = 0; 117 | if (child == null) { 118 | bottom = getHeight() - getPaddingBottom() - mDividerHeight; 119 | } else { 120 | //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 121 | bottom = child.getBottom()/* + lp.bottomMargin*/; 122 | } 123 | drawHorizontalDivider(canvas, bottom); 124 | } 125 | } 126 | 127 | private void drawDividersHorizontal(Canvas canvas) { 128 | final int count = getChildCount(); 129 | for (int i = 0; i < count; i++) { 130 | final View child = getChildAt(i); 131 | 132 | if (child != null && child.getVisibility() != GONE) { 133 | if (hasDividerBeforeChildAt(i)) { 134 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 135 | final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/; 136 | drawVerticalDivider(canvas, left); 137 | } 138 | } 139 | } 140 | 141 | if (hasDividerBeforeChildAt(count)) { 142 | final View child = getChildAt(count - 1); 143 | int right = 0; 144 | if (child == null) { 145 | right = getWidth() - getPaddingRight() - mDividerWidth; 146 | } else { 147 | //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 148 | right = child.getRight()/* + lp.rightMargin*/; 149 | } 150 | drawVerticalDivider(canvas, right); 151 | } 152 | } 153 | 154 | private void drawHorizontalDivider(Canvas canvas, int top) { 155 | mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, 156 | getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); 157 | mDivider.draw(canvas); 158 | } 159 | 160 | private void drawVerticalDivider(Canvas canvas, int left) { 161 | mDivider.setBounds(left, getPaddingTop() + mDividerPadding, 162 | left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); 163 | mDivider.draw(canvas); 164 | } 165 | 166 | private boolean hasDividerBeforeChildAt(int childIndex) { 167 | if (childIndex == 0 || childIndex == getChildCount()) { 168 | return false; 169 | } 170 | if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { 171 | boolean hasVisibleViewBefore = false; 172 | for (int i = childIndex - 1; i >= 0; i--) { 173 | if (getChildAt(i).getVisibility() != GONE) { 174 | hasVisibleViewBefore = true; 175 | break; 176 | } 177 | } 178 | return hasVisibleViewBefore; 179 | } 180 | return false; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/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 com.jiang.android.indicator; 19 | 20 | 21 | import android.support.v4.view.ViewPager; 22 | 23 | /** 24 | * A PageIndicator is responsible to show an visual indicator on the total views 25 | * number and the current visible view. 26 | */ 27 | public interface PageIndicator extends ViewPager.OnPageChangeListener { 28 | /** 29 | * Bind the indicator to a ViewPager. 30 | * 31 | * @param view 32 | */ 33 | void setViewPager(ViewPager view); 34 | 35 | /** 36 | * Bind the indicator to a ViewPager. 37 | * 38 | * @param view 39 | * @param initialPosition 40 | */ 41 | void setViewPager(ViewPager view, int initialPosition); 42 | 43 | /** 44 | *

Set the current page of both the ViewPager and indicator.

45 | * 46 | *

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

48 | * 49 | * @param item 50 | */ 51 | void setCurrentItem(int item); 52 | 53 | /** 54 | * Set a page change listener which will receive forwarded events. 55 | * 56 | * @param listener 57 | */ 58 | void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); 59 | 60 | /** 61 | * Notify the indicator that the fragment list has changed. 62 | */ 63 | void notifyDataSetChanged(); 64 | } 65 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/TabPageIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 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 com.jiang.android.indicator; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.graphics.Canvas; 22 | import android.graphics.Paint; 23 | import android.graphics.drawable.ShapeDrawable; 24 | import android.graphics.drawable.shapes.OvalShape; 25 | import android.os.Build; 26 | import android.support.v4.view.PagerAdapter; 27 | import android.support.v4.view.ViewPager; 28 | import android.util.AttributeSet; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.widget.HorizontalScrollView; 32 | import android.widget.LinearLayout; 33 | import android.widget.TextView; 34 | 35 | import com.jiang.android.indicator.adapter.IconPagerAdapter; 36 | import com.jiang.android.indicator.adapter.ModelFragmentPagerAdapter; 37 | import com.jiang.android.indicator.model.PageModel; 38 | 39 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 40 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 41 | 42 | /** 43 | * This widget implements the dynamic action bar tab behavior that can change 44 | * across different configurations or circumstances. 45 | */ 46 | public class TabPageIndicator extends HorizontalScrollView implements PageIndicator { 47 | /** 48 | * Title text used when no title is provided by the adapter. 49 | */ 50 | private static final CharSequence EMPTY_TITLE = ""; 51 | private int oldSelected; 52 | private int mAllWidth; 53 | private int mAllHeight; 54 | 55 | /** 56 | * Interface for a callback when the selected tab has been reselected. 57 | */ 58 | public interface OnTabReselectedListener { 59 | /** 60 | * Callback when the selected tab has been reselected. 61 | * 62 | * @param position Position of the current center item. 63 | */ 64 | void onTabReselected(int position); 65 | } 66 | 67 | private Runnable mTabSelector; 68 | 69 | private final OnClickListener mTabClickListener = new OnClickListener() { 70 | public void onClick(View view) { 71 | TabView tabView = (TabView) view; 72 | oldSelected = mViewPager.getCurrentItem(); 73 | final int newSelected = tabView.getIndex(); 74 | mViewPager.setCurrentItem(newSelected); 75 | if (oldSelected == newSelected && mTabReselectedListener != null) { 76 | mTabReselectedListener.onTabReselected(newSelected); 77 | } 78 | } 79 | }; 80 | 81 | private final IcsLinearLayout mTabLayout; 82 | 83 | private ViewPager mViewPager; 84 | private ViewPager.OnPageChangeListener mListener; 85 | 86 | private int mMaxTabWidth; 87 | private int mSelectedTabIndex; 88 | 89 | private OnTabReselectedListener mTabReselectedListener; 90 | 91 | 92 | public TabPageIndicator(Context context) { 93 | this(context, null); 94 | } 95 | 96 | public TabPageIndicator(Context context, AttributeSet attrs) { 97 | super(context, attrs); 98 | setHorizontalScrollBarEnabled(false); 99 | 100 | mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle); 101 | addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT)); 102 | 103 | } 104 | 105 | public void setOnTabReselectedListener(OnTabReselectedListener listener) { 106 | mTabReselectedListener = listener; 107 | } 108 | 109 | @Override 110 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 111 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 112 | final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY; 113 | setFillViewport(lockedExpanded); 114 | 115 | final int childCount = mTabLayout.getChildCount(); 116 | if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { 117 | if (childCount > 2) { 118 | mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) / 3); 119 | } else { 120 | mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; 121 | } 122 | } else { 123 | mMaxTabWidth = -1; 124 | } 125 | 126 | final int oldWidth = getMeasuredWidth(); 127 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 128 | final int newWidth = getMeasuredWidth(); 129 | 130 | if (lockedExpanded && oldWidth != newWidth) { 131 | // Recenter the tab display if we're at a new (scrollable) size. 132 | setCurrentItem(mSelectedTabIndex); 133 | } 134 | 135 | mAllWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); 136 | mAllHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); 137 | } 138 | 139 | private void animateToTab(final int position) { 140 | TabView oldView = (TabView) mTabLayout.getChildAt(oldSelected); 141 | oldView.setTextSize(getNormalSize()); 142 | oldView.setTextColor(getContext().getResources().getColor(R.color.normal_item_textcolor)); 143 | final TabView tabView = (TabView) mTabLayout.getChildAt(position); 144 | tabView.setTextColor(getContext().getResources().getColor(R.color.selected_item_textcolor)); 145 | tabView.setTextSize(getSelectedSize()); 146 | 147 | 148 | if (mTabSelector != null) { 149 | removeCallbacks(mTabSelector); 150 | } 151 | mTabSelector = new Runnable() { 152 | public void run() { 153 | final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; 154 | smoothScrollTo(scrollPos, 0); 155 | mTabSelector = null; 156 | } 157 | }; 158 | post(mTabSelector); 159 | } 160 | 161 | private float getSelectedSize() { 162 | float size = getContext().getResources().getDimension(R.dimen.selected_item_textsize); 163 | return px2sp(getContext(), size); 164 | } 165 | 166 | private float getNormalSize() { 167 | float size = getContext().getResources().getDimension(R.dimen.normal_item_textsize); 168 | return px2sp(getContext(), size); 169 | } 170 | 171 | public static int px2sp(Context context, float pxValue) { 172 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 173 | return (int) (pxValue / fontScale + 0.5f); 174 | } 175 | 176 | @Override 177 | public void onAttachedToWindow() { 178 | super.onAttachedToWindow(); 179 | if (mTabSelector != null) { 180 | // Re-post the selector we saved 181 | post(mTabSelector); 182 | } 183 | } 184 | 185 | @Override 186 | public void onDetachedFromWindow() { 187 | super.onDetachedFromWindow(); 188 | if (mTabSelector != null) { 189 | removeCallbacks(mTabSelector); 190 | } 191 | } 192 | 193 | private void addTab(int index, CharSequence text, int count, int iconResId) { 194 | final TabView tabView = new TabView(getContext()); 195 | tabView.mIndex = index; 196 | tabView.setFocusable(true); 197 | tabView.setOnClickListener(mTabClickListener); 198 | tabView.setText(text); 199 | if (count > 0) { 200 | tabView.setNeedPoint(true); 201 | } else { 202 | tabView.setNeedPoint(false); 203 | } 204 | if (iconResId != 0) { 205 | tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0); 206 | } 207 | 208 | // mTabLayout.addView(tabView); 209 | mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1)); 210 | } 211 | 212 | @Override 213 | public void onPageScrollStateChanged(int arg0) { 214 | 215 | if (arg0 == ViewPager.SCROLL_STATE_DRAGGING) { 216 | oldSelected = mViewPager.getCurrentItem(); 217 | } 218 | 219 | if (mListener != null) { 220 | mListener.onPageScrollStateChanged(arg0); 221 | } 222 | } 223 | 224 | @Override 225 | public void onPageScrolled(int arg0, float arg1, int arg2) { 226 | if (mListener != null) { 227 | mListener.onPageScrolled(arg0, arg1, arg2); 228 | } 229 | } 230 | 231 | @Override 232 | public void onPageSelected(int arg0) { 233 | setCurrentItem(arg0); 234 | if (mListener != null) { 235 | mListener.onPageSelected(arg0); 236 | } 237 | } 238 | 239 | @Override 240 | public void setViewPager(ViewPager view) { 241 | if (mViewPager == view) { 242 | return; 243 | } 244 | if (mViewPager != null) { 245 | mViewPager.setOnPageChangeListener(null); 246 | } 247 | final PagerAdapter adapter = view.getAdapter(); 248 | if (adapter == null) { 249 | throw new IllegalStateException("ViewPager does not have adapter instance."); 250 | } 251 | mViewPager = view; 252 | view.setOnPageChangeListener(this); 253 | notifyDataSetChanged(); 254 | } 255 | 256 | @Override 257 | protected void dispatchDraw(Canvas canvas) { 258 | super.dispatchDraw(canvas); 259 | } 260 | 261 | public void notifyDataSetChanged() { 262 | mTabLayout.removeAllViews(); 263 | ModelFragmentPagerAdapter adapter = (ModelFragmentPagerAdapter) mViewPager.getAdapter(); 264 | IconPagerAdapter iconAdapter = null; 265 | if (adapter instanceof IconPagerAdapter) { 266 | iconAdapter = (IconPagerAdapter) adapter; 267 | } 268 | final int count = adapter.getCount(); 269 | for (int i = 0; i < count; i++) { 270 | PageModel title = adapter.getPageModel(i); 271 | if (title == null) { 272 | title.setName(EMPTY_TITLE); 273 | } 274 | int iconResId = 0; 275 | if (iconAdapter != null) { 276 | iconResId = iconAdapter.getIconResId(i); 277 | } 278 | addTab(i, title.getName(), title.getCount(), iconResId); 279 | } 280 | if (mSelectedTabIndex > count) { 281 | mSelectedTabIndex = count - 1; 282 | } 283 | setCurrentItem(mSelectedTabIndex); 284 | requestLayout(); 285 | } 286 | 287 | @Override 288 | public void setViewPager(ViewPager view, int initialPosition) { 289 | setViewPager(view); 290 | setCurrentItem(initialPosition); 291 | } 292 | 293 | @Override 294 | public void setCurrentItem(int item) { 295 | if (mViewPager == null) { 296 | throw new IllegalStateException("ViewPager has not been bound."); 297 | } 298 | mSelectedTabIndex = item; 299 | 300 | mViewPager.setCurrentItem(item); 301 | 302 | final int tabCount = mTabLayout.getChildCount(); 303 | for (int i = 0; i < tabCount; i++) { 304 | final View child = mTabLayout.getChildAt(i); 305 | final boolean isSelected = (i == item); 306 | child.setSelected(isSelected); 307 | if (isSelected) { 308 | animateToTab(item); 309 | } 310 | } 311 | } 312 | 313 | @Override 314 | public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { 315 | mListener = listener; 316 | } 317 | 318 | private class TabView extends TextView { 319 | private int mIndex; 320 | private Paint paint; 321 | private boolean needPoint; 322 | private int RADIUS; 323 | private int PADDING; 324 | private final ShapeDrawable drawable; 325 | 326 | public boolean isNeedPoint() { 327 | return needPoint; 328 | } 329 | 330 | public void setNeedPoint(boolean needPoint) { 331 | this.needPoint = needPoint; 332 | invalidate(); 333 | } 334 | 335 | public TabView(Context context) { 336 | super(context, null, R.attr.vpiTabPageIndicatorStyle); 337 | paint = new Paint(); 338 | paint.setStyle(Paint.Style.FILL); 339 | paint.setColor(getContext().getResources().getColor(R.color.selected_item_textcolor)); 340 | RADIUS = dip2px(context, 4); 341 | PADDING = dip2px(context, 13); 342 | drawable = new ShapeDrawable(new OvalShape()); 343 | } 344 | 345 | @Override 346 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 347 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 348 | 349 | // Re-measure if we went beyond our maximum size. 350 | if (mMaxTabWidth > 0 && getMeasuredWidth() <= mMaxTabWidth) { 351 | super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), 352 | heightMeasureSpec); 353 | } 354 | } 355 | 356 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) 357 | public void callOnClick(int item) { 358 | if (mTabLayout.getChildCount() > item) { 359 | mTabLayout.getChildAt(item).callOnClick(); 360 | } 361 | 362 | } 363 | 364 | public int getIndex() { 365 | return mIndex; 366 | } 367 | 368 | 369 | @Override 370 | protected void onDraw(Canvas canvas) { 371 | super.onDraw(canvas); 372 | int x = (int) (getWidth() * (float) (4 * 1.0 / 5)); 373 | int y = PADDING; 374 | if (needPoint) { 375 | //canvas.drawCircle(x, y, RADIUS, paint); 376 | drawable.getPaint().setColor(getResources().getColor(R.color.selected_item_textcolor)); 377 | drawable.setBounds(x, y, x + RADIUS * 2, y + RADIUS * 2); 378 | drawable.draw(canvas); 379 | } 380 | } 381 | 382 | public int dip2px(Context context, float dipValue) { 383 | final float scale = context.getResources().getDisplayMetrics().density; 384 | return (int) (dipValue * scale + 0.5f); 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/adapter/IconPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.indicator.adapter; 2 | 3 | public interface IconPagerAdapter { 4 | /** 5 | * Get icon representing the page at {@code index} in the adapter. 6 | */ 7 | int getIconResId(int index); 8 | 9 | // From PagerAdapter 10 | int getCount(); 11 | } 12 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/adapter/ModelFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * created by jiang, 16/2/26 3 | * Copyright (c) 2016, jyuesong@gmail.com All Rights Reserved. 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | */ 28 | 29 | package com.jiang.android.indicator.adapter; 30 | 31 | 32 | import android.support.v4.app.FragmentManager; 33 | import android.support.v4.app.FragmentPagerAdapter; 34 | 35 | import com.jiang.android.indicator.model.PageModel; 36 | 37 | 38 | /** 39 | * Created by jiang on 16/2/26. 40 | */ 41 | public abstract class ModelFragmentPagerAdapter extends FragmentPagerAdapter { 42 | 43 | 44 | public ModelFragmentPagerAdapter(FragmentManager fm) { 45 | super(fm); 46 | } 47 | 48 | 49 | @Override 50 | public CharSequence getPageTitle(int position) { 51 | getPageModel(position); 52 | return super.getPageTitle(position); 53 | } 54 | 55 | public abstract PageModel getPageModel(int position); 56 | } 57 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/model/PageModel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * created by jiang, 16/2/26 3 | * Copyright (c) 2016, jyuesong@gmail.com All Rights Reserved. 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | */ 28 | 29 | package com.jiang.android.indicator.model; 30 | 31 | import java.io.Serializable; 32 | 33 | /** 34 | * Created by jiang on 16/2/26. 35 | */ 36 | public class PageModel implements Serializable { 37 | private CharSequence name; 38 | private int count; 39 | 40 | 41 | public PageModel() { 42 | } 43 | 44 | 45 | 46 | public PageModel(String name, int count) { 47 | this.name = name; 48 | this.count = count; 49 | } 50 | 51 | public CharSequence getName() { 52 | return name; 53 | } 54 | 55 | public void setName(CharSequence name) { 56 | this.name = name; 57 | } 58 | 59 | public int getCount() { 60 | return count; 61 | } 62 | 63 | public void setCount(int count) { 64 | this.count = count; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /indicator/src/main/res/color/vpi__dark_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /indicator/src/main/res/color/vpi__light_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /indicator/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 14sp 7 | 18sp 8 | 9 | 10 | -------------------------------------------------------------------------------- /indicator/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Indicator 3 | 4 | -------------------------------------------------------------------------------- /indicator/src/main/res/values/vpi__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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /indicator/src/main/res/values/vpi__colors.xml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | #ff000000 18 | #fff3f3f3 19 | @color/vpi__background_holo_light 20 | @color/vpi__background_holo_dark 21 | #ff4c4c4c 22 | #ffb2b2b2 23 | 24 | @color/vpi__bright_foreground_holo_light 25 | 26 | 27 | @color/vpi__bright_foreground_holo_dark 28 | 29 | 30 | #9b9b9b 31 | #D63E3E 32 | 33 | -------------------------------------------------------------------------------- /indicator/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 | 12dp 28 | 4dp 29 | 1dp 30 | #FF33B5E5 31 | #FFBBBBBB 32 | true 33 | 34 | 4dp 35 | #FF33B5E5 36 | 2dp 37 | 2 38 | 4dp 39 | 20dp 40 | 7dp 41 | 0 42 | #FFFFFFFF 43 | true 44 | #BBFFFFFF 45 | 15dp 46 | 5dp 47 | 7dp 48 | 49 | true 50 | 300 51 | 400 52 | #FF33B5E5 53 | -------------------------------------------------------------------------------- /indicator/src/main/res/values/vpi__styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 22 | 23 | 25 | 26 | 36 | 37 | 41 | 42 | 46 | 47 | -------------------------------------------------------------------------------- /indicator/src/test/java/com/jiang/android/indicator/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.indicator; 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 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':indicator' 2 | --------------------------------------------------------------------------------