├── AnimationBanner
├── .idea
│ ├── .name
│ ├── copyright
│ │ └── profiles_settings.xml
│ ├── vcs.xml
│ ├── encodings.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ ├── compiler.xml
│ ├── gradle.xml
│ └── misc.xml
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── banner1.jpg
│ │ │ │ │ ├── banner2.png
│ │ │ │ │ ├── banner3.jpg
│ │ │ │ │ ├── banner4.png
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-v21
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-w820dp
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── menu
│ │ │ │ │ └── menu_example.xml
│ │ │ │ └── layout
│ │ │ │ │ ├── content_example.xml
│ │ │ │ │ └── activity_example.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── me
│ │ │ │ └── geed
│ │ │ │ ├── animationbanner
│ │ │ │ └── ExampleActivity.java
│ │ │ │ └── widget
│ │ │ │ └── animationbanner
│ │ │ │ └── AnimationBanner.java
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── me
│ │ │ │ └── geed
│ │ │ │ └── animationbanner
│ │ │ │ └── ExampleUnitTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── me
│ │ │ └── geed
│ │ │ └── animationbanner
│ │ │ └── ApplicationTest.java
│ ├── proguard-rules.pro
│ └── build.gradle
├── settings.gradle
├── .gitignore
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── build.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
├── images
└── AnimationBanner.gif
├── .gitignore
└── README.md
/AnimationBanner/.idea/.name:
--------------------------------------------------------------------------------
1 | AnimationBanner
--------------------------------------------------------------------------------
/AnimationBanner/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/AnimationBanner/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/images/AnimationBanner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/images/AnimationBanner.gif
--------------------------------------------------------------------------------
/AnimationBanner/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/AnimationBanner/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/AnimationBanner/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-hdpi/banner1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-hdpi/banner1.jpg
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-hdpi/banner2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-hdpi/banner2.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-hdpi/banner3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-hdpi/banner3.jpg
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-hdpi/banner4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-hdpi/banner4.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ASPOOK/Android-AnimationBanner/HEAD/AnimationBanner/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AnimationBanner
3 | Settings
4 |
5 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/AnimationBanner/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
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.8-all.zip
7 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 | >
2 |
8 |
9 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/test/java/me/geed/animationbanner/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.geed.animationbanner;
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 | }
--------------------------------------------------------------------------------
/AnimationBanner/app/src/androidTest/java/me/geed/animationbanner/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package me.geed.animationbanner;
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 | }
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/menu/menu_example.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/AnimationBanner/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 |
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 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-AnimationBanner
2 |
3 | ##控件简介
4 | AnimationBanner是一个利用属性动画原理实现的横幅广告控件。
5 |
6 | ##控件特点
7 | 1.实现原理简单,使用方便
8 | 2.可灵活设置Banner数量,1张或N张
9 | 3.附带Indicator小圆点指示器,并自动联动
10 | 4.可指定Indicator指示器在Banner中的位置
11 | 5.可自定义Indicator小圆点半径,处于当前位置或非当前位置时的颜色
12 | 6.可配置动画持续时间来调整Banner滑动的速度
13 | 7.可自定义Banner的大小以适配图片
14 | 8.可响应Banner滑动与点击事件
15 |
16 | ##实现原理
17 | 请参考之前的一篇博客 [利用Android属性动画实现Banner的原理与实践](http://blog.csdn.net/ahence/article/details/46646559)
18 |
19 | ##效果演示
20 | 
21 |
22 |
--------------------------------------------------------------------------------
/AnimationBanner/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 D:\Android\android-sdk-windows/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 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/AnimationBanner/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 "me.geed.animationbanner"
9 | minSdkVersion 11
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(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.+'
26 | compile 'com.android.support:design:23.+'
27 | }
28 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/AnimationBanner/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
19 | org.gradle.jvmargs=-Xmx1024m
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/layout/content_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/res/layout/activity_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/AnimationBanner/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 |
--------------------------------------------------------------------------------
/AnimationBanner/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
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 | 1.7
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/java/me/geed/animationbanner/ExampleActivity.java:
--------------------------------------------------------------------------------
1 | package me.geed.animationbanner;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.design.widget.FloatingActionButton;
6 | import android.support.design.widget.Snackbar;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.widget.ImageView;
13 | import android.widget.Toast;
14 |
15 | import java.util.List;
16 |
17 | import me.geed.widget.animationbanner.AnimationBanner;
18 |
19 | public class ExampleActivity extends AppCompatActivity {
20 | private AnimationBanner mBanner;
21 | private List mImageViews;
22 |
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_example);
28 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
29 | setSupportActionBar(toolbar);
30 |
31 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
32 | fab.setOnClickListener(new View.OnClickListener() {
33 | @Override
34 | public void onClick(View view) {
35 | Snackbar.make(view, "Author Email:yourswee@gmail.com", Snackbar.LENGTH_LONG)
36 | .setAction("Action", null).show();
37 | }
38 | });
39 |
40 | showAnimationBanner();
41 | }
42 |
43 | @Override
44 | public void onWindowFocusChanged(boolean hasFocus) {
45 | super.onWindowFocusChanged(hasFocus);
46 | }
47 |
48 | @Override
49 | public boolean onCreateOptionsMenu(Menu menu) {
50 | // Inflate the menu; this adds items to the action bar if it is present.
51 | getMenuInflater().inflate(R.menu.menu_example, menu);
52 | return true;
53 | }
54 |
55 | @Override
56 | public boolean onOptionsItemSelected(MenuItem item) {
57 | // Handle action bar item clicks here. The action bar will
58 | // automatically handle clicks on the Home/Up button, so long
59 | // as you specify a parent activity in AndroidManifest.xml.
60 | int id = item.getItemId();
61 |
62 | //noinspection SimplifiableIfStatement
63 | if (id == R.id.action_settings) {
64 | return true;
65 | }
66 |
67 | return super.onOptionsItemSelected(item);
68 | }
69 |
70 | /**
71 | * 显示自定义Banner控件
72 | */
73 | public void showAnimationBanner() {
74 | /**
75 | * 注意各方法的调用顺序
76 | */
77 | mBanner = (AnimationBanner) findViewById(R.id.banner);
78 | mBanner.setBannerNum(4);
79 |
80 | /**
81 | * Indicator默认位置为右下方
82 | */
83 | //mBanner.setIndicatorPosition(AnimationBanner.IndicatorPosition.CENTER_BOTTOM);
84 |
85 | /**
86 | * 设置Indicator小圆点半径
87 | */
88 | //mBanner.setIndicatorRaidus(20);
89 |
90 | /**
91 | * 设置Indicator小圆点处于当前位置时的颜色
92 | */
93 | //mBanner.setSelectedColor(Color.RED);
94 |
95 | /**
96 | * 设置Indicator小圆点非当前位置时的颜色
97 | */
98 | //mBanner.setUnSelectedColor(Color.BLUE);
99 |
100 | /**
101 | * 动画持续时间默认为500毫秒
102 | */
103 | //mBanner.setAnimDuration(400);
104 |
105 | mBanner.initView();
106 |
107 | /**
108 | * 根据所下载图片的实际宽高来设置Banner的宽高,按比例显示图片;默认宽度为屏幕宽度
109 | * 需在initView之后,下载图片之前调用,否则无效
110 | * 本例中的图片为640*210
111 | */
112 | mBanner.setBannerSize(640 / 210);
113 |
114 | /**
115 | * 根据所下载图片的实际宽高来设置Banner的宽高,按比例显示图片;设置自定义宽度
116 | * 需在initView之后,下载图片之前调用,否则无效
117 | */
118 | //mBanner.setBannerSize(640 / 210, 800);
119 |
120 | /**
121 | * 先初始化才能获取里面的ImageViews
122 | * 此处的示例为本地图片模拟,通常情况下为网络下载图片,如使用Glide等图片库,同时会将图片缓存机制添加到其中
123 | */
124 | mImageViews = mBanner.getBannerImageViews();
125 | mImageViews.get(0).setImageResource(R.mipmap.banner1);
126 | mImageViews.get(1).setImageResource(R.mipmap.banner2);
127 | mImageViews.get(2).setImageResource(R.mipmap.banner3);
128 | mImageViews.get(3).setImageResource(R.mipmap.banner4);
129 |
130 | /**
131 | * Banner点击响应交给使用者来实现
132 | */
133 | mBanner.setOnBannerClickListener(new AnimationBanner.OnBannerClickListener() {
134 | @Override
135 | public void onClick(int bannerIndex) {
136 | Toast.makeText(ExampleActivity.this, "Banner Clicked! index:" + bannerIndex, Toast.LENGTH_SHORT).show();
137 | }
138 | });
139 |
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/AnimationBanner/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 |
--------------------------------------------------------------------------------
/AnimationBanner/app/src/main/java/me/geed/widget/animationbanner/AnimationBanner.java:
--------------------------------------------------------------------------------
1 | package me.geed.widget.animationbanner;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.content.Context;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.util.AttributeSet;
10 | import android.util.DisplayMetrics;
11 | import android.view.Gravity;
12 | import android.view.MotionEvent;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.WindowManager;
16 | import android.view.animation.LinearInterpolator;
17 | import android.widget.FrameLayout;
18 | import android.widget.ImageView;
19 | import android.widget.LinearLayout;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | /**
25 | * AnimationBanner
26 | *
27 | * Created by Andy on 2015/11/30.
28 | * Author Email:yourswee@gmail.com
29 | */
30 | public class AnimationBanner extends FrameLayout {
31 | /**
32 | * 上下文对象
33 | */
34 | private Context mContext;
35 |
36 | /**
37 | * Banner横幅数量,必须大于等于1
38 | */
39 | private int bannerNum;
40 |
41 | /**
42 | * 默认滑动动画时间,默认500
43 | */
44 | private int mDuration = 500;
45 |
46 | /**
47 | * 当前显示Banner条幅的索引
48 | */
49 | private int mCurrentBannerIndex = 0;
50 |
51 | /**
52 | * Banner点击事件监听器
53 | */
54 | private OnBannerClickListener mListener;
55 |
56 | /**
57 | * 存储所有的Banner横幅
58 | */
59 | private List mImageViews;
60 |
61 | /**
62 | * 存储所有的Indicator
63 | */
64 | private List mIndicators;
65 |
66 | /**
67 | * Indicator小圆点指示器位置,默认在右下方
68 | */
69 | private int mIndicatorPosition = IndicatorPosition.RIGHT_BOTTOM;
70 |
71 | /**
72 | * 指示器小圆点半径,默认为10
73 | */
74 | private int mRadius = 10;
75 |
76 | /**
77 | * 小圆点处于当前位置时的颜色
78 | */
79 | private int mSelectedColor = Color.WHITE;
80 |
81 | /**
82 | * 小圆点非当前位置时的颜色
83 | */
84 | private int mUnSelectedColor = Color.GRAY;
85 |
86 | /**
87 | * 是否只有一张广告横幅
88 | */
89 | private boolean isOnlyOne = false;
90 |
91 | /**
92 | * 动画是否正在进行状态
93 | */
94 | private boolean isAnimationing = false;
95 |
96 | /**
97 | * 是否自动循环播放,默认false
98 | */
99 | private boolean isAutoSwitch = false;
100 |
101 | /**
102 | * 自动播放动画器
103 | */
104 | private ObjectAnimator autoAnimator;
105 |
106 | /**
107 | * 用于Touch监听的初始坐标
108 | */
109 | private float firstX = 0;
110 | private float firstY = 0;
111 |
112 | /**
113 | * 默认距离阈值,大于此值则认为是滑动
114 | */
115 | private static final int DISTANCE_THRESHOLD = 30;
116 |
117 | public AnimationBanner(Context context) {
118 | this(context, null);
119 | }
120 |
121 | public AnimationBanner(Context context, AttributeSet attrs) {
122 | super(context, attrs);
123 | mContext = context;
124 | }
125 |
126 | public void initView() {
127 | if (bannerNum <= 0) {
128 | return;
129 | }
130 |
131 | if (bannerNum == 1) {
132 | isOnlyOne = true;
133 | }
134 | /**
135 | * 添加Banner布局
136 | */
137 | LinearLayout mBannerContainer = getBannerContainer();
138 | if (mBannerContainer != null) {
139 | addView(mBannerContainer);
140 | }
141 |
142 | /**
143 | * 添加指示器小圆点布局
144 | */
145 | LinearLayout mIndicatorContainer = getIndicatorContainer();
146 | if (mIndicatorContainer != null) {
147 | addView(mIndicatorContainer);
148 | }
149 |
150 | /**
151 | * 或者在xml布局文件中设置,否则无法响应ACTION_MOVE和ACTION_UP
152 | */
153 | setClickable(true);
154 | }
155 |
156 |
157 | @Override
158 | public boolean onTouchEvent(MotionEvent event) {
159 | /**
160 | * 在此处理左右滑动响应
161 | */
162 | int action = event.getAction();
163 | float currentX, currentY;
164 |
165 | switch (action) {
166 | case MotionEvent.ACTION_DOWN:
167 | firstX = event.getX();
168 | firstY = event.getY();
169 | break;
170 | case MotionEvent.ACTION_MOVE:
171 | break;
172 | case MotionEvent.ACTION_UP:
173 | currentX = event.getX();
174 | currentY = event.getY();
175 | if (Math.abs(currentX - firstX) > Math.abs(currentY - firstY)) {
176 | if (!isOnlyOne) {
177 | if (currentX - firstX > 0) {
178 | /**
179 | }
180 | * 向右滑动,如果处于动画状态,则不响应本次滑动
181 | */
182 | if (!isAnimationing) {
183 | doRightAnimation();
184 | }
185 | } else {
186 | /**
187 | * 向左滑动,如果处于动画状态,则不响应本次滑动
188 | */
189 | if (!isAnimationing) {
190 | doLeftAnimation();
191 | }
192 | }
193 | }
194 | } else if (Math.abs(currentX - firstX) < DISTANCE_THRESHOLD && Math.abs(currentY - firstY) < DISTANCE_THRESHOLD) {
195 | /**
196 | * 响应点击,如正在滑动状态则不响应
197 | */
198 | if (mListener != null) {
199 | if (!isAnimationing) {
200 | mListener.onClick(mCurrentBannerIndex);
201 | }
202 | }
203 | }
204 | break;
205 | default:
206 | break;
207 | }
208 |
209 | return super.onTouchEvent(event);
210 | }
211 |
212 | @Override
213 | public boolean onInterceptTouchEvent(MotionEvent event) {
214 | /**
215 | * 拦截事件,在onTouchEvent中做处理
216 | */
217 | return true;
218 | }
219 |
220 | /**
221 | * 动态生成Banner视图
222 | *
223 | * @return
224 | */
225 | private LinearLayout getBannerContainer() {
226 | mImageViews = new ArrayList<>();
227 | LinearLayout mBannerContainer = new LinearLayout(mContext);
228 | mBannerContainer.setOrientation(LinearLayout.HORIZONTAL);
229 | mBannerContainer.setLayoutParams(new ViewGroup.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
230 | mBannerContainer.removeAllViews();
231 |
232 | for (int i = 0; i < bannerNum; i++) {
233 | ImageView imageView = new ImageView(mContext);
234 | imageView.setLayoutParams(new LinearLayout.LayoutParams(getScreenWidth(), LinearLayout.LayoutParams.WRAP_CONTENT));
235 | mBannerContainer.addView(imageView);
236 | mImageViews.add(imageView);
237 | }
238 |
239 | return mBannerContainer;
240 | }
241 |
242 | /**
243 | * 动态生成Indicator小圆点指示器,当Banner条幅数量为1时,不显示Indicator
244 | * 还可扩展,如在小圆点前面添加文本描述、添加透明背景等,可自由实现
245 | *
246 | * @return
247 | */
248 | private LinearLayout getIndicatorContainer() {
249 | if (isOnlyOne) {
250 | return null;
251 | }
252 |
253 | mIndicators = new ArrayList<>();
254 | LinearLayout mIndicatorContainer = new LinearLayout(mContext);
255 | mIndicatorContainer.setOrientation(LinearLayout.HORIZONTAL);
256 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
257 | switch (mIndicatorPosition) {
258 | case IndicatorPosition.CENTER_BOTTOM:
259 | params.gravity = Gravity.CENTER | Gravity.BOTTOM;
260 | break;
261 | case IndicatorPosition.LEFT_BOTTOM:
262 | params.gravity = Gravity.LEFT | Gravity.BOTTOM;
263 | break;
264 | case IndicatorPosition.RIGHT_BOTTOM:
265 | params.gravity = Gravity.RIGHT | Gravity.BOTTOM;
266 | break;
267 | default:
268 | break;
269 | }
270 | mIndicatorContainer.setLayoutParams(params);
271 | mIndicatorContainer.setPadding(50, 30, 50, 30);
272 | mIndicatorContainer.removeAllViews();
273 |
274 | for (int i = 0; i < bannerNum; i++) {
275 | PointIndicator indicator;
276 | if (i == 0) {
277 | indicator = new PointIndicator(mContext, mRadius, mSelectedColor);
278 | indicator.setLayoutParams(new LinearLayout.LayoutParams(mRadius * 3, mRadius * 2));
279 | } else {
280 | indicator = new PointIndicator(mContext, mRadius, mUnSelectedColor);
281 | indicator.setLayoutParams(new LinearLayout.LayoutParams(mRadius * 3, mRadius * 2));
282 | }
283 | mIndicatorContainer.addView(indicator);
284 | mIndicators.add(indicator);
285 | }
286 |
287 | return mIndicatorContainer;
288 | }
289 |
290 | /**
291 | * 设置动画持续时间,即Banner滑动的快慢
292 | *
293 | * @param millisecond
294 | */
295 | public void setAnimDuration(int millisecond) {
296 | mDuration = millisecond;
297 | }
298 |
299 | /**
300 | * 设置Banner条幅的数量,需要与实际提供的图片url数量一致
301 | *
302 | * @param num
303 | */
304 | public void setBannerNum(int num) {
305 | bannerNum = num;
306 | }
307 |
308 | /**
309 | * 设置指示器小圆点的显示位置
310 | * 有3种选择:IndicatorPosition.CENTER_BOTTOM、IndicatorPosition.RIGHT_BOTTOM、IndicatorPosition.LEFT_BOTTOM
311 | * 默认为RIGHT_BOTTOM
312 | *
313 | * @param position 位置常量
314 | */
315 | public void setIndicatorPosition(int position) {
316 | mIndicatorPosition = position;
317 | }
318 |
319 | /**
320 | * 设置指示器小圆点的半径
321 | *
322 | * @param radius
323 | */
324 | public void setIndicatorRaidus(int radius) {
325 | mRadius = radius;
326 | }
327 |
328 | /**
329 | * 设置小圆点处于当前位置的颜色
330 | *
331 | * @param selectedColor
332 | */
333 | public void setSelectedColor(int selectedColor) {
334 | mSelectedColor = selectedColor;
335 | }
336 |
337 | /**
338 | * 设置小圆点非当前位置的颜色
339 | *
340 | * @param unSelectedColor
341 | */
342 | public void setUnSelectedColor(int unSelectedColor) {
343 | mUnSelectedColor = unSelectedColor;
344 | }
345 |
346 | /**
347 | * 设置Banner的大小,并按比例适配图片,需在initView之后,下载图片之前调用,否则无效
348 | *
349 | * @param ratio 实际图片的宽与高之比
350 | * @param bannerWidth 最终需要显示的Banner宽度
351 | */
352 | public void setBannerSize(double ratio, int bannerWidth) {
353 | if (mImageViews == null || mImageViews.size() <= 0) {
354 | return;
355 | }
356 |
357 | if (ratio > 0 && bannerWidth > 0) {
358 | int bannerHeight = (int) (bannerWidth / ratio);
359 | for (ImageView imageView : mImageViews) {
360 | imageView.getLayoutParams().width = bannerWidth;
361 | imageView.getLayoutParams().height = bannerHeight;
362 | }
363 | getLayoutParams().width = bannerWidth;
364 | }
365 | }
366 |
367 | /**
368 | * 设置Banner的大小,并按比例适配图片,宽度默认为屏幕宽度,需在initView之后,下载图片之前调用,否则无效
369 | *
370 | * @param ratio 实际图片的宽与高之比
371 | */
372 | public void setBannerSize(double ratio) {
373 | int defaultBannerWidth = getScreenWidth();
374 | setBannerSize(ratio, defaultBannerWidth);
375 | }
376 |
377 | /**
378 | * 设置点击监听接口
379 | *
380 | * @param listener
381 | */
382 | public void setOnBannerClickListener(OnBannerClickListener listener) {
383 | this.mListener = listener;
384 | }
385 |
386 | /**
387 | * 提供获取ImageView的接口,供图片库下载图片使用
388 | *
389 | * @return
390 | */
391 |
392 | public List getBannerImageViews() {
393 | if (mImageViews != null) {
394 | return mImageViews;
395 | }
396 |
397 | return null;
398 | }
399 |
400 | /**
401 | * 获取设备屏幕宽度
402 | *
403 | * @return
404 | */
405 | private int getScreenWidth() {
406 | DisplayMetrics metrics = new DisplayMetrics();
407 | WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
408 | manager.getDefaultDisplay().getMetrics(metrics);
409 |
410 | return metrics.widthPixels;
411 | }
412 |
413 | /**
414 | * 处理左滑
415 | *
416 | * 需要单独对每个ImageView做动画,如果对ImageView的容器做动画,则循环时会有突兀的感觉
417 | */
418 | private void doLeftAnimation() {
419 | if (mCurrentBannerIndex == bannerNum - 1) {
420 | mCurrentBannerIndex = 0;
421 | } else {
422 | mCurrentBannerIndex += 1;
423 | }
424 |
425 | for (int i = 0; i < bannerNum; i++) {
426 | doLeftAnimation(mImageViews.get(i), i);
427 | }
428 | }
429 |
430 | /**
431 | * 左滑动画,左滑之后,将滑到左边看不到的广告位移动到右边末尾
432 | * Calls requires API level 11
433 | *
434 | * @param v
435 | */
436 | private void doLeftAnimation(final View v, final int index) {
437 | ObjectAnimator leftAnimator = ObjectAnimator.ofFloat(v, "x", v.getX(), v.getX() - v.getWidth());
438 | leftAnimator.setDuration(mDuration);
439 | leftAnimator.setInterpolator(new LinearInterpolator());
440 | leftAnimator.addListener(new Animator.AnimatorListener() {
441 | @Override
442 | public void onAnimationCancel(Animator animation) {
443 |
444 | }
445 |
446 | @Override
447 | public void onAnimationEnd(Animator animation) {
448 | isAnimationing = false;
449 | if (index == bannerNum - 1) {
450 | /**
451 | * 修改指示器小圆点状态
452 | */
453 | mIndicators.get(mCurrentBannerIndex).changeStatus(true);
454 | if (mCurrentBannerIndex != 0) {
455 | mIndicators.get(mCurrentBannerIndex - 1).changeStatus(false);
456 | } else {
457 | mIndicators.get(bannerNum - 1).changeStatus(false);
458 | }
459 | }
460 |
461 | /**
462 | * 将滑到屏幕左侧的视图移到右侧尾部
463 | */
464 | if (v.getX() < 0) {
465 | v.setX((bannerNum - 1) * v.getWidth());
466 | }
467 | }
468 |
469 | @Override
470 | public void onAnimationRepeat(Animator animation) {
471 |
472 | }
473 |
474 | @Override
475 | public void onAnimationStart(Animator animation) {
476 | isAnimationing = true;
477 | }
478 | });
479 | leftAnimator.start();
480 | }
481 |
482 | /**
483 | * 处理右滑
484 | *
485 | * 由于没有采用常规的设置一个很大数,然后取余实现循环的原理;
486 | * 本例默认N个Banner广告均在手机屏幕可见区域及右侧(屏幕可见区域仅显示当前广告条)
487 | * 因此向右滑动之前,需要预先将最右侧的广告条移到手机屏幕左侧不可见区域
488 | */
489 | private void doRightAnimation() {
490 | if (mCurrentBannerIndex == 0) {
491 | mCurrentBannerIndex = bannerNum - 1;
492 | } else {
493 | mCurrentBannerIndex -= 1;
494 | }
495 |
496 | /**
497 | * 将屏幕右侧尾部的视图移到左侧
498 | */
499 | for (int i = 0; i < bannerNum; i++) {
500 | if (mImageViews.get(i).getX() == mImageViews.get(i).getWidth() * (bannerNum - 1)) {
501 | mImageViews.get(i).setX(-mImageViews.get(i).getWidth());
502 | break;
503 | }
504 | }
505 |
506 | for (int i = 0; i < bannerNum; i++) {
507 | doRightAnimation(mImageViews.get(i), i);
508 | }
509 | }
510 |
511 | /**
512 | * 右滑动画
513 | * Calls requires API level 11
514 | */
515 | private void doRightAnimation(final View v, final int index) {
516 | ObjectAnimator rightAnimator = ObjectAnimator.ofFloat(v, "x", v.getX(), v.getX() + v.getWidth());
517 | rightAnimator.setDuration(mDuration);
518 | rightAnimator.setInterpolator(new LinearInterpolator());
519 | rightAnimator.addListener(new Animator.AnimatorListener() {
520 | @Override
521 | public void onAnimationCancel(Animator animation) {
522 |
523 | }
524 |
525 | @Override
526 | public void onAnimationEnd(Animator animation) {
527 | isAnimationing = false;
528 | if (index == bannerNum - 1) {
529 | /**
530 | * 修改指示器小圆点状态
531 | */
532 | mIndicators.get(mCurrentBannerIndex).changeStatus(true);
533 | if (mCurrentBannerIndex != bannerNum - 1) {
534 | mIndicators.get(mCurrentBannerIndex + 1).changeStatus(false);
535 | } else {
536 | mIndicators.get(0).changeStatus(false);
537 | }
538 | }
539 | }
540 |
541 | @Override
542 | public void onAnimationRepeat(Animator animation) {
543 |
544 | }
545 |
546 | @Override
547 | public void onAnimationStart(Animator animation) {
548 | isAnimationing = true;
549 | }
550 | });
551 | rightAnimator.start();
552 | }
553 |
554 | /**
555 | * 自动播放广告条,播放速度可由动画时间控制
556 | * 目前自动播放效果不好,且可能与点击造成混乱,暂时关闭,待后续优化
557 | */
558 | public void startAutoPlay() {
559 | if (isAutoSwitch && !isOnlyOne) {
560 | for (int i = 0; i < bannerNum; i++) {
561 | doAutoAnimation(mImageViews.get(i));
562 | }
563 | }
564 | }
565 |
566 | /**
567 | * 取消自动播放动画,需要在界面退出或跳转其他界面时取消自动播放的动画以防内存泄露
568 | */
569 | public void stopAutoPlay() {
570 | if (autoAnimator != null) {
571 | autoAnimator.cancel();
572 | }
573 | }
574 |
575 | /**
576 | * 自动播放循环动画
577 | * Calls requires API level 11
578 | *
579 | * @param v
580 | */
581 | public void doAutoAnimation(final View v) {
582 | autoAnimator = ObjectAnimator.ofFloat(v, "x", v.getX(), v.getX() - v.getWidth());
583 | autoAnimator.setDuration(mDuration * 3);
584 | autoAnimator.setInterpolator(new LinearInterpolator());
585 | autoAnimator.addListener(new Animator.AnimatorListener() {
586 | @Override
587 | public void onAnimationCancel(Animator animation) {
588 |
589 | }
590 |
591 | @Override
592 | public void onAnimationEnd(Animator animation) {
593 | if (v.getX() < 0) {
594 | v.setX((bannerNum - 1) * v.getWidth());
595 | }
596 |
597 | doAutoAnimation(v);
598 | }
599 |
600 | @Override
601 | public void onAnimationRepeat(Animator animation) {
602 |
603 | }
604 |
605 | @Override
606 | public void onAnimationStart(Animator animation) {
607 |
608 | }
609 | });
610 | autoAnimator.start();
611 | }
612 |
613 | /**
614 | * Indicator位置常量
615 | */
616 | public final class IndicatorPosition {
617 | public static final int RIGHT_BOTTOM = 0;
618 | public static final int LEFT_BOTTOM = 1;
619 | public static final int CENTER_BOTTOM = 2;
620 | }
621 |
622 | /**
623 | * Banner点击监听接口
624 | */
625 | public interface OnBannerClickListener {
626 | void onClick(int bannerIndex);
627 | }
628 |
629 | /**
630 | * Indicator指示器实现类
631 | *
632 | * 目前方案:先绘制小圆点,再根据切换改变颜色,需要重绘
633 | * 其他方案:
634 | * 1.使用属性动画移动圆点,会造成视觉移动,体验不好
635 | * 2.每个位置绘制一个灰色和白色圆点,叠在一起,根据切换显示与隐藏
636 | * 3.第一个位置绘制白色和灰色圆点,叠加放置,其他都是灰色圆点,对第一个白色圆点做属性动画
637 | */
638 | public class PointIndicator extends View {
639 | private Paint mPaint;
640 | private int mRadius;
641 | private int mColor;
642 |
643 | public PointIndicator(Context context, int radius, int color) {
644 | super(context);
645 | this.mRadius = radius;
646 | this.mColor = color;
647 | initPaint();
648 | }
649 |
650 | public void initPaint() {
651 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
652 | mPaint.setStyle(Paint.Style.FILL);
653 | mPaint.setColor(mColor);
654 | }
655 |
656 | @Override
657 | protected void onDraw(Canvas canvas) {
658 | super.onDraw(canvas);
659 | canvas.drawCircle(mRadius + mRadius / 2, mRadius, mRadius, mPaint);
660 | }
661 |
662 | /**
663 | * 更改小圆点颜色
664 | *
665 | * @param isCurrent 是否当前需要高亮的小圆点
666 | */
667 | public void changeStatus(boolean isCurrent) {
668 | if (isCurrent) {
669 | mColor = mSelectedColor;
670 | initPaint();
671 | invalidate();
672 | } else {
673 | mColor = mUnSelectedColor;
674 | initPaint();
675 | invalidate();
676 | }
677 | }
678 | }
679 |
680 | }
681 |
--------------------------------------------------------------------------------