├── banner ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── no_banner.png │ │ │ │ ├── black_background.xml │ │ │ │ ├── gray_radius.xml │ │ │ │ └── white_radius.xml │ │ │ ├── values │ │ │ │ ├── ids.xml │ │ │ │ └── attr.xml │ │ │ ├── animator │ │ │ │ └── scale_with_alpha.xml │ │ │ └── layout │ │ │ │ └── banner.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── youth │ │ │ │ └── banner │ │ │ │ ├── listener │ │ │ │ ├── OnBannerListener.java │ │ │ │ └── OnBannerClickListener.java │ │ │ │ ├── loader │ │ │ │ ├── ImageLoaderInterface.java │ │ │ │ └── ImageLoader.java │ │ │ │ ├── transformer │ │ │ │ ├── ScaleInOutTransformer.java │ │ │ │ ├── StackTransformer.java │ │ │ │ ├── DefaultTransformer.java │ │ │ │ ├── AccordionTransformer.java │ │ │ │ ├── CubeOutTransformer.java │ │ │ │ ├── CubeInTransformer.java │ │ │ │ ├── ZoomInTransformer.java │ │ │ │ ├── ZoomOutTranformer.java │ │ │ │ ├── RotateUpTransformer.java │ │ │ │ ├── RotateDownTransformer.java │ │ │ │ ├── BackgroundToForegroundTransformer.java │ │ │ │ ├── ForegroundToBackgroundTransformer.java │ │ │ │ ├── FlipVerticalTransformer.java │ │ │ │ ├── DepthPageTransformer.java │ │ │ │ ├── FlipHorizontalTransformer.java │ │ │ │ ├── ZoomOutSlideTransformer.java │ │ │ │ ├── TabletTransformer.java │ │ │ │ └── ABaseTransformer.java │ │ │ │ ├── BannerScroller.java │ │ │ │ ├── BannerConfig.java │ │ │ │ ├── view │ │ │ │ └── BannerViewPager.java │ │ │ │ ├── Transformer.java │ │ │ │ ├── WeakHandler.java │ │ │ │ └── Banner.java │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── youth │ │ └── banner │ │ └── ApplicationTest.java ├── proguard-rules.pro ├── build.gradle └── banner.iml ├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── mipmap-xhdpi │ │ │ ├── a.jpg │ │ │ ├── b.jpg │ │ │ ├── c.jpg │ │ │ ├── d.jpg │ │ │ ├── e.jpg │ │ │ ├── f.jpg │ │ │ ├── b1.jpg │ │ │ ├── b2.jpg │ │ │ ├── b3.jpg │ │ │ └── ic_launcher.png │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ ├── colors.xml │ │ │ └── arrays.xml │ │ ├── drawable │ │ │ ├── white.xml │ │ │ ├── green.xml │ │ │ ├── unselected_radius.xml │ │ │ ├── background.xml │ │ │ ├── selected_radius.xml │ │ │ └── default_selecter.xml │ │ ├── layout │ │ │ ├── header.xml │ │ │ ├── text_item.xml │ │ │ ├── activity_banner_animation.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_custom_view_pager.xml │ │ │ ├── activity_banner_local.xml │ │ │ ├── activity_indicator_position.xml │ │ │ ├── activity_banner_style.xml │ │ │ ├── activity_custom_banner.xml │ │ │ └── banner_custom_viewpager.xml │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ └── animator │ │ │ └── indicator_animator.xml │ │ ├── java │ │ └── com │ │ │ └── test │ │ │ └── banner │ │ │ ├── ui │ │ │ ├── CustomViewPager.java │ │ │ ├── GlideRoundTransform.java │ │ │ └── RoundAngleImageView.java │ │ │ ├── loader │ │ │ ├── FrescoImageLoader.java │ │ │ └── GlideImageLoader.java │ │ │ ├── demo │ │ │ ├── BannerLocalActivity.java │ │ │ ├── CustomBannerActivity.java │ │ │ ├── CustomViewPagerActivity.java │ │ │ ├── IndicatorPositionActivity.java │ │ │ ├── BannerStyleActivity.java │ │ │ └── BannerAnimationActivity.java │ │ │ ├── GallyPageTransformer.java │ │ │ ├── SampleAdapter.java │ │ │ ├── App.java │ │ │ ├── SuperSwipeRefreshLayout.java │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradlew ├── LICENSE └── README.md /banner/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app',':banner' -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/a.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/b.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/c.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/d.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/e.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/f.jpg -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/b1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/b1.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/b2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/b2.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/b3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/b3.jpg -------------------------------------------------------------------------------- /banner/src/main/res/drawable/no_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/banner/src/main/res/drawable/no_banner.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/banner/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Banner Example 3 | ScrollView嵌套banner 4 | 5 | 6 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/listener/OnBannerListener.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.listener; 2 | 3 | public interface OnBannerListener { 4 | public void OnBannerClick(int position); 5 | } 6 | -------------------------------------------------------------------------------- /banner/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/white.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/green.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /banner/src/main/res/drawable/black_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/unselected_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selected_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /banner/src/main/res/drawable/gray_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /banner/src/main/res/drawable/white_radius.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 22 16:29:20 CST 2019 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-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/listener/OnBannerClickListener.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.listener; 2 | 3 | 4 | /** 5 | * 旧版接口,由于返回的下标是从1开始,下标越界而废弃(因为有人使用所以不能直接删除) 6 | */ 7 | @Deprecated 8 | public interface OnBannerClickListener { 9 | public void OnBannerClick(int position); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/header.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/default_selecter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/loader/ImageLoaderInterface.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.loader; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import java.io.Serializable; 7 | 8 | 9 | public interface ImageLoaderInterface extends Serializable { 10 | 11 | void displayImage(Context context, Object path, T imageView); 12 | 13 | T createImageView(Context context); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /banner/src/androidTest/java/com/youth/banner/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 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 | } -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/loader/ImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.loader; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | 7 | public abstract class ImageLoader implements ImageLoaderInterface { 8 | 9 | @Override 10 | public ImageView createImageView(Context context) { 11 | ImageView imageView = new ImageView(context); 12 | return imageView; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /banner/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/ui/CustomViewPager.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.youth.banner.view.BannerViewPager; 7 | 8 | 9 | public class CustomViewPager extends BannerViewPager { 10 | // do something by yourself. 11 | 12 | public CustomViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public CustomViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/text_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ScaleInOutTransformer.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.transformer; 2 | 3 | import android.view.View; 4 | 5 | public class ScaleInOutTransformer extends ABaseTransformer { 6 | 7 | @Override 8 | protected void onTransform(View view, float position) { 9 | view.setPivotX(position < 0 ? 0 : view.getWidth()); 10 | view.setPivotY(view.getHeight() / 2f); 11 | float scale = position < 0 ? 1f + position : 1f - position; 12 | view.setScaleX(scale); 13 | view.setScaleY(scale); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/animator/indicator_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /banner/src/main/res/animator/scale_with_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @color/main_color 4 | @color/main_color 5 | @color/main_color 6 | 7 | @color/main_color 8 | @color/main_color 9 | #BDBDBD 10 | #FFFFFF 11 | 12 | #5CB85C 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_banner_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio Navigation editor temp files 29 | .navigation/ 30 | 31 | # Android Studio captures folder 32 | captures/ 33 | .idea 34 | /banner.iml 35 | wellswu-banner.iml 36 | banner/banner-banner.iml 37 | app/app.iml 38 | gradlew.bat -------------------------------------------------------------------------------- /banner/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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/loader/FrescoImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.loader; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.widget.ImageView; 6 | 7 | import com.facebook.drawee.view.SimpleDraweeView; 8 | import com.youth.banner.loader.ImageLoader; 9 | 10 | 11 | public class FrescoImageLoader extends ImageLoader { 12 | @Override 13 | public void displayImage(Context context, Object path, ImageView imageView) { 14 | //用fresco加载图片 15 | Uri uri = Uri.parse((String) path); 16 | imageView.setImageURI(uri); 17 | 18 | } 19 | 20 | //提供createImageView 方法,方便fresco自定义ImageView 21 | @Override 22 | public ImageView createImageView(Context context) { 23 | SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context); 24 | return simpleDraweeView; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/loader/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.loader; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.Glide; 7 | import com.test.banner.ui.GlideRoundTransform; 8 | import com.test.banner.ui.RoundAngleImageView; 9 | import com.youth.banner.loader.ImageLoader; 10 | 11 | 12 | public class GlideImageLoader extends ImageLoader { 13 | @Override 14 | public void displayImage(Context context, Object path, ImageView imageView) { 15 | //具体方法内容自己去选择,次方法是为了减少banner过多的依赖第三方包,所以将这个权限开放给使用者去选择 16 | Glide.with(context.getApplicationContext()) 17 | .load(path) 18 | .into(imageView); 19 | } 20 | 21 | // @Override 22 | // public ImageView createImageView(Context context) { 23 | // //圆角 24 | // return new RoundAngleImageView(context); 25 | // } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_custom_view_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_banner_local.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 22 | 23 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/StackTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class StackTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | view.setTranslationX(position < 0 ? 0f : -view.getWidth() * position); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/DefaultTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class DefaultTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | } 26 | 27 | @Override 28 | public boolean isPagingEnabled() { 29 | return true; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '28.0.3' 6 | 7 | defaultConfig { 8 | applicationId "com.test.banner" 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | testImplementation 'junit:junit:4.12' 25 | implementation 'androidx.appcompat:appcompat:1.1.0-alpha02' 26 | implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha02' 27 | implementation "com.github.bumptech.glide:glide:3.7.0" 28 | implementation 'com.facebook.fresco:fresco:0.12.0' 29 | implementation 'com.zxy.android:recovery:0.0.8' 30 | implementation project(':banner') 31 | // compile 'com.youth.banner:banner:+' 32 | } 33 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/AccordionTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class AccordionTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | view.setPivotX(position < 0 ? 0 : view.getWidth()); 26 | view.setScaleX(position < 0 ? 1f + position : 1f - position); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/BannerLocalActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import com.test.banner.R; 7 | import com.test.banner.loader.GlideImageLoader; 8 | import com.youth.banner.Banner; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class BannerLocalActivity extends AppCompatActivity { 14 | 15 | Banner banner; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_banner_local); 21 | initView(); 22 | } 23 | 24 | private void initView() { 25 | banner = (Banner) findViewById(R.id.banner); 26 | //本地图片数据(资源文件) 27 | List list=new ArrayList<>(); 28 | list.add(R.mipmap.b1); 29 | list.add(R.mipmap.b2); 30 | list.add(R.mipmap.b3); 31 | 32 | 33 | 34 | banner.setImages(list) 35 | .setImageLoader(new GlideImageLoader()) 36 | .start(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/BannerScroller.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 2 | 3 | import android.content.Context; 4 | import android.view.animation.Interpolator; 5 | import android.widget.Scroller; 6 | 7 | public class BannerScroller extends Scroller { 8 | private int mDuration = BannerConfig.DURATION; 9 | 10 | public BannerScroller(Context context) { 11 | super(context); 12 | } 13 | 14 | public BannerScroller(Context context, Interpolator interpolator) { 15 | super(context, interpolator); 16 | } 17 | 18 | public BannerScroller(Context context, Interpolator interpolator, boolean flywheel) { 19 | super(context, interpolator, flywheel); 20 | } 21 | 22 | @Override 23 | public void startScroll(int startX, int startY, int dx, int dy, int duration) { 24 | super.startScroll(startX, startY, dx, dy, mDuration); 25 | } 26 | 27 | @Override 28 | public void startScroll(int startX, int startY, int dx, int dy) { 29 | super.startScroll(startX, startY, dx, dy, mDuration); 30 | } 31 | 32 | public void setDuration(int time) { 33 | mDuration = time; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_indicator_position.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 15 | 21 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/GallyPageTransformer.java: -------------------------------------------------------------------------------- 1 | package com.test.banner; 2 | 3 | import androidx.viewpager.widget.ViewPager; 4 | import android.view.View; 5 | 6 | /** 7 | * 自定义动画效果 8 | * 调用 banner.setPageTransformer()方法设置 9 | */ 10 | 11 | public class GallyPageTransformer implements ViewPager.PageTransformer { 12 | private static final float min_scale = 0.85f; 13 | 14 | @Override 15 | public void transformPage(View page, float position) { 16 | float scaleFactor = Math.max(min_scale, 1 - Math.abs(position)); 17 | float rotate = 20 * Math.abs(position); 18 | if (position < -1) { 19 | 20 | } else if (position < 0) { 21 | page.setScaleX(scaleFactor); 22 | page.setScaleY(scaleFactor); 23 | page.setRotationY(rotate); 24 | } else if (position >= 0 && position < 1) { 25 | page.setScaleX(scaleFactor); 26 | page.setScaleY(scaleFactor); 27 | page.setRotationY(-rotate); 28 | } else if (position >= 1) { 29 | page.setScaleX(scaleFactor); 30 | page.setScaleY(scaleFactor); 31 | page.setRotationY(-rotate); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/CubeOutTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class CubeOutTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | view.setPivotX(position < 0f ? view.getWidth() : 0f); 26 | view.setPivotY(view.getHeight() * 0.5f); 27 | view.setRotationY(90f * position); 28 | } 29 | 30 | @Override 31 | public boolean isPagingEnabled() { 32 | return true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/CubeInTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class CubeInTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | // Rotate the fragment on the left or right edge 26 | view.setPivotX(position > 0 ? 0 : view.getWidth()); 27 | view.setPivotY(0); 28 | view.setRotationY(-90f * position); 29 | } 30 | 31 | @Override 32 | public boolean isPagingEnabled() { 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_banner_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 16 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/BannerConfig.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 2 | 3 | 4 | public class BannerConfig { 5 | /** 6 | * indicator style 7 | */ 8 | public static final int NOT_INDICATOR = 0; 9 | public static final int CIRCLE_INDICATOR = 1; 10 | public static final int NUM_INDICATOR = 2; 11 | public static final int NUM_INDICATOR_TITLE = 3; 12 | public static final int CIRCLE_INDICATOR_TITLE = 4; 13 | public static final int CIRCLE_INDICATOR_TITLE_INSIDE = 5; 14 | /** 15 | * indicator gravity 16 | */ 17 | public static final int LEFT = 5; 18 | public static final int CENTER = 6; 19 | public static final int RIGHT = 7; 20 | 21 | /** 22 | * banner 23 | */ 24 | public static final int PADDING_SIZE = 5; 25 | public static final int TIME = 2000; 26 | public static final int DURATION = 800; 27 | public static final boolean IS_AUTO_PLAY = true; 28 | public static final boolean IS_SCROLL = true; 29 | 30 | /** 31 | * title style 32 | */ 33 | public static final int TITLE_BACKGROUND = -1; 34 | public static final int TITLE_HEIGHT = -1; 35 | public static final int TITLE_TEXT_COLOR = -1; 36 | public static final int TITLE_TEXT_SIZE = -1; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ZoomInTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class ZoomInTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float scale = position < 0 ? position + 1f : Math.abs(1f - position); 26 | view.setScaleX(scale); 27 | view.setScaleY(scale); 28 | view.setPivotX(view.getWidth() * 0.5f); 29 | view.setPivotY(view.getHeight() * 0.5f); 30 | view.setAlpha(position < -1f || position > 1f ? 0f : 1f - (scale - 1f)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ZoomOutTranformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class ZoomOutTranformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float scale = 1f + Math.abs(position); 26 | view.setScaleX(scale); 27 | view.setScaleY(scale); 28 | view.setPivotX(view.getWidth() * 0.5f); 29 | view.setPivotY(view.getHeight() * 0.5f); 30 | view.setAlpha(position < -1f || position > 1f ? 0f : 1f - (scale - 1f)); 31 | if(position == -1){ 32 | view.setTranslationX(view.getWidth() * -1); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/RotateUpTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class RotateUpTransformer extends ABaseTransformer { 22 | 23 | private static final float ROT_MOD = -15f; 24 | 25 | @Override 26 | protected void onTransform(View view, float position) { 27 | final float width = view.getWidth(); 28 | final float rotation = ROT_MOD * position; 29 | 30 | view.setPivotX(width * 0.5f); 31 | view.setPivotY(0f); 32 | view.setTranslationX(0f); 33 | view.setRotation(rotation); 34 | } 35 | 36 | @Override 37 | protected boolean isPagingEnabled() { 38 | return true; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/RotateDownTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class RotateDownTransformer extends ABaseTransformer { 22 | 23 | private static final float ROT_MOD = -15f; 24 | 25 | @Override 26 | protected void onTransform(View view, float position) { 27 | final float width = view.getWidth(); 28 | final float height = view.getHeight(); 29 | final float rotation = ROT_MOD * position * -1.25f; 30 | 31 | view.setPivotX(width * 0.5f); 32 | view.setPivotY(height); 33 | view.setRotation(rotation); 34 | } 35 | 36 | @Override 37 | protected boolean isPagingEnabled() { 38 | return true; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/BackgroundToForegroundTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class BackgroundToForegroundTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float height = view.getHeight(); 26 | final float width = view.getWidth(); 27 | final float scale = min(position < 0 ? 1f : Math.abs(1f - position), 0.5f); 28 | 29 | view.setScaleX(scale); 30 | view.setScaleY(scale); 31 | view.setPivotX(width * 0.5f); 32 | view.setPivotY(height * 0.5f); 33 | view.setTranslationX(position < 0 ? width * position : -width * position * 0.25f); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ForegroundToBackgroundTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class ForegroundToBackgroundTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float height = view.getHeight(); 26 | final float width = view.getWidth(); 27 | final float scale = min(position > 0 ? 1f : Math.abs(1f + position), 0.5f); 28 | 29 | view.setScaleX(scale); 30 | view.setScaleY(scale); 31 | view.setPivotX(width * 0.5f); 32 | view.setPivotY(height * 0.5f); 33 | view.setTranslationX(position > 0 ? width * position : -width * position * 0.25f); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/CustomBannerActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.test.banner.App; 7 | import com.test.banner.R; 8 | import com.test.banner.loader.GlideImageLoader; 9 | import com.youth.banner.Banner; 10 | import com.youth.banner.BannerConfig; 11 | 12 | public class CustomBannerActivity extends AppCompatActivity { 13 | Banner banner1,banner2,banner3; 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_custom_banner); 18 | banner1 = (Banner) findViewById(R.id.banner1); 19 | banner2 = (Banner) findViewById(R.id.banner2); 20 | banner3 = (Banner) findViewById(R.id.banner3); 21 | 22 | banner1.setImages(App.images) 23 | .setImageLoader(new GlideImageLoader()) 24 | .start(); 25 | 26 | banner2.setImages(App.images) 27 | .setImageLoader(new GlideImageLoader()) 28 | .start(); 29 | 30 | banner3.setImages(App.images) 31 | .setBannerTitles(App.titles) 32 | .setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) 33 | .setImageLoader(new GlideImageLoader()) 34 | .start(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/view/BannerViewPager.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner.view; 2 | 3 | import android.content.Context; 4 | import androidx.viewpager.widget.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | 9 | public class BannerViewPager extends ViewPager { 10 | private boolean scrollable = true; 11 | 12 | public BannerViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public BannerViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | @Override 21 | public boolean onTouchEvent(MotionEvent ev) { 22 | if(this.scrollable) { 23 | if (getCurrentItem() == 0 && getChildCount() == 0) { 24 | return false; 25 | } 26 | return super.onTouchEvent(ev); 27 | } else { 28 | return false; 29 | } 30 | } 31 | 32 | @Override 33 | public boolean onInterceptTouchEvent(MotionEvent ev) { 34 | if(this.scrollable) { 35 | if (getCurrentItem() == 0 && getChildCount() == 0) { 36 | return false; 37 | } 38 | return super.onInterceptTouchEvent(ev); 39 | } else { 40 | return false; 41 | } 42 | } 43 | 44 | public void setScrollable(boolean scrollable) { 45 | this.scrollable = scrollable; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/SampleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.test.banner; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | 11 | public class SampleAdapter extends BaseAdapter { 12 | 13 | private String[] mDataSet; 14 | private Context context; 15 | 16 | public SampleAdapter(Context context,String[] dataSet) { 17 | this.mDataSet = dataSet; 18 | this.context = context; 19 | } 20 | 21 | @Override 22 | public int getCount() { 23 | return mDataSet.length; 24 | } 25 | 26 | @Override 27 | public Object getItem(int position) { 28 | return position; 29 | } 30 | 31 | @Override 32 | public long getItemId(int position) { 33 | return position; 34 | } 35 | 36 | @Override 37 | public View getView(int position, View convertView, ViewGroup parent) { 38 | convertView = View.inflate(context, R.layout.text_item, null); 39 | TextView textView = (TextView) convertView.findViewById(R.id.text); 40 | textView.setText(mDataSet[position]); 41 | if (position % 2 == 0) { 42 | textView.setBackgroundColor(Color.parseColor("#f5f5f5")); 43 | } else { 44 | textView.setBackgroundColor(Color.WHITE); 45 | } 46 | return convertView; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/FlipVerticalTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class FlipVerticalTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float rotation = -180f * position; 26 | 27 | view.setAlpha(rotation > 90f || rotation < -90f ? 0f : 1f); 28 | view.setPivotX(view.getWidth() * 0.5f); 29 | view.setPivotY(view.getHeight() * 0.5f); 30 | view.setRotationX(rotation); 31 | } 32 | 33 | @Override 34 | protected void onPostTransform(View page, float position) { 35 | super.onPostTransform(page, position); 36 | 37 | if (position > -0.5f && position < 0.5f) { 38 | page.setVisibility(View.VISIBLE); 39 | } else { 40 | page.setVisibility(View.INVISIBLE); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/App.java: -------------------------------------------------------------------------------- 1 | package com.test.banner; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.util.DisplayMetrics; 6 | 7 | import com.facebook.drawee.backends.pipeline.Fresco; 8 | import com.zxy.recovery.core.Recovery; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | 15 | public class App extends Application { 16 | public static List images=new ArrayList<>(); 17 | public static List titles=new ArrayList<>(); 18 | public static int H,W; 19 | public static App app; 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | app=this; 24 | getScreen(this); 25 | Fresco.initialize(this); 26 | Recovery.getInstance() 27 | .debug(true) 28 | .recoverInBackground(false) 29 | .recoverStack(true) 30 | .mainPage(MainActivity.class) 31 | .init(this); 32 | String[] urls = getResources().getStringArray(R.array.url); 33 | String[] tips = getResources().getStringArray(R.array.title); 34 | List list = Arrays.asList(urls); 35 | images = new ArrayList(list); 36 | List list1 = Arrays.asList(tips); 37 | titles= new ArrayList(list1); 38 | } 39 | public void getScreen(Context aty) { 40 | DisplayMetrics dm = aty.getResources().getDisplayMetrics(); 41 | H=dm.heightPixels; 42 | W=dm.widthPixels; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/DepthPageTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class DepthPageTransformer extends ABaseTransformer { 22 | 23 | private static final float MIN_SCALE = 0.75f; 24 | 25 | @Override 26 | protected void onTransform(View view, float position) { 27 | if (position <= 0f) { 28 | view.setTranslationX(0f); 29 | view.setScaleX(1f); 30 | view.setScaleY(1f); 31 | } else if (position <= 1f) { 32 | final float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); 33 | view.setAlpha(1 - position); 34 | view.setPivotY(0.5f * view.getHeight()); 35 | view.setTranslationX(view.getWidth() * -position); 36 | view.setScaleX(scaleFactor); 37 | view.setScaleY(scaleFactor); 38 | } 39 | } 40 | 41 | @Override 42 | protected boolean isPagingEnabled() { 43 | return true; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/CustomViewPagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import android.view.ViewGroup; 6 | import android.widget.RelativeLayout; 7 | 8 | import com.test.banner.App; 9 | import com.test.banner.R; 10 | import com.test.banner.loader.GlideImageLoader; 11 | import com.youth.banner.Banner; 12 | import com.youth.banner.listener.OnBannerListener; 13 | 14 | 15 | public class CustomViewPagerActivity extends AppCompatActivity implements OnBannerListener { 16 | Banner banner; 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_custom_view_pager); 21 | 22 | banner = (Banner) findViewById(R.id.banner); 23 | banner.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, App.H / 4)); 24 | //简单使用 25 | banner.setImages(App.images) 26 | .setImageLoader(new GlideImageLoader()) 27 | .setOnBannerListener(this) 28 | .start(); 29 | } 30 | 31 | @Override 32 | public void OnBannerClick(int position) { 33 | 34 | } 35 | 36 | //如果你需要考虑更好的体验,可以这么操作 37 | @Override 38 | protected void onStart() { 39 | super.onStart(); 40 | //开始轮播 41 | banner.startAutoPlay(); 42 | } 43 | 44 | @Override 45 | protected void onStop() { 46 | super.onStop(); 47 | //结束轮播 48 | banner.stopAutoPlay(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/FlipHorizontalTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class FlipHorizontalTransformer extends ABaseTransformer { 22 | 23 | @Override 24 | protected void onTransform(View view, float position) { 25 | final float rotation = 180f * position; 26 | 27 | view.setAlpha(rotation > 90f || rotation < -90f ? 0 : 1); 28 | view.setPivotX(view.getWidth() * 0.5f); 29 | view.setPivotY(view.getHeight() * 0.5f); 30 | view.setRotationY(rotation); 31 | } 32 | 33 | @Override 34 | protected void onPostTransform(View page, float position) { 35 | super.onPostTransform(page, position); 36 | 37 | //resolve problem: new page can't handle click event! 38 | if (position > -0.5f && position < 0.5f) { 39 | page.setVisibility(View.VISIBLE); 40 | } else { 41 | page.setVisibility(View.INVISIBLE); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /banner/src/main/res/values/attr.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 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/IndicatorPositionActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.AdapterView; 7 | import android.widget.Spinner; 8 | 9 | import com.test.banner.App; 10 | import com.test.banner.R; 11 | import com.test.banner.loader.GlideImageLoader; 12 | import com.youth.banner.Banner; 13 | import com.youth.banner.BannerConfig; 14 | 15 | public class IndicatorPositionActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { 16 | Banner banner; 17 | Spinner spinnerPosition; 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_indicator_position); 22 | banner = (Banner) findViewById(R.id.banner); 23 | spinnerPosition= (Spinner) findViewById(R.id.spinnerPosition); 24 | spinnerPosition.setOnItemSelectedListener(this); 25 | 26 | banner.setImages(App.images) 27 | .setImageLoader(new GlideImageLoader()) 28 | .start(); 29 | } 30 | 31 | @Override 32 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 33 | switch (position) { 34 | case 0: 35 | banner.setIndicatorGravity(BannerConfig.LEFT); 36 | break; 37 | case 1: 38 | banner.setIndicatorGravity(BannerConfig.CENTER); 39 | break; 40 | case 2: 41 | banner.setIndicatorGravity(BannerConfig.RIGHT); 42 | break; 43 | } 44 | banner.start(); 45 | } 46 | 47 | @Override 48 | public void onNothingSelected(AdapterView parent) { 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/ui/GlideRoundTransform.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.ui; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.Resources; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapShader; 8 | import android.graphics.Canvas; 9 | import android.graphics.Paint; 10 | import android.graphics.RectF; 11 | 12 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 13 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 14 | 15 | public class GlideRoundTransform extends BitmapTransformation { 16 | 17 | private static float radius = 0f; 18 | 19 | public GlideRoundTransform(Context context) { 20 | this(context, 4); 21 | } 22 | 23 | public GlideRoundTransform(Context context, int dp) { 24 | super(context); 25 | this.radius = Resources.getSystem().getDisplayMetrics().density * dp; 26 | } 27 | 28 | @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 29 | return roundCrop(pool, toTransform); 30 | } 31 | 32 | private static Bitmap roundCrop(BitmapPool pool, Bitmap source) { 33 | if (source == null) return null; 34 | 35 | Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 36 | if (result == null) { 37 | result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 38 | } 39 | 40 | Canvas canvas = new Canvas(result); 41 | Paint paint = new Paint(); 42 | paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 43 | paint.setAntiAlias(true); 44 | RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); 45 | canvas.drawRoundRect(rectF, radius, radius, paint); 46 | return result; 47 | } 48 | 49 | @Override public String getId() { 50 | return getClass().getName() + Math.round(radius); 51 | } 52 | } -------------------------------------------------------------------------------- /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\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 | #指定代码的压缩级别 19 | -optimizationpasses 5 20 | #包明不混合大小写 21 | -dontusemixedcaseclassnames 22 | #不去忽略非公共的库类 23 | -dontskipnonpubliclibraryclasses 24 | #优化 不优化输入的类文件 25 | -dontoptimize 26 | #预校验 27 | -dontpreverify 28 | #混淆时是否记录日志 29 | -verbose 30 | # 混淆时所采用的算法 31 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 32 | #保护注解 33 | -keepattributes *Annotation* 34 | # 保持哪些类不被混淆 35 | -keep public class * extends android.app.Fragment 36 | -keep public class * extends android.app.Activity 37 | -keep public class * extends android.app.Application 38 | -keep public class * extends android.app.Service 39 | -keep public class * extends android.content.BroadcastReceiver 40 | -keep public class * extends android.content.ContentProvider 41 | -keep public class * extends android.app.backup.BackupAgentHelper 42 | -keep public class * extends android.preference.Preference 43 | -keep public class com.android.vending.licensing.ILicensingService 44 | #如果有引用v4包可以添加下面这行 45 | -keep public class * extends android.support.v4.app.Fragment 46 | #忽略警告 47 | -ignorewarning 48 | 49 | -keep class com.youth.banner.** { 50 | *; 51 | } 52 | -keep public class * implements com.bumptech.glide.module.GlideModule 53 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 54 | **[] $VALUES; 55 | public *; 56 | } 57 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ZoomOutSlideTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.view.View; 20 | 21 | public class ZoomOutSlideTransformer extends ABaseTransformer { 22 | 23 | private static final float MIN_SCALE = 0.85f; 24 | private static final float MIN_ALPHA = 0.5f; 25 | 26 | @Override 27 | protected void onTransform(View view, float position) { 28 | if (position >= -1 || position <= 1) { 29 | // Modify the default slide transition to shrink the page as well 30 | final float height = view.getHeight(); 31 | final float width = view.getWidth(); 32 | final float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); 33 | final float vertMargin = height * (1 - scaleFactor) / 2; 34 | final float horzMargin = width * (1 - scaleFactor) / 2; 35 | 36 | // Center vertically 37 | view.setPivotY(0.5f * height); 38 | view.setPivotX(0.5f * width); 39 | 40 | if (position < 0) { 41 | view.setTranslationX(horzMargin - vertMargin / 2); 42 | } else { 43 | view.setTranslationX(-horzMargin + vertMargin / 2); 44 | } 45 | 46 | // Scale the page down (between MIN_SCALE and 1) 47 | view.setScaleX(scaleFactor); 48 | view.setScaleY(scaleFactor); 49 | 50 | // Fade the page relative to its size. 51 | view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/TabletTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import android.graphics.Camera; 20 | import android.graphics.Matrix; 21 | import android.view.View; 22 | 23 | public class TabletTransformer extends ABaseTransformer { 24 | 25 | private static final Matrix OFFSET_MATRIX = new Matrix(); 26 | private static final Camera OFFSET_CAMERA = new Camera(); 27 | private static final float[] OFFSET_TEMP_FLOAT = new float[2]; 28 | 29 | @Override 30 | protected void onTransform(View view, float position) { 31 | final float rotation = (position < 0 ? 30f : -30f) * Math.abs(position); 32 | 33 | view.setTranslationX(getOffsetXForRotation(rotation, view.getWidth(), view.getHeight())); 34 | view.setPivotX(view.getWidth() * 0.5f); 35 | view.setPivotY(0); 36 | view.setRotationY(rotation); 37 | } 38 | 39 | protected static final float getOffsetXForRotation(float degrees, int width, int height) { 40 | OFFSET_MATRIX.reset(); 41 | OFFSET_CAMERA.save(); 42 | OFFSET_CAMERA.rotateY(Math.abs(degrees)); 43 | OFFSET_CAMERA.getMatrix(OFFSET_MATRIX); 44 | OFFSET_CAMERA.restore(); 45 | 46 | OFFSET_MATRIX.preTranslate(-width * 0.5f, -height * 0.5f); 47 | OFFSET_MATRIX.postTranslate(width * 0.5f, height * 0.5f); 48 | OFFSET_TEMP_FLOAT[0] = width; 49 | OFFSET_TEMP_FLOAT[1] = height; 50 | OFFSET_MATRIX.mapPoints(OFFSET_TEMP_FLOAT); 51 | return (width - OFFSET_TEMP_FLOAT[0]) * (degrees > 0.0f ? 1.0f : -1.0f); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_custom_banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 20 | 21 | 30 | 38 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/BannerStyleActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.AdapterView; 7 | import android.widget.Spinner; 8 | 9 | import com.test.banner.App; 10 | import com.test.banner.R; 11 | import com.test.banner.loader.GlideImageLoader; 12 | import com.youth.banner.Banner; 13 | import com.youth.banner.BannerConfig; 14 | 15 | public class BannerStyleActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { 16 | Banner banner; 17 | Spinner spinnerStyle; 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_banner_style); 22 | banner = (Banner) findViewById(R.id.banner); 23 | spinnerStyle = (Spinner) findViewById(R.id.spinnerStyle); 24 | spinnerStyle.setOnItemSelectedListener(this); 25 | 26 | //默认是CIRCLE_INDICATOR 27 | banner.setImages(App.images) 28 | .setBannerTitles(App.titles) 29 | .setImageLoader(new GlideImageLoader()) 30 | .start(); 31 | } 32 | 33 | @Override 34 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 35 | switch (position){ 36 | case 0: 37 | banner.updateBannerStyle(BannerConfig.NOT_INDICATOR); 38 | break; 39 | case 1: 40 | banner.updateBannerStyle(BannerConfig.CIRCLE_INDICATOR); 41 | break; 42 | case 2: 43 | banner.updateBannerStyle(BannerConfig.NUM_INDICATOR); 44 | break; 45 | case 3: 46 | banner.updateBannerStyle(BannerConfig.NUM_INDICATOR_TITLE); 47 | break; 48 | case 4: 49 | banner.updateBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE); 50 | break; 51 | case 5: 52 | banner.updateBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE); 53 | break; 54 | } 55 | } 56 | 57 | @Override 58 | public void onNothingSelected(AdapterView parent) { 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/SuperSwipeRefreshLayout.java: -------------------------------------------------------------------------------- 1 | package com.test.banner; 2 | 3 | 4 | import android.content.Context; 5 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.ViewConfiguration; 9 | 10 | 11 | /** 12 | 重写SwipeRefreshLayout的onIntercept方法解决与viewpager冲突问题。 13 | 思路: 14 | 1. 因为下拉刷新,只有纵向滑动的时候才有效,那么我们就判断此时是纵向滑动还是横向滑动就可以了。 15 | 2. 纵向滑动就拦截事件,横向滑动不拦截。 16 | 3. 怎么判断是纵向滑动还是横向滑动,只要判断Y轴的移动距离大于X轴的移动距离那么就判定为纵向滑动就行了。 17 | */ 18 | public class SuperSwipeRefreshLayout extends SwipeRefreshLayout { 19 | 20 | private float startY; 21 | private float startX; 22 | // 记录viewPager是否拖拽的标记 23 | private boolean mIsVpDragger; 24 | private final int mTouchSlop; 25 | 26 | public SuperSwipeRefreshLayout(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 29 | } 30 | 31 | @Override 32 | public boolean onInterceptTouchEvent(MotionEvent ev) { 33 | int action = ev.getAction(); 34 | switch (action) { 35 | case MotionEvent.ACTION_DOWN: 36 | // 记录手指按下的位置 37 | startY = ev.getY(); 38 | startX = ev.getX(); 39 | // 初始化标记 40 | mIsVpDragger = false; 41 | break; 42 | case MotionEvent.ACTION_MOVE: 43 | // 如果viewpager正在拖拽中,那么不拦截它的事件,直接return false; 44 | if(mIsVpDragger) { 45 | return false; 46 | } 47 | 48 | // 获取当前手指位置 49 | float endY = ev.getY(); 50 | float endX = ev.getX(); 51 | float distanceX = Math.abs(endX - startX); 52 | float distanceY = Math.abs(endY - startY); 53 | // 如果X轴位移大于Y轴位移,那么将事件交给viewPager处理。 54 | if(distanceX > mTouchSlop && distanceX > distanceY) { 55 | mIsVpDragger = true; 56 | return false; 57 | } 58 | break; 59 | case MotionEvent.ACTION_UP: 60 | case MotionEvent.ACTION_CANCEL: 61 | // 初始化标记 62 | mIsVpDragger = false; 63 | break; 64 | } 65 | // 如果是Y轴位移大于X轴,事件交给swipeRefreshLayout处理。 66 | return super.onInterceptTouchEvent(ev); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 2 | 3 | import androidx.viewpager.widget.ViewPager.PageTransformer; 4 | 5 | import com.youth.banner.transformer.AccordionTransformer; 6 | import com.youth.banner.transformer.BackgroundToForegroundTransformer; 7 | import com.youth.banner.transformer.CubeInTransformer; 8 | import com.youth.banner.transformer.CubeOutTransformer; 9 | import com.youth.banner.transformer.DefaultTransformer; 10 | import com.youth.banner.transformer.DepthPageTransformer; 11 | import com.youth.banner.transformer.FlipHorizontalTransformer; 12 | import com.youth.banner.transformer.FlipVerticalTransformer; 13 | import com.youth.banner.transformer.ForegroundToBackgroundTransformer; 14 | import com.youth.banner.transformer.RotateDownTransformer; 15 | import com.youth.banner.transformer.RotateUpTransformer; 16 | import com.youth.banner.transformer.ScaleInOutTransformer; 17 | import com.youth.banner.transformer.StackTransformer; 18 | import com.youth.banner.transformer.TabletTransformer; 19 | import com.youth.banner.transformer.ZoomInTransformer; 20 | import com.youth.banner.transformer.ZoomOutSlideTransformer; 21 | import com.youth.banner.transformer.ZoomOutTranformer; 22 | 23 | public class Transformer { 24 | public static Class Default = DefaultTransformer.class; 25 | public static Class Accordion = AccordionTransformer.class; 26 | public static Class BackgroundToForeground = BackgroundToForegroundTransformer.class; 27 | public static Class ForegroundToBackground = ForegroundToBackgroundTransformer.class; 28 | public static Class CubeIn = CubeInTransformer.class; 29 | public static Class CubeOut = CubeOutTransformer.class; 30 | public static Class DepthPage = DepthPageTransformer.class; 31 | public static Class FlipHorizontal = FlipHorizontalTransformer.class; 32 | public static Class FlipVertical = FlipVerticalTransformer.class; 33 | public static Class RotateDown = RotateDownTransformer.class; 34 | public static Class RotateUp = RotateUpTransformer.class; 35 | public static Class ScaleInOut = ScaleInOutTransformer.class; 36 | public static Class Stack = StackTransformer.class; 37 | public static Class Tablet = TabletTransformer.class; 38 | public static Class ZoomIn = ZoomInTransformer.class; 39 | public static Class ZoomOut = ZoomOutTranformer.class; 40 | public static Class ZoomOutSlide = ZoomOutSlideTransformer.class; 41 | } 42 | -------------------------------------------------------------------------------- /banner/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | version = "1.5-alpha01" 5 | 6 | android { 7 | compileSdkVersion 28 8 | buildToolsVersion '28.0.3' 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 28 13 | versionCode 42 14 | versionName version 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | lintOptions { 23 | abortOnError false 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | compileOnly 'androidx.fragment:fragment:1.1.0-alpha04' 30 | } 31 | 32 | 33 | def siteUrl = 'https://github.com/deltaguita/banner' // 项目的主页 34 | def gitUrl = 'https://github.com/deltaguita/banner.git' // Git仓库的url 35 | group = "github.com.deltaguita" //一般填你唯一的包名 36 | //gradlew bintrayUpload 37 | install { 38 | repositories.mavenInstaller { 39 | // This generates POM.xml with proper parameters 40 | pom { 41 | project { 42 | packaging 'aar' 43 | // Add your description here项目描述 44 | name 'Android图片轮播控件' 45 | url siteUrl 46 | licenses { 47 | license { 48 | name 'The Apache Software License, Version 2.0' 49 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 50 | } 51 | } 52 | developers { 53 | developer {//填写的一些基本信息 54 | id 'youth5201314' 55 | name 'spring' 56 | email '1028729086@qq.com' 57 | } 58 | } 59 | scm { 60 | connection gitUrl 61 | developerConnection gitUrl 62 | url siteUrl 63 | } 64 | } 65 | } 66 | } 67 | } 68 | task sourcesJar(type: Jar) { 69 | from android.sourceSets.main.java.srcDirs 70 | classifier = 'sources' 71 | } 72 | 73 | task javadoc(type: Javadoc) { 74 | source = android.sourceSets.main.java.srcDirs 75 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 76 | } 77 | 78 | task javadocJar(type: Jar, dependsOn: javadoc) { 79 | classifier = 'javadoc' 80 | from javadoc.destinationDir 81 | } 82 | 83 | artifacts { 84 | // archives javadocJar 85 | archives sourcesJar 86 | } 87 | 88 | Properties properties = new Properties() 89 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 90 | bintray { 91 | //读取Bintray帐号和密码。 92 | //一般的为了保密和安全性,在项目的local.properties文件中添加两行句话即可: 93 | //bintray.user=username 94 | //bintray.apikey=apikey 95 | user = properties.getProperty("bintray.user") 96 | key = properties.getProperty("bintray.apikey") 97 | configurations = ['archives'] 98 | pkg { 99 | repo = "maven" 100 | name = "banner" //发布到JCenter上的项目名字 101 | websiteUrl = siteUrl 102 | vcsUrl = gitUrl 103 | licenses = ["Apache-2.0"] 104 | publish = true 105 | } 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /banner/src/main/res/layout/banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 19 | 28 | 29 | 40 | 41 | 49 | 50 | 63 | 64 | 69 | 70 | 78 | 79 | 86 | 87 | 88 | 89 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/banner_custom_viewpager.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 15 | 19 | 20 | 29 | 30 | 41 | 42 | 50 | 51 | 64 | 65 | 70 | 71 | 79 | 80 | 87 | 88 | 89 | 90 | 96 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://ww4.sinaimg.cn/large/006uZZy8jw1faic1xjab4j30ci08cjrv.jpg 5 | http://ww4.sinaimg.cn/large/006uZZy8jw1faic21363tj30ci08ct96.jpg 6 | http://ww4.sinaimg.cn/large/006uZZy8jw1faic259ohaj30ci08c74r.jpg 7 | http://ww4.sinaimg.cn/large/006uZZy8jw1faic2b16zuj30ci08cwf4.jpg 8 | http://ww4.sinaimg.cn/large/006uZZy8jw1faic2e7vsaj30ci08cglz.jpg 9 | 10 | 11 | 12 | http://img.zcool.cn/community/01700557a7f42f0000018c1bd6eb23.jpg 13 | 14 | 15 | http://img.zcool.cn/community/01d28457d621800000018c1bb7877e.jpg 16 | http://img.zcool.cn/community/01ae5656e1427f6ac72531cb72bac5.jpg 17 | 18 | 19 | http://img.zcool.cn/community/01b72057a7e0790000018c1bf4fce0.png 20 | http://img.zcool.cn/community/01fca557a7f5f90000012e7e9feea8.jpg 21 | http://img.zcool.cn/community/01996b57a7f6020000018c1bedef97.jpg 22 | http://img.zcool.cn/community/01700557a7f42f0000018c1bd6eb23.jpg 23 | 24 | 25 | 26 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f19cc0079.jpg 27 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1ac12d1d.jpg 28 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1bad97d1.jpg 29 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1c83c228.jpg 30 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1d53e3dd.jpg 31 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1e37fea9.jpg 32 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f1ef4d709.jpg 33 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f20b3ea10.jpg 34 | http://bpic.588ku.com/element_origin_min_pic/00/00/05/115732f21927f8d.jpg 35 | 36 | 37 | 51巅峰钜惠 38 | 十大星级品牌联盟,全场2折起 39 | 生命不是要超越别人,而是要超越自己。 40 | 己所不欲,勿施于人。——孔子 41 | 嗨购5折不要停 42 | 43 | 44 | banner动画预览 45 | banner内置样式预览 46 | banner指示器位置设置预览 47 | banner一些自定义样式方法预览 48 | banner加载本地图片 49 | banner自定义布局文件(这里通过修改ViewPager举一反三吧) 50 | !!banner更多用法请看文档,这里就不一一列举了! 51 | 52 | 53 | Default 54 | Accordion 55 | BackgroundToForeground 56 | ForegroundToBackground 57 | CubeIn 58 | CubeOut 59 | DepthPage 60 | FlipHorizontal 61 | FlipVertical 62 | RotateDown 63 | RotateUp 64 | ScaleInOut 65 | Stack 66 | Tablet 67 | ZoomIn 68 | ZoomOut 69 | ZoomOutSlide 70 | 71 | 72 | NOT_INDICATOR 73 | CIRCLE_INDICATOR 74 | NUM_INDICATOR 75 | NUM_INDICATOR_TITLE 76 | CIRCLE_INDICATOR_TITLE 77 | CIRCLE_INDICATOR_TITLE_INSIDE 78 | 79 | 80 | LEFT 81 | CENTER 82 | RIGHT 83 | 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/demo/BannerAnimationActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.demo; 2 | 3 | import android.os.Bundle; 4 | import androidx.viewpager.widget.ViewPager; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.ListView; 9 | import android.widget.Toast; 10 | 11 | import com.test.banner.App; 12 | import com.test.banner.R; 13 | import com.test.banner.SampleAdapter; 14 | import com.test.banner.loader.GlideImageLoader; 15 | import com.youth.banner.Banner; 16 | import com.youth.banner.listener.OnBannerListener; 17 | import com.youth.banner.transformer.AccordionTransformer; 18 | import com.youth.banner.transformer.BackgroundToForegroundTransformer; 19 | import com.youth.banner.transformer.CubeInTransformer; 20 | import com.youth.banner.transformer.CubeOutTransformer; 21 | import com.youth.banner.transformer.DefaultTransformer; 22 | import com.youth.banner.transformer.DepthPageTransformer; 23 | import com.youth.banner.transformer.FlipHorizontalTransformer; 24 | import com.youth.banner.transformer.FlipVerticalTransformer; 25 | import com.youth.banner.transformer.ForegroundToBackgroundTransformer; 26 | import com.youth.banner.transformer.RotateDownTransformer; 27 | import com.youth.banner.transformer.RotateUpTransformer; 28 | import com.youth.banner.transformer.ScaleInOutTransformer; 29 | import com.youth.banner.transformer.StackTransformer; 30 | import com.youth.banner.transformer.TabletTransformer; 31 | import com.youth.banner.transformer.ZoomInTransformer; 32 | import com.youth.banner.transformer.ZoomOutSlideTransformer; 33 | import com.youth.banner.transformer.ZoomOutTranformer; 34 | 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | public class BannerAnimationActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, OnBannerListener { 39 | Banner banner; 40 | List> transformers=new ArrayList<>(); 41 | public void initData(){ 42 | transformers.add(DefaultTransformer.class); 43 | transformers.add(AccordionTransformer.class); 44 | transformers.add(BackgroundToForegroundTransformer.class); 45 | transformers.add(ForegroundToBackgroundTransformer.class); 46 | transformers.add(CubeInTransformer.class);//兼容问题,慎用 47 | transformers.add(CubeOutTransformer.class); 48 | transformers.add(DepthPageTransformer.class); 49 | transformers.add(FlipHorizontalTransformer.class); 50 | transformers.add(FlipVerticalTransformer.class); 51 | transformers.add(RotateDownTransformer.class); 52 | transformers.add(RotateUpTransformer.class); 53 | transformers.add(ScaleInOutTransformer.class); 54 | transformers.add(StackTransformer.class); 55 | transformers.add(TabletTransformer.class); 56 | transformers.add(ZoomInTransformer.class); 57 | transformers.add(ZoomOutTranformer.class); 58 | transformers.add(ZoomOutSlideTransformer.class); 59 | } 60 | 61 | @Override 62 | protected void onCreate(Bundle savedInstanceState) { 63 | super.onCreate(savedInstanceState); 64 | setContentView(R.layout.activity_banner_animation); 65 | initData(); 66 | banner = (Banner) findViewById(R.id.banner); 67 | ListView listView = (ListView) findViewById(R.id.list); 68 | String[] data = getResources().getStringArray(R.array.anim); 69 | listView.setAdapter(new SampleAdapter(this, data)); 70 | listView.setOnItemClickListener(this); 71 | 72 | banner.setImages(App.images) 73 | .setImageLoader(new GlideImageLoader()) 74 | .setOnBannerListener(this) 75 | .start(); 76 | 77 | } 78 | 79 | @Override 80 | public void onItemClick(AdapterView parent, View view, int position, long id) { 81 | banner.setBannerAnimation(transformers.get(position)); 82 | } 83 | 84 | @Override 85 | public void OnBannerClick(int position) { 86 | Toast.makeText(getApplicationContext(),"你点击了:"+position,Toast.LENGTH_SHORT).show(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/ui/RoundAngleImageView.java: -------------------------------------------------------------------------------- 1 | package com.test.banner.ui; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Paint; 10 | import android.graphics.Path; 11 | import android.graphics.PorterDuff; 12 | import android.graphics.PorterDuffXfermode; 13 | import android.graphics.RectF; 14 | import android.util.AttributeSet; 15 | import android.widget.ImageView; 16 | 17 | 18 | public class RoundAngleImageView extends ImageView { 19 | 20 | private Paint paint; 21 | private int roundWidth = 5; 22 | private int roundHeight = 5; 23 | private Paint paint2; 24 | 25 | public RoundAngleImageView(Context context, AttributeSet attrs, int defStyle) { 26 | super(context, attrs, defStyle); 27 | init(context, attrs); 28 | } 29 | 30 | public RoundAngleImageView(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | init(context, attrs); 33 | } 34 | 35 | public RoundAngleImageView(Context context) { 36 | super(context); 37 | init(context, null); 38 | } 39 | 40 | private void init(Context context, AttributeSet attrs) { 41 | paint = new Paint(); 42 | paint.setColor(Color.WHITE); 43 | paint.setAntiAlias(true); 44 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); 45 | paint2 = new Paint(); 46 | paint2.setXfermode(null); 47 | } 48 | 49 | @Override 50 | public void draw(Canvas canvas) { 51 | Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 52 | Canvas canvas2 = new Canvas(bitmap); 53 | super.draw(canvas2); 54 | drawLiftUp(canvas2); 55 | drawRightUp(canvas2); 56 | drawLiftDown(canvas2); 57 | drawRightDown(canvas2); 58 | canvas.drawBitmap(bitmap, 0, 0, paint2); 59 | bitmap.recycle(); 60 | } 61 | 62 | private void drawLiftUp(Canvas canvas) { 63 | Path path = new Path(); 64 | path.moveTo(0, roundHeight); 65 | path.lineTo(0, 0); 66 | path.lineTo(roundWidth, 0); 67 | path.arcTo(new RectF( 68 | 0, 69 | 0, 70 | roundWidth*2, 71 | roundHeight*2), 72 | -90, 73 | -90); 74 | path.close(); 75 | canvas.drawPath(path, paint); 76 | } 77 | 78 | private void drawLiftDown(Canvas canvas) { 79 | Path path = new Path(); 80 | path.moveTo(0, getHeight()-roundHeight); 81 | path.lineTo(0, getHeight()); 82 | path.lineTo(roundWidth, getHeight()); 83 | path.arcTo(new RectF( 84 | 0, 85 | getHeight()-roundHeight*2, 86 | 0+roundWidth*2, 87 | getHeight()), 88 | 90, 89 | 90); 90 | path.close(); 91 | canvas.drawPath(path, paint); 92 | } 93 | 94 | private void drawRightDown(Canvas canvas) { 95 | Path path = new Path(); 96 | path.moveTo(getWidth()-roundWidth, getHeight()); 97 | path.lineTo(getWidth(), getHeight()); 98 | path.lineTo(getWidth(), getHeight()-roundHeight); 99 | path.arcTo(new RectF( 100 | getWidth()-roundWidth*2, 101 | getHeight()-roundHeight*2, 102 | getWidth(), 103 | getHeight()), 0, 90); 104 | path.close(); 105 | canvas.drawPath(path, paint); 106 | } 107 | 108 | private void drawRightUp(Canvas canvas) { 109 | Path path = new Path(); 110 | path.moveTo(getWidth(), roundHeight); 111 | path.lineTo(getWidth(), 0); 112 | path.lineTo(getWidth()-roundWidth, 0); 113 | path.arcTo(new RectF( 114 | getWidth()-roundWidth*2, 115 | 0, 116 | getWidth(), 117 | 0+roundHeight*2), 118 | -90, 119 | 90); 120 | path.close(); 121 | canvas.drawPath(path, paint); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/banner/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.banner; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.AbsListView; 12 | import android.widget.AdapterView; 13 | import android.widget.ListView; 14 | import android.widget.Toast; 15 | 16 | import com.test.banner.demo.BannerAnimationActivity; 17 | import com.test.banner.demo.BannerLocalActivity; 18 | import com.test.banner.demo.BannerStyleActivity; 19 | import com.test.banner.demo.CustomBannerActivity; 20 | import com.test.banner.demo.CustomViewPagerActivity; 21 | import com.test.banner.demo.IndicatorPositionActivity; 22 | import com.test.banner.loader.GlideImageLoader; 23 | import com.youth.banner.Banner; 24 | import com.youth.banner.listener.OnBannerListener; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | 31 | public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener, OnBannerListener { 32 | static final int REFRESH_COMPLETE = 0X1112; 33 | SuperSwipeRefreshLayout mSwipeLayout; 34 | ListView listView; 35 | Banner banner; 36 | 37 | private Handler mHandler = new Handler() { 38 | public void handleMessage(android.os.Message msg) { 39 | switch (msg.what) { 40 | case REFRESH_COMPLETE: 41 | String[] urls = getResources().getStringArray(R.array.url4); 42 | List list = Arrays.asList(urls); 43 | List arrayList = new ArrayList(list); 44 | banner.update(arrayList); 45 | mSwipeLayout.setRefreshing(false); 46 | break; 47 | } 48 | } 49 | }; 50 | @Override 51 | protected void onCreate(Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_main); 54 | mSwipeLayout = (SuperSwipeRefreshLayout) findViewById(R.id.swipe); 55 | mSwipeLayout.setOnRefreshListener(this); 56 | listView = (ListView) findViewById(R.id.list); 57 | View header = LayoutInflater.from(this).inflate(R.layout.header, null); 58 | banner = (Banner) header.findViewById(R.id.banner); 59 | banner.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, App.H / 4)); 60 | listView.addHeaderView(banner); 61 | 62 | String[] data = getResources().getStringArray(R.array.demo_list); 63 | listView.setAdapter(new SampleAdapter(this,data)); 64 | listView.setOnItemClickListener(this); 65 | 66 | //简单使用 67 | banner.setImages(App.images) 68 | .setImageLoader(new GlideImageLoader()) 69 | .setOnBannerListener(this) 70 | .start(); 71 | 72 | } 73 | 74 | @Override 75 | public void OnBannerClick(int position) { 76 | Toast.makeText(getApplicationContext(),"你点击了:"+position,Toast.LENGTH_SHORT).show(); 77 | } 78 | 79 | 80 | //如果你需要考虑更好的体验,可以这么操作 81 | @Override 82 | protected void onStart() { 83 | super.onStart(); 84 | //开始轮播 85 | banner.startAutoPlay(); 86 | } 87 | 88 | @Override 89 | protected void onStop() { 90 | super.onStop(); 91 | //结束轮播 92 | banner.stopAutoPlay(); 93 | } 94 | 95 | 96 | @Override 97 | public void onRefresh() { 98 | mHandler.sendEmptyMessageDelayed(REFRESH_COMPLETE, 2000); 99 | } 100 | 101 | @Override 102 | public void onItemClick(AdapterView parent, View view, int position, long id) { 103 | switch (position){ 104 | case 1: 105 | startActivity(new Intent(this, BannerAnimationActivity.class)); 106 | break; 107 | case 2: 108 | startActivity(new Intent(this, BannerStyleActivity.class)); 109 | break; 110 | case 3: 111 | startActivity(new Intent(this, IndicatorPositionActivity.class)); 112 | break; 113 | case 4: 114 | startActivity(new Intent(this, CustomBannerActivity.class)); 115 | break; 116 | case 5: 117 | startActivity(new Intent(this, BannerLocalActivity.class)); 118 | break; 119 | case 6: 120 | startActivity(new Intent(this, CustomViewPagerActivity.class)); 121 | break; 122 | } 123 | } 124 | 125 | 126 | } 127 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/transformer/ABaseTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Toxic Bakery 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.youth.banner.transformer; 18 | 19 | import androidx.viewpager.widget.ViewPager.PageTransformer; 20 | import android.view.View; 21 | 22 | public abstract class ABaseTransformer implements PageTransformer { 23 | 24 | /** 25 | * Called each {@link #transformPage(View, float)}. 26 | * 27 | * @param page 28 | * Apply the transformation to this page 29 | * @param position 30 | * Position of page relative to the current front-and-center position of the pager. 0 is front and 31 | * center. 1 is one full page position to the right, and -1 is one page position to the left. 32 | */ 33 | protected abstract void onTransform(View page, float position); 34 | 35 | /** 36 | * Apply a property transformation to the given page. For most use cases, this method should not be overridden. 37 | * Instead use {@link #transformPage(View, float)} to perform typical transformations. 38 | * 39 | * @param page 40 | * Apply the transformation to this page 41 | * @param position 42 | * Position of page relative to the current front-and-center position of the pager. 0 is front and 43 | * center. 1 is one full page position to the right, and -1 is one page position to the left. 44 | */ 45 | @Override 46 | public void transformPage(View page, float position) { 47 | onPreTransform(page, position); 48 | onTransform(page, position); 49 | onPostTransform(page, position); 50 | } 51 | 52 | /** 53 | * If the position offset of a fragment is less than negative one or greater than one, returning true will set the 54 | * fragment alpha to 0f. Otherwise fragment alpha is always defaulted to 1f. 55 | * 56 | * @return 57 | */ 58 | protected boolean hideOffscreenPages() { 59 | return true; 60 | } 61 | 62 | /** 63 | * Indicates if the default animations of the view pager should be used. 64 | * 65 | * @return 66 | */ 67 | protected boolean isPagingEnabled() { 68 | return false; 69 | } 70 | 71 | /** 72 | * Called each {@link #transformPage(View, float)} before {{@link #onTransform(View, float)}. 73 | *

74 | * The default implementation attempts to reset all view properties. This is useful when toggling transforms that do 75 | * not modify the same page properties. For instance changing from a transformation that applies rotation to a 76 | * transformation that fades can inadvertently leave a fragment stuck with a rotation or with some degree of applied 77 | * alpha. 78 | * 79 | * @param page 80 | * Apply the transformation to this page 81 | * @param position 82 | * Position of page relative to the current front-and-center position of the pager. 0 is front and 83 | * center. 1 is one full page position to the right, and -1 is one page position to the left. 84 | */ 85 | protected void onPreTransform(View page, float position) { 86 | final float width = page.getWidth(); 87 | 88 | page.setRotationX(0); 89 | page.setRotationY(0); 90 | page.setRotation(0); 91 | page.setScaleX(1); 92 | page.setScaleY(1); 93 | page.setPivotX(0); 94 | page.setPivotY(0); 95 | page.setTranslationY(0); 96 | page.setTranslationX(isPagingEnabled() ? 0f : -width * position); 97 | 98 | if (hideOffscreenPages()) { 99 | page.setAlpha(position <= -1f || position >= 1f ? 0f : 1f); 100 | // page.setEnabled(false); 101 | } else { 102 | // page.setEnabled(true); 103 | page.setAlpha(1f); 104 | } 105 | } 106 | 107 | /** 108 | * Called each {@link #transformPage(View, float)} after {@link #onTransform(View, float)}. 109 | * 110 | * @param page 111 | * Apply the transformation to this page 112 | * @param position 113 | * Position of page relative to the current front-and-center position of the pager. 0 is front and 114 | * center. 1 is one full page position to the right, and -1 is one page position to the left. 115 | */ 116 | protected void onPostTransform(View page, float position) { 117 | } 118 | 119 | /** 120 | * Same as {@link Math#min(double, double)} without double casting, zero closest to infinity handling, or NaN support. 121 | * 122 | * @param val 123 | * @param min 124 | * @return 125 | */ 126 | protected static final float min(float val, float min) { 127 | return val < min ? min : val; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | 174 | yes | $ANDROID_HOME/tools/bin/sdkmanager "build-tools;28.0.3" -------------------------------------------------------------------------------- /banner/banner.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android图片轮播控件 2 | [![Apache 2.0 License](https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0.html) 3 | 4 | 5 | ## 新框架发布,欢迎大家Star 6 | 7 | [XFrame - Android快速开发框架](https://github.com/youth5201314/XFrame) 8 | 9 | [XFrame详细功能文档预览](https://github.com/youth5201314/XFrame/wiki) 10 | 11 | 12 |
13 | 14 | 现在的绝大数app都有banner界面,实现循环播放多个广告图片和手动滑动循环等功能。因为ViewPager并不支持循环翻页, 15 | 所以要实现循环还得需要自己去动手,我就把项目中的控件剔了出来,希望大家觉得有用。目前框架可以进行不同样式、不同动画设置, 16 | 以及完善的api方法能满足大部分的需求了。 17 | 18 | ## 效果图 19 | 20 | |模式|图片 21 | |---|---| 22 | |指示器模式|![效果示例](http://oceh51kku.bkt.clouddn.com/banner_example1.png)| 23 | |数字模式|![效果示例](http://oceh51kku.bkt.clouddn.com/banner_example2.png)| 24 | |数字加标题模式|![效果示例](http://oceh51kku.bkt.clouddn.com/banner_example3.png)| 25 | |指示器加标题模式
垂直显示|![效果示例](http://oceh51kku.bkt.clouddn.com/banner_example4.png)| 26 | |指示器加标题模式
水平显示|![效果示例](http://oceh51kku.bkt.clouddn.com/banner_example5.png)| 27 | 28 | ### 联系方式 29 | ![效果示例](http://oceh51kku.bkt.clouddn.com/Android%E6%8A%80%E6%9C%AF%E4%BA%A4%E6%B5%81%E7%BE%A4%E4%BA%8C%E7%BB%B4%E7%A0%81.png) 30 | * 如果有问题可以加群大家一起交流 31 | * 我的个人微博:https://weibo.com/u/3013494003 有兴趣的也可以关注,大家一起交流 32 | 33 | ## 常量 34 | |常量名称|描述|所属方法 35 | |---|---|---| 36 | |BannerConfig.NOT_INDICATOR| 不显示指示器和标题|setBannerStyle 37 | |BannerConfig.CIRCLE_INDICATOR| 显示圆形指示器|setBannerStyle 38 | |BannerConfig.NUM_INDICATOR| 显示数字指示器|setBannerStyle 39 | |BannerConfig.NUM_INDICATOR_TITLE| 显示数字指示器和标题|setBannerStyle 40 | |BannerConfig.CIRCLE_INDICATOR_TITLE| 显示圆形指示器和标题(垂直显示)|setBannerStyle 41 | |BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE| 显示圆形指示器和标题(水平显示)|setBannerStyle 42 | |BannerConfig.LEFT| 指示器居左|setIndicatorGravity 43 | |BannerConfig.CENTER| 指示器居中|setIndicatorGravity 44 | |BannerConfig.RIGHT| 指示器居右|setIndicatorGravity 45 | 46 | ## 动画常量类(setBannerAnimation方法调用) 47 | [ViewPagerTransforms](https://github.com/ToxicBakery/ViewPagerTransforms) `动画时集成的第三方库,可能有兼容问题导致position位置不准确,你可以选择参考动画然后自定义动画` 48 | 49 | |常量类名| 50 | |---| 51 | |Transformer.Default| 52 | |Transformer.Accordion| 53 | |Transformer.BackgroundToForeground| 54 | |Transformer.ForegroundToBackground| 55 | |Transformer.CubeIn| 56 | |Transformer.CubeOut| 57 | |Transformer.DepthPage| 58 | |Transformer.FlipHorizontal| 59 | |Transformer.FlipVertical| 60 | |Transformer.RotateDown| 61 | |Transformer.RotateUp| 62 | |Transformer.ScaleInOut| 63 | |Transformer.Stack| 64 | |Transformer.Tablet| 65 | |Transformer.ZoomIn| 66 | |Transformer.ZoomOut| 67 | |Transformer.ZoomOutSlide| 68 | 69 | 70 | ## 方法 71 | |方法名|描述|版本限制 72 | |---|---|---| 73 | |setBannerStyle(int bannerStyle)| 设置轮播样式(默认为CIRCLE_INDICATOR)|无 74 | |setIndicatorGravity(int type)| 设置指示器位置(没有标题默认为右边,有标题时默认左边)|无 75 | |isAutoPlay(boolean isAutoPlay)| 设置是否自动轮播(默认自动)|无 76 | |setViewPagerIsScroll(boolean isScroll)| 设置是否允许手动滑动轮播图(默认true)|1.4.5开始 77 | |update(List imageUrls,List titles)| 更新图片和标题 |1.4.5开始 78 | |update(List imageUrls)| 更新图片 |1.4.5开始 79 | |startAutoPlay()|开始轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行 80 | |stopAutoPlay()|结束轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行 81 | |start()|开始进行banner渲染(必须放到最后执行)|1.4开始 82 | |setOffscreenPageLimit(int limit)|同viewpager的方法作用一样|1.4.2开始 83 | |setBannerTitle(String[] titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束 84 | |setBannerTitleList(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束 85 | |setBannerTitles(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.4开始 86 | |setDelayTime(int time)| 设置轮播图片间隔时间(单位毫秒,默认为2000)|无 87 | |setImages(Object[]/List imagesUrl)| 设置轮播图片(所有设置参数方法都放在此方法之前执行)|1.4后去掉数组传参 88 | |setImages(Object[]/List imagesUrl,OnLoadImageListener listener)| 设置轮播图片,并且自定义图片加载方式|1.3.3结束 89 | |setOnBannerClickListener(this)|设置点击事件,下标是从1开始|无(1.4.9以后废弃了) 90 | |setOnBannerListener(this)|设置点击事件,下标是从0开始|1.4.9以后 91 | |setOnLoadImageListener(this)|设置图片加载事件,可以自定义图片加载方式|1.3.3结束 92 | |setImageLoader(Object implements ImageLoader)|设置图片加载器|1.4开始 93 | |setOnPageChangeListener(this)|设置viewpager的滑动监听|无 94 | |setBannerAnimation(Class transformer)|设置viewpager的默认动画,传值见动画表|无 95 | |setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)|设置viewpager的自定义动画|无 96 | 97 | ## Attributes属性(banner布局文件中调用) 98 | |Attributes|forma|describe 99 | |---|---|---| 100 | |delay_time| integer|轮播间隔时间,默认2000 101 | |scroll_time| integer|轮播滑动执行时间,默认800 102 | |is_auto_play| boolean|是否自动轮播,默认true 103 | |title_background| color|reference|标题栏的背景色 104 | |title_textcolor| color|标题字体颜色 105 | |title_textsize| dimension|标题字体大小 106 | |title_height| dimension|标题栏高度 107 | |indicator_width| dimension|指示器圆形按钮的宽度 108 | |indicator_height| dimension|指示器圆形按钮的高度 109 | |indicator_margin| dimension|指示器之间的间距 110 | |indicator_drawable_selected| reference|指示器选中效果 111 | |indicator_drawable_unselected| reference|指示器未选中效果 112 | |image_scale_type| enum |和imageview的ScaleType作用一样 113 | |banner_default_image| reference | 当banner数据为空是显示的默认图片 114 | |banner_layout| reference |自定义banner布局文件,但是必须保证id的名称一样(你可以将banner的布局文件复制出来进行修改) 115 | 116 | 117 | ### [ 点击查看 ViewPager的PageTransformer用法 ] 118 | 119 | 120 | ## 使用步骤 121 | 122 | #### Step 1.依赖banner 123 | Gradle 124 | ```groovy 125 | dependencies{ 126 | compile 'com.youth.banner:banner:1.4.10' //最新版本 127 | } 128 | ``` 129 | 或者引用本地lib 130 | ```groovy 131 | compile project(':banner') 132 | ``` 133 | 134 | 135 | #### Step 2.添加权限到你的 AndroidManifest.xml 136 | ```xml 137 | 138 | 139 | 140 | 141 | 142 | ``` 143 | 144 | #### Step 3.在布局文件中添加Banner,可以设置自定义属性 145 | !!!此步骤可以省略,直接在Activity或者Fragment中new Banner(); 146 | ```xml 147 | 152 | ``` 153 | 154 | #### Step 4.重写图片加载器 155 | ```java 156 | public class GlideImageLoader extends ImageLoader { 157 | @Override 158 | public void displayImage(Context context, Object path, ImageView imageView) { 159 | /** 160 | 注意: 161 | 1.图片加载器由自己选择,这里不限制,只是提供几种使用方法 162 | 2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器, 163 | 传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行, 164 | 切记不要胡乱强转! 165 | */ 166 | eg: 167 | 168 | //Glide 加载图片简单用法 169 | Glide.with(context).load(path).into(imageView); 170 | 171 | //Picasso 加载图片简单用法 172 | Picasso.with(context).load(path).into(imageView); 173 | 174 | //用fresco加载图片简单用法,记得要写下面的createImageView方法 175 | Uri uri = Uri.parse((String) path); 176 | imageView.setImageURI(uri); 177 | } 178 | 179 | //提供createImageView 方法,如果不用可以不重写这个方法,主要是方便自定义ImageView的创建 180 | @Override 181 | public ImageView createImageView(Context context) { 182 | //使用fresco,需要创建它提供的ImageView,当然你也可以用自己自定义的具有图片加载功能的ImageView 183 | SimpleDraweeView simpleDraweeView=new SimpleDraweeView(context); 184 | return simpleDraweeView; 185 | } 186 | } 187 | ``` 188 | 189 | #### Step 5.在Activity或者Fragment中配置Banner 190 | 191 | - 注意!start()方法必须放到最后执行,点击事件请放到start()前,每次都提交问题问为什么点击没有反应?需要轮播一圈才能点击?点击第一个怎么返回1?麻烦仔细阅读文档。 192 | 193 | ```java 194 | --------------------------简单使用------------------------------- 195 | @Override 196 | protected void onCreate(Bundle savedInstanceState) { 197 | super.onCreate(savedInstanceState); 198 | setContentView(R.layout.activity_main); 199 | Banner banner = (Banner) findViewById(R.id.banner); 200 | //设置图片加载器 201 | banner.setImageLoader(new GlideImageLoader()); 202 | //设置图片集合 203 | banner.setImages(images); 204 | //banner设置方法全部调用完毕时最后调用 205 | banner.start(); 206 | } 207 | --------------------------详细使用------------------------------- 208 | @Override 209 | protected void onCreate(Bundle savedInstanceState) { 210 | super.onCreate(savedInstanceState); 211 | setContentView(R.layout.activity_main); 212 | Banner banner = (Banner) findViewById(R.id.banner); 213 | //设置banner样式 214 | banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE); 215 | //设置图片加载器 216 | banner.setImageLoader(new GlideImageLoader()); 217 | //设置图片集合 218 | banner.setImages(images); 219 | //设置banner动画效果 220 | banner.setBannerAnimation(Transformer.DepthPage); 221 | //设置标题集合(当banner样式有显示title时) 222 | banner.setBannerTitles(titles); 223 | //设置自动轮播,默认为true 224 | banner.isAutoPlay(true); 225 | //设置轮播时间 226 | banner.setDelayTime(1500); 227 | //设置指示器位置(当banner模式中有指示器时) 228 | banner.setIndicatorGravity(BannerConfig.CENTER); 229 | //banner设置方法全部调用完毕时最后调用 230 | banner.start(); 231 | } 232 | -----------------当然如果你想偷下懒也可以这么用-------------------- 233 | @Override 234 | protected void onCreate(Bundle savedInstanceState) { 235 | super.onCreate(savedInstanceState); 236 | setContentView(R.layout.activity_main); 237 | Banner banner = (Banner) findViewById(R.id.banner); 238 | banner.setImages(images).setImageLoader(new GlideImageLoader()).start(); 239 | } 240 | ``` 241 | 242 | #### Step 6.(可选)增加体验 243 | ```java 244 | //如果你需要考虑更好的体验,可以这么操作 245 | @Override 246 | protected void onStart() { 247 | super.onStart(); 248 | //开始轮播 249 | banner.startAutoPlay(); 250 | } 251 | 252 | @Override 253 | protected void onStop() { 254 | super.onStop(); 255 | //结束轮播 256 | banner.stopAutoPlay(); 257 | } 258 | ``` 259 | 260 | ## 混淆代码 261 | ```java 262 | # glide 的混淆代码 263 | -keep public class * implements com.bumptech.glide.module.GlideModule 264 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 265 | **[] $VALUES; 266 | public *; 267 | } 268 | # banner 的混淆代码 269 | -keep class com.youth.banner.** { 270 | *; 271 | } 272 | 273 | ``` 274 | 275 | [历史版本资源地址] 276 | 277 | [1.3.3以前旧版本使用文档地址] 278 | 279 | ## 常见问题 280 | 281 | * 问:eclipse怎么使用banner? 282 | 283 | * 答:`在历史版本列表中下载你想要版本的aar包提取最新资源/也可以自己把工程转成eclipse的`
284 | eclipse的集成demo群文件里有共享! 285 | 286 | * 问:怎么显示的一片空白? 287 | * 答:
288 | 1、没有添加网络权限
289 | 2、检查图片链接是否能打开。 290 | * 问:怎么加载其他图片资源(资源文件、文件、Uri、assets、raw、ContentProvider、sd卡资源)? 291 | * 答:列如!如果你使用的是glide,那么可以如下操作,其他图片图片加载框架可能有不同 292 | ```java 293 | //资源文件 294 | Integer[] images={R.mipmap.a,R.mipmap.b,R.mipmap.c}; 295 | //Uri 296 | Uri uri = resourceIdToUri(context, R.mipmap.ic_launcher); 297 | Uri[] images={uri}; 298 | //文件对象 299 | File[] images={"文件对象","文件对象"}; 300 | //raw 两种方式 301 | String[] images={"Android.resource://com.frank.glide/raw/raw_1"}; 302 | String[] images={"android.resource://com.frank.glide/raw/"+R.raw.raw_1"}; 303 | //ContentProvider 304 | String[] images={"content://media/external/images/media/139469"}; 305 | //assets 306 | String[] images={"file:///android_asset/f003.gif"}; 307 | //sd卡资源 308 | String[] images={"file://"+ Environment.getExternalStorageDirectory().getPath()+"/test.jpg"}; 309 | 310 | banner.setImages(images);//这里接收集合,上面写成集合太占地方,这个大家举一反三就行了啊 311 | ``` 312 | 313 | * 问:设置banner指示器颜色怎么变成方的了? 314 | 315 | * 答:首先我先要说很多软件的指示器也是矩形的,然后banner的指示器可以设置color、资源图片、drawable文件夹自定义shape , 316 | 所以形状你自己可以根据需求定义哦! 317 | 318 | * 问:为什么banner的点击事件没有反应,需要下一次轮播才行?点击第一个图片怎么返回1? 319 | 320 |     * 答:请将点击事件放在start方法之前执行,start必须放到最后执行,详情可以看demo。 321 | 322 | ## Thanks 323 | 324 | - [ViewPagerTransforms](https://github.com/ToxicBakery/ViewPagerTransforms) 325 | 326 | ## 更新说明 327 | 328 | #### v1.4.10 329 | 很久没有维护banner了,有工作原因比较忙,也有经常遇见一些素质低的人,感觉整个世界都欠他们的,特别影响心情。就放弃更新维护了, 330 | 但是这半年每天邮箱都会收到各种建议反馈,也有很多人私信我,所以在此修复一些当前版本bug, 331 | 关于有朋友要求让轮播类型可以自定义,不局限于imageview的需求, 332 | 这个过段时间再发布一个全新的banner版本,会更加灵活,就不在原来的上面弄了,到时候分两个版本走! 333 | 334 | * 解决轮播手动滑动跳转问题:从第一张-->最后一张-->直接跳转到第二张 335 | * 解决update刷新轮播图崩溃问题 336 | * 将onPageScrolled和onPageSelected方法返回的position转成真实的position 337 | * 增加属性banner_default_image,设置当banner数据为空是显示的默认图片 338 | * 增加属性banner_layout,可以自定义布局文件,但是必须保证id的名称一样 339 | * 修改ViewPager偶发性的越界问题 340 | * SwipeRefreshLayout嵌套ViewPager的滑动冲突问题参考demo的SuperSwipeRefreshLayout类 341 | 342 | #### v1.4.9 343 | banner 优化更新 344 | * 废弃以前的点击事件(当然还是可以使用以前的方法),增加新的setOnBannerListener点击事件,下标从0开始 345 | * 解决update刷新轮播图后,会造成多次调用OnPageChangeListener的情况 346 | * 改变布局文件变量名,减少和工程冲突 347 | 348 | #### v1.4.8 349 | banner 优化更新 350 | * 修改点击事件返回下标偶尔越界问题 351 | 352 | #### v1.4.7 353 | banner 优化更新 354 | * 修复从第一个到最后一个,和从最后一个到第一个,数字和标题切换有点延迟的问题 355 | 356 | #### v1.4.6 357 | banner 优化更新 358 | * 修改demo,更容易理解 359 | * 修复第一张过渡第二张图片切换时间翻倍问题 360 | * 图片默认全屏展示 361 | 362 | #### v1.4.5 363 | banner 优化更新 364 | * 增加setViewPagerIsScroll(boolean isScroll)方法控制是否允许手动滑动轮播图,默认为true 365 | * 增加update()方法,方便更新图片 366 | * 解决最后一张图片切换到第一张,会出现卡顿(特别是不设置动画时有点明显) 367 | 368 | #### v1.4.3-1.4.4 369 | banner bug修改 370 | * 轮播图变少时刷新崩溃问题 371 | * 增加控制图片显示属性 image_scale_type 的属性值(center,center_crop,center_inside,fit_center,fit_end,fit_start,fit_xy,matrix),和 ImageView 的效果一样 372 | * 当只有一张图片时不显示圆形指示器和数字指示器 373 | 374 | #### v1.4.2 375 | banner优化更新<感谢 694551594,FeverCombo3,MIkeeJY > 376 | * !!!注意!!ImageLoader已从接口改变成抽象类,请调整下代码哦! 377 | * ImageLoader中增加ImageView控件创建方法createImageView(),可以满足fresco加载图片时扩展ImageView需求 378 | * 修改关于banner刷新时需要第二轮才会更新图片问题(同title更新图片不更新问题),具体看demo 379 | * 开放viewpager的setOffscreenPageLimit(int limit)方法 380 | * 优化banner在开始0s~20s之间会出现的内存泄漏问题 381 | * 优化最后一张到第一张之间滑动卡顿现象 382 | 383 | #### v1.4.1 384 | bug修改<感谢深圳-放飞,台北-Tom> 385 | * 第一次加载一张图片(不能滑动[正常])-->刷新-->第二次加载多张图片(不能滑动[bug]) 386 | * 滑动事件传递拦截优化 387 | * demo里添加了下拉刷新和RecyclerView添加头部的两种方式 388 | 389 | #### v1.4 390 | 全新升级,此次更新比较大,如果不习惯使用1.4的还是可以用1.3.3 391 | * 去掉app:default_image="默认加载图片",需要可以自己在图片加载器中设置 392 | * 去掉glide图片加载相关代码全部用户自定义,外部通过实现(ImageLoader)去加载图片,尽力减少对第三方库的依赖 393 | * 去掉OnLoadImageListener图片加载监听事件,增加ImageLoader接口,通过setImageLoader设置图片加载器 394 | * 去掉isAutoPlay方法,改用startAutoPlay()|stopAutoPlay()这两个方法只能是渲染完(start())后调用 395 | * 调整代码结构和执行顺序,由start()进行最后渲染和逻辑判断,前面方法可以随意调用打乱顺序。 396 | * 将设置图片和标题的方法改成setImages和setBannerTitles,传参方式改成集合,如果要用数组可以Arrays.asList()转成集合使用 397 | * 调整默认样式为CIRCLE_INDICATOR 398 | * 禁止单张轮播手动滑动问题 399 | * banner的标题文字单位指定为TypedValue.COMPLEX_UNIT_PX 400 | * demo改版,如果需要1.3.3的demo请在QQ群中下载 401 | 402 | 403 | #### v1.3.3 404 | 优化轮播首尾过渡时间 405 | * 再实现轮播从最后一张到第一张时,在第一张前面加了一张图片用于过渡,保证轮播不太生硬。这样也就造成了第一张时间有点长, 406 | 开始没有发现,感谢大家的反馈,现在简单优化了下依然保留第一张500毫秒的时间用于过渡,让轮播保证流畅性。相信500毫秒的时间 407 | 对于效果没有什么影响,这段时间很忙后面会对算法进行修改,目前先这样用着吧。 408 | 409 | #### v1.3.2 410 | 修复bug 411 | * 解决在自动轮播中,轮播中途触摸图片/左右移动时停止轮播,抬起不自动轮播问题 412 | 413 | #### v1.3.1 414 | 修复bug 415 | * app:delay_time="轮播间隔时间" 参数无用问题 416 | * 在暂停轮播时,当你手动滑动时会重新开始轮播问题 417 | * 在轮播中,当你按住轮播时暂停,松开后不会轮播问题 418 | 419 | #### v1.2.9 420 | 修复bug以及更新功能 421 | * app:image_scale_type="fit_xy,和imageview的ScaleType作用一样,不过只提供了两个常用的" 422 | * 修复设置动画后点击事件失效的问题。 423 | * 取消setScrollerTime设置方法 424 | 425 | #### v1.2.8 426 | 增加ViewPager的切换速度设置方法,以及动画的重新封装 427 | * 整理了17种viewpager过渡动画,并整理为常量方便调用,改变了传参格式,如果不够用可以自行自定义动画 428 | * 增加setScrollerTime(int duration)设置viewpager的切换速度,(单位毫秒,默认800) 429 | 430 | #### v1.2.7 431 | 增加viewpager的切换默认几种动画,和自定义动画方法 432 | * setBannerAnimation(int type)设置viewpager的默认动画 433 | * setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)设置viewpager的自定义动画 434 | 435 | #### v1.2.5 436 | 修改bug 437 | * app:title_height="标题栏高度",高度过小文字不显示问题 438 | 439 | #### v1.2.4 440 | 优化更新 441 | * app:title_background="标题栏的背景色" 442 | * app:title_height="标题栏高度" 443 | * app:title_textcolor="标题字体颜色" 444 | * app:title_textsize="标题字体大小" 445 | 446 | #### v1.2.3 447 | 优化更新 448 | * 修复刷新banner从多张到1张时,还出现滑动的问题 449 | * demo增加功能 450 | 451 | #### v1.2.2 452 | 优化更新 453 | * 增加BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE显示圆形指示器和标题(水平显示) 454 | * 修改数字指示器时,变形问题。 455 | 456 | #### v1.2.1 457 | 优化更新 458 | * 修复NUM_INDICATOR和NUM_INDICATOR_TITLE模式下,没有轮播初始化为“1/1”的情况 459 | * 将图片加载默认图片取消,开发者可根据需要设置 460 | 461 | #### v1.2.0 462 | 优化更新 463 | * 修复小图片每次轮播被放大的问题 464 | * 开放了viewpager的滑动事件setOnPageChangeListener() 465 | * 增加触摸轮播图时暂停轮播,离开时继续轮播 466 | * 增加demo代码解释,关于刷新下标越界问题这个不存在,不懂的请看demo, 467 | 不要一出问题就认为是banner的错,看看自己的用法是不是出来问题。 468 | 469 | #### v1.1.9 470 | 优化更新 471 | * 当图片为一张时,禁止轮播 472 | * 优化标题初始化速度 473 | 474 | #### v1.1.8 475 | bug修改 476 | * 可能的存在的图片拉伸问题,替换为glide的图片加载大小计算 477 | * 修改关于非arraylist集合的强转问题。 478 | 479 | #### v1.1.7 480 | 应朋友的要求,做出更新 481 | * 为标题增加设置集合的方法:setBannerTitleList(List titles) 482 | 483 | #### v1.1.6 484 | 综合大家的反馈,做出一些更新 485 | * 将代码的常量全部提出到BannerConfig类里了,以前的代码,大家需要修改下 486 | * 有人喜欢xml属性的方式来设置参数,那么增加了几个xml属性 487 | app:default_image="默认加载图片" 488 | app:delay_time="轮播间隔时间" 489 | app:is_auto_play="是否自动轮播" 490 | * 增加了设置glide加载方式的默认加载图片方法 491 | app:default_image="默认加载图片" 492 | * 重新写了一下demo,方便大家更加容易懂 493 | 494 | #### v1.1.5 495 | 感谢朋友的反馈 496 | * 创建指示器初始化时默认的背景的添加,减少延迟等待更新 497 | * 优化指示器背景更新操作 498 | 499 | #### v1.1.4 500 | 更新内容 501 | * 增加setImages传参可以接收list集合 502 | * 优化在添加数据和创建指示器时的对象内存回收 503 | 504 | #### v1.1.3 505 | 修复了 <2316692710@qq.com> 朋友反馈的bug: 506 | * bug① 有标题的时候,向左滑动 ,会数组越界崩溃 507 | * bug② 指示器为数字的时候,向左滑动时会有一次显示为0/5 508 | 509 | #### v1.1.2 510 | 感谢 朋友提的意见,做出了如下更改: 511 | * 增加设置轮播图片,并且自定义图片加载方式:setImages(Object[] imagesUrl,OnLoadImageListener listener) 512 | * 增加设置图片加载事件,可以自定义图片加载方式:setOnBannerImageListener(this) 513 | 514 | #### v1.1.1 515 | 感谢 <969482412@qq.com> 朋友提的意见,做出了如下更改: 516 | * 增加圆形指示器的位置方法setIndicatorGravity(int type) 517 | * 增加设置是否自动轮播的方法isAutoPlay(boolean isAutoPlay) 518 | 519 | #### v1.1.0 520 | 感谢 <997058003@qq.com> 朋友提的意见,做出了如下更改: 521 | * 修改指示器样式 522 | * 增加5种轮播样式,更加灵活方便的运用轮播控件,满足项目需求 523 | 524 | 525 | 526 | -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/WeakHandler.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | import androidx.annotation.VisibleForTesting; 9 | 10 | import java.lang.ref.WeakReference; 11 | import java.util.concurrent.locks.Lock; 12 | import java.util.concurrent.locks.ReentrantLock; 13 | 14 | @SuppressWarnings("unused") 15 | public class WeakHandler { 16 | private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory 17 | private final ExecHandler mExec; 18 | private Lock mLock = new ReentrantLock(); 19 | @SuppressWarnings("ConstantConditions") 20 | @VisibleForTesting 21 | final ChainedRef mRunnables = new ChainedRef(mLock, null); 22 | 23 | /** 24 | * Default constructor associates this handler with the {@link Looper} for the 25 | * current thread. 26 | * 27 | * If this thread does not have a looper, this handler won't be able to receive messages 28 | * so an exception is thrown. 29 | */ 30 | public WeakHandler() { 31 | mCallback = null; 32 | mExec = new ExecHandler(); 33 | } 34 | 35 | /** 36 | * Constructor associates this handler with the {@link Looper} for the 37 | * current thread and takes a callback interface in which you can handle 38 | * messages. 39 | * 40 | * If this thread does not have a looper, this handler won't be able to receive messages 41 | * so an exception is thrown. 42 | * 43 | * @param callback The callback interface in which to handle messages, or null. 44 | */ 45 | public WeakHandler(@Nullable Handler.Callback callback) { 46 | mCallback = callback; // Hard referencing body 47 | mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler 48 | } 49 | 50 | /** 51 | * Use the provided {@link Looper} instead of the default one. 52 | * 53 | * @param looper The looper, must not be null. 54 | */ 55 | public WeakHandler(@NonNull Looper looper) { 56 | mCallback = null; 57 | mExec = new ExecHandler(looper); 58 | } 59 | 60 | /** 61 | * Use the provided {@link Looper} instead of the default one and take a callback 62 | * interface in which to handle messages. 63 | * 64 | * @param looper The looper, must not be null. 65 | * @param callback The callback interface in which to handle messages, or null. 66 | */ 67 | public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { 68 | mCallback = callback; 69 | mExec = new ExecHandler(looper, new WeakReference<>(callback)); 70 | } 71 | 72 | /** 73 | * Causes the Runnable r to be added to the message queue. 74 | * The runnable will be run on the thread to which this handler is 75 | * attached. 76 | * 77 | * @param r The Runnable that will be executed. 78 | * 79 | * @return Returns true if the Runnable was successfully placed in to the 80 | * message queue. Returns false on failure, usually because the 81 | * looper processing the message queue is exiting. 82 | */ 83 | public final boolean post(@NonNull Runnable r) { 84 | return mExec.post(wrapRunnable(r)); 85 | } 86 | 87 | /** 88 | * Causes the Runnable r to be added to the message queue, to be run 89 | * at a specific time given by uptimeMillis. 90 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 91 | * The runnable will be run on the thread to which this handler is attached. 92 | * 93 | * @param r The Runnable that will be executed. 94 | * @param uptimeMillis The absolute time at which the callback should run, 95 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 96 | * 97 | * @return Returns true if the Runnable was successfully placed in to the 98 | * message queue. Returns false on failure, usually because the 99 | * looper processing the message queue is exiting. Note that a 100 | * result of true does not mean the Runnable will be processed -- if 101 | * the looper is quit before the delivery time of the message 102 | * occurs then the message will be dropped. 103 | */ 104 | public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { 105 | return mExec.postAtTime(wrapRunnable(r), uptimeMillis); 106 | } 107 | 108 | /** 109 | * Causes the Runnable r to be added to the message queue, to be run 110 | * at a specific time given by uptimeMillis. 111 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 112 | * The runnable will be run on the thread to which this handler is attached. 113 | * 114 | * @param r The Runnable that will be executed. 115 | * @param uptimeMillis The absolute time at which the callback should run, 116 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 117 | * 118 | * @return Returns true if the Runnable was successfully placed in to the 119 | * message queue. Returns false on failure, usually because the 120 | * looper processing the message queue is exiting. Note that a 121 | * result of true does not mean the Runnable will be processed -- if 122 | * the looper is quit before the delivery time of the message 123 | * occurs then the message will be dropped. 124 | * 125 | * @see android.os.SystemClock#uptimeMillis 126 | */ 127 | public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { 128 | return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); 129 | } 130 | 131 | /** 132 | * Causes the Runnable r to be added to the message queue, to be run 133 | * after the specified amount of time elapses. 134 | * The runnable will be run on the thread to which this handler 135 | * is attached. 136 | * 137 | * @param r The Runnable that will be executed. 138 | * @param delayMillis The delay (in milliseconds) until the Runnable 139 | * will be executed. 140 | * 141 | * @return Returns true if the Runnable was successfully placed in to the 142 | * message queue. Returns false on failure, usually because the 143 | * looper processing the message queue is exiting. Note that a 144 | * result of true does not mean the Runnable will be processed -- 145 | * if the looper is quit before the delivery time of the message 146 | * occurs then the message will be dropped. 147 | */ 148 | public final boolean postDelayed(Runnable r, long delayMillis) { 149 | return mExec.postDelayed(wrapRunnable(r), delayMillis); 150 | } 151 | 152 | /** 153 | * Posts a message to an object that implements Runnable. 154 | * Causes the Runnable r to executed on the next iteration through the 155 | * message queue. The runnable will be run on the thread to which this 156 | * handler is attached. 157 | * This method is only for use in very special circumstances -- it 158 | * can easily starve the message queue, cause ordering problems, or have 159 | * other unexpected side-effects. 160 | * 161 | * @param r The Runnable that will be executed. 162 | * 163 | * @return Returns true if the message was successfully placed in to the 164 | * message queue. Returns false on failure, usually because the 165 | * looper processing the message queue is exiting. 166 | */ 167 | public final boolean postAtFrontOfQueue(Runnable r) { 168 | return mExec.postAtFrontOfQueue(wrapRunnable(r)); 169 | } 170 | 171 | /** 172 | * Remove any pending posts of Runnable r that are in the message queue. 173 | */ 174 | public final void removeCallbacks(Runnable r) { 175 | final WeakRunnable runnable = mRunnables.remove(r); 176 | if (runnable != null) { 177 | mExec.removeCallbacks(runnable); 178 | } 179 | } 180 | 181 | /** 182 | * Remove any pending posts of Runnable r with Object 183 | * token that are in the message queue. If token is null, 184 | * all callbacks will be removed. 185 | */ 186 | public final void removeCallbacks(Runnable r, Object token) { 187 | final WeakRunnable runnable = mRunnables.remove(r); 188 | if (runnable != null) { 189 | mExec.removeCallbacks(runnable, token); 190 | } 191 | } 192 | 193 | /** 194 | * Pushes a message onto the end of the message queue after all pending messages 195 | * before the current time. It will be received in callback, 196 | * in the thread attached to this handler. 197 | * 198 | * @return Returns true if the message was successfully placed in to the 199 | * message queue. Returns false on failure, usually because the 200 | * looper processing the message queue is exiting. 201 | */ 202 | public final boolean sendMessage(Message msg) { 203 | return mExec.sendMessage(msg); 204 | } 205 | 206 | /** 207 | * Sends a Message containing only the what value. 208 | * 209 | * @return Returns true if the message was successfully placed in to the 210 | * message queue. Returns false on failure, usually because the 211 | * looper processing the message queue is exiting. 212 | */ 213 | public final boolean sendEmptyMessage(int what) { 214 | return mExec.sendEmptyMessage(what); 215 | } 216 | 217 | /** 218 | * Sends a Message containing only the what value, to be delivered 219 | * after the specified amount of time elapses. 220 | * @see #sendMessageDelayed(android.os.Message, long) 221 | * 222 | * @return Returns true if the message was successfully placed in to the 223 | * message queue. Returns false on failure, usually because the 224 | * looper processing the message queue is exiting. 225 | */ 226 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 227 | return mExec.sendEmptyMessageDelayed(what, delayMillis); 228 | } 229 | 230 | /** 231 | * Sends a Message containing only the what value, to be delivered 232 | * at a specific time. 233 | * @see #sendMessageAtTime(android.os.Message, long) 234 | * 235 | * @return Returns true if the message was successfully placed in to the 236 | * message queue. Returns false on failure, usually because the 237 | * looper processing the message queue is exiting. 238 | */ 239 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 240 | return mExec.sendEmptyMessageAtTime(what, uptimeMillis); 241 | } 242 | 243 | /** 244 | * Enqueue a message into the message queue after all pending messages 245 | * before (current time + delayMillis). You will receive it in 246 | * callback, in the thread attached to this handler. 247 | * 248 | * @return Returns true if the message was successfully placed in to the 249 | * message queue. Returns false on failure, usually because the 250 | * looper processing the message queue is exiting. Note that a 251 | * result of true does not mean the message will be processed -- if 252 | * the looper is quit before the delivery time of the message 253 | * occurs then the message will be dropped. 254 | */ 255 | public final boolean sendMessageDelayed(Message msg, long delayMillis) { 256 | return mExec.sendMessageDelayed(msg, delayMillis); 257 | } 258 | 259 | /** 260 | * Enqueue a message into the message queue after all pending messages 261 | * before the absolute time (in milliseconds) uptimeMillis. 262 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 263 | * You will receive it in callback, in the thread attached 264 | * to this handler. 265 | * 266 | * @param uptimeMillis The absolute time at which the message should be 267 | * delivered, using the 268 | * {@link android.os.SystemClock#uptimeMillis} time-base. 269 | * 270 | * @return Returns true if the message was successfully placed in to the 271 | * message queue. Returns false on failure, usually because the 272 | * looper processing the message queue is exiting. Note that a 273 | * result of true does not mean the message will be processed -- if 274 | * the looper is quit before the delivery time of the message 275 | * occurs then the message will be dropped. 276 | */ 277 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 278 | return mExec.sendMessageAtTime(msg, uptimeMillis); 279 | } 280 | 281 | /** 282 | * Enqueue a message at the front of the message queue, to be processed on 283 | * the next iteration of the message loop. You will receive it in 284 | * callback, in the thread attached to this handler. 285 | * This method is only for use in very special circumstances -- it 286 | * can easily starve the message queue, cause ordering problems, or have 287 | * other unexpected side-effects. 288 | * 289 | * @return Returns true if the message was successfully placed in to the 290 | * message queue. Returns false on failure, usually because the 291 | * looper processing the message queue is exiting. 292 | */ 293 | public final boolean sendMessageAtFrontOfQueue(Message msg) { 294 | return mExec.sendMessageAtFrontOfQueue(msg); 295 | } 296 | 297 | /** 298 | * Remove any pending posts of messages with code 'what' that are in the 299 | * message queue. 300 | */ 301 | public final void removeMessages(int what) { 302 | mExec.removeMessages(what); 303 | } 304 | 305 | /** 306 | * Remove any pending posts of messages with code 'what' and whose obj is 307 | * 'object' that are in the message queue. If object is null, 308 | * all messages will be removed. 309 | */ 310 | public final void removeMessages(int what, Object object) { 311 | mExec.removeMessages(what, object); 312 | } 313 | 314 | /** 315 | * Remove any pending posts of callbacks and sent messages whose 316 | * obj is token. If token is null, 317 | * all callbacks and messages will be removed. 318 | */ 319 | public final void removeCallbacksAndMessages(Object token) { 320 | mExec.removeCallbacksAndMessages(token); 321 | } 322 | 323 | /** 324 | * Check if there are any pending posts of messages with code 'what' in 325 | * the message queue. 326 | */ 327 | public final boolean hasMessages(int what) { 328 | return mExec.hasMessages(what); 329 | } 330 | 331 | /** 332 | * Check if there are any pending posts of messages with code 'what' and 333 | * whose obj is 'object' in the message queue. 334 | */ 335 | public final boolean hasMessages(int what, Object object) { 336 | return mExec.hasMessages(what, object); 337 | } 338 | 339 | public final Looper getLooper() { 340 | return mExec.getLooper(); 341 | } 342 | 343 | private WeakRunnable wrapRunnable(@NonNull Runnable r) { 344 | //noinspection ConstantConditions 345 | if (r == null) { 346 | throw new NullPointerException("Runnable can't be null"); 347 | } 348 | final ChainedRef hardRef = new ChainedRef(mLock, r); 349 | mRunnables.insertAfter(hardRef); 350 | return hardRef.wrapper; 351 | } 352 | 353 | private static class ExecHandler extends Handler { 354 | private final WeakReference mCallback; 355 | 356 | ExecHandler() { 357 | mCallback = null; 358 | } 359 | 360 | ExecHandler(WeakReference callback) { 361 | mCallback = callback; 362 | } 363 | 364 | ExecHandler(Looper looper) { 365 | super(looper); 366 | mCallback = null; 367 | } 368 | 369 | ExecHandler(Looper looper, WeakReference callback) { 370 | super(looper); 371 | mCallback = callback; 372 | } 373 | 374 | @Override 375 | public void handleMessage(@NonNull Message msg) { 376 | if (mCallback == null) { 377 | return; 378 | } 379 | final Handler.Callback callback = mCallback.get(); 380 | if (callback == null) { // Already disposed 381 | return; 382 | } 383 | callback.handleMessage(msg); 384 | } 385 | } 386 | 387 | static class WeakRunnable implements Runnable { 388 | private final WeakReference mDelegate; 389 | private final WeakReference mReference; 390 | 391 | WeakRunnable(WeakReference delegate, WeakReference reference) { 392 | mDelegate = delegate; 393 | mReference = reference; 394 | } 395 | 396 | @Override 397 | public void run() { 398 | final Runnable delegate = mDelegate.get(); 399 | final ChainedRef reference = mReference.get(); 400 | if (reference != null) { 401 | reference.remove(); 402 | } 403 | if (delegate != null) { 404 | delegate.run(); 405 | } 406 | } 407 | } 408 | 409 | static class ChainedRef { 410 | @Nullable 411 | ChainedRef next; 412 | @Nullable 413 | ChainedRef prev; 414 | @NonNull 415 | final Runnable runnable; 416 | @NonNull 417 | final WeakRunnable wrapper; 418 | 419 | @NonNull 420 | Lock lock; 421 | 422 | public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { 423 | this.runnable = r; 424 | this.lock = lock; 425 | this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); 426 | } 427 | 428 | public WeakRunnable remove() { 429 | lock.lock(); 430 | try { 431 | if (prev != null) { 432 | prev.next = next; 433 | } 434 | if (next != null) { 435 | next.prev = prev; 436 | } 437 | prev = null; 438 | next = null; 439 | } finally { 440 | lock.unlock(); 441 | } 442 | return wrapper; 443 | } 444 | 445 | public void insertAfter(@NonNull ChainedRef candidate) { 446 | lock.lock(); 447 | try { 448 | if (this.next != null) { 449 | this.next.prev = candidate; 450 | } 451 | 452 | candidate.next = this.next; 453 | this.next = candidate; 454 | candidate.prev = this; 455 | } finally { 456 | lock.unlock(); 457 | } 458 | } 459 | 460 | @Nullable 461 | public WeakRunnable remove(Runnable obj) { 462 | lock.lock(); 463 | try { 464 | ChainedRef curr = this.next; // Skipping head 465 | while (curr != null) { 466 | if (curr.runnable == obj) { // We do comparison exactly how Handler does inside 467 | return curr.remove(); 468 | } 469 | curr = curr.next; 470 | } 471 | } finally { 472 | lock.unlock(); 473 | } 474 | return null; 475 | } 476 | } 477 | } -------------------------------------------------------------------------------- /banner/src/main/java/com/youth/banner/Banner.java: -------------------------------------------------------------------------------- 1 | package com.youth.banner; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import androidx.viewpager.widget.PagerAdapter; 6 | import androidx.viewpager.widget.ViewPager; 7 | import android.util.AttributeSet; 8 | import android.util.DisplayMetrics; 9 | import android.util.Log; 10 | import android.util.TypedValue; 11 | import android.view.Gravity; 12 | import android.view.LayoutInflater; 13 | import android.view.MotionEvent; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.FrameLayout; 17 | import android.widget.ImageView; 18 | import android.widget.ImageView.ScaleType; 19 | import android.widget.LinearLayout; 20 | import android.widget.RelativeLayout; 21 | import android.widget.TextView; 22 | 23 | import com.youth.banner.listener.OnBannerClickListener; 24 | import com.youth.banner.listener.OnBannerListener; 25 | import com.youth.banner.loader.ImageLoaderInterface; 26 | import com.youth.banner.view.BannerViewPager; 27 | 28 | import java.lang.reflect.Field; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | import static androidx.viewpager.widget.ViewPager.OnPageChangeListener; 33 | import static androidx.viewpager.widget.ViewPager.PageTransformer; 34 | 35 | public class Banner extends FrameLayout implements OnPageChangeListener { 36 | public String tag = "banner"; 37 | private int mIndicatorMargin = BannerConfig.PADDING_SIZE; 38 | private int mIndicatorWidth; 39 | private int mIndicatorHeight; 40 | private int indicatorSize; 41 | private int bannerBackgroundImage; 42 | private int bannerStyle = BannerConfig.CIRCLE_INDICATOR; 43 | private int delayTime = BannerConfig.TIME; 44 | private int scrollTime = BannerConfig.DURATION; 45 | private boolean isAutoPlay = BannerConfig.IS_AUTO_PLAY; 46 | private boolean isScroll = BannerConfig.IS_SCROLL; 47 | private int mIndicatorSelectedResId = R.drawable.gray_radius; 48 | private int mIndicatorUnselectedResId = R.drawable.white_radius; 49 | private int mLayoutResId = R.layout.banner; 50 | private int titleHeight; 51 | private int titleBackground; 52 | private int titleTextColor; 53 | private int titleTextSize; 54 | private int count = 0; 55 | private int currentItem; 56 | private int gravity = -1; 57 | private int lastPosition = 1; 58 | private int scaleType = 1; 59 | private List titles; 60 | private List imageUrls; 61 | private List imageViews; 62 | private List indicatorImages; 63 | private Context context; 64 | private BannerViewPager viewPager; 65 | private TextView bannerTitle, numIndicatorInside, numIndicator; 66 | private LinearLayout indicator, indicatorInside, titleView; 67 | private ImageView bannerDefaultImage; 68 | private ImageLoaderInterface imageLoader; 69 | private BannerPagerAdapter adapter; 70 | private OnPageChangeListener mOnPageChangeListener; 71 | private BannerScroller mScroller; 72 | private OnBannerClickListener bannerListener; 73 | private OnBannerListener listener; 74 | private DisplayMetrics dm; 75 | 76 | private WeakHandler handler = new WeakHandler(); 77 | 78 | public Banner(Context context) { 79 | this(context, null); 80 | } 81 | 82 | public Banner(Context context, AttributeSet attrs) { 83 | this(context, attrs, 0); 84 | } 85 | 86 | public Banner(Context context, AttributeSet attrs, int defStyle) { 87 | super(context, attrs, defStyle); 88 | this.context = context; 89 | titles = new ArrayList<>(); 90 | imageUrls = new ArrayList<>(); 91 | imageViews = new ArrayList<>(); 92 | indicatorImages = new ArrayList<>(); 93 | dm = context.getResources().getDisplayMetrics(); 94 | indicatorSize = dm.widthPixels / 80; 95 | initView(context, attrs); 96 | } 97 | 98 | private void initView(Context context, AttributeSet attrs) { 99 | imageViews.clear(); 100 | handleTypedArray(context, attrs); 101 | View view = LayoutInflater.from(context).inflate(mLayoutResId, this, true); 102 | bannerDefaultImage = (ImageView) view.findViewById(R.id.bannerDefaultImage); 103 | viewPager = (BannerViewPager) view.findViewById(R.id.bannerViewPager); 104 | titleView = (LinearLayout) view.findViewById(R.id.titleView); 105 | indicator = (LinearLayout) view.findViewById(R.id.circleIndicator); 106 | indicatorInside = (LinearLayout) view.findViewById(R.id.indicatorInside); 107 | bannerTitle = (TextView) view.findViewById(R.id.bannerTitle); 108 | numIndicator = (TextView) view.findViewById(R.id.numIndicator); 109 | numIndicatorInside = (TextView) view.findViewById(R.id.numIndicatorInside); 110 | bannerDefaultImage.setImageResource(bannerBackgroundImage); 111 | initViewPagerScroll(); 112 | } 113 | 114 | private void handleTypedArray(Context context, AttributeSet attrs) { 115 | if (attrs == null) { 116 | return; 117 | } 118 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner); 119 | mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_width, indicatorSize); 120 | mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_height, indicatorSize); 121 | mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_margin, BannerConfig.PADDING_SIZE); 122 | mIndicatorSelectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_selected, R.drawable.gray_radius); 123 | mIndicatorUnselectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_unselected, R.drawable.white_radius); 124 | scaleType = typedArray.getInt(R.styleable.Banner_image_scale_type, scaleType); 125 | delayTime = typedArray.getInt(R.styleable.Banner_delay_time, BannerConfig.TIME); 126 | scrollTime = typedArray.getInt(R.styleable.Banner_scroll_time, BannerConfig.DURATION); 127 | isAutoPlay = typedArray.getBoolean(R.styleable.Banner_is_auto_play, BannerConfig.IS_AUTO_PLAY); 128 | titleBackground = typedArray.getColor(R.styleable.Banner_title_background, BannerConfig.TITLE_BACKGROUND); 129 | titleHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_title_height, BannerConfig.TITLE_HEIGHT); 130 | titleTextColor = typedArray.getColor(R.styleable.Banner_title_textcolor, BannerConfig.TITLE_TEXT_COLOR); 131 | titleTextSize = typedArray.getDimensionPixelSize(R.styleable.Banner_title_textsize, BannerConfig.TITLE_TEXT_SIZE); 132 | mLayoutResId = typedArray.getResourceId(R.styleable.Banner_banner_layout, mLayoutResId); 133 | bannerBackgroundImage = typedArray.getResourceId(R.styleable.Banner_banner_default_image, R.drawable.no_banner); 134 | typedArray.recycle(); 135 | } 136 | 137 | private void initViewPagerScroll() { 138 | try { 139 | Field mField = ViewPager.class.getDeclaredField("mScroller"); 140 | mField.setAccessible(true); 141 | mScroller = new BannerScroller(viewPager.getContext()); 142 | mScroller.setDuration(scrollTime); 143 | mField.set(viewPager, mScroller); 144 | } catch (Exception e) { 145 | Log.e(tag, e.getMessage()); 146 | } 147 | } 148 | 149 | 150 | public Banner isAutoPlay(boolean isAutoPlay) { 151 | this.isAutoPlay = isAutoPlay; 152 | return this; 153 | } 154 | 155 | public Banner setImageLoader(ImageLoaderInterface imageLoader) { 156 | this.imageLoader = imageLoader; 157 | return this; 158 | } 159 | 160 | public Banner setDelayTime(int delayTime) { 161 | this.delayTime = delayTime; 162 | return this; 163 | } 164 | 165 | public Banner setIndicatorGravity(int type) { 166 | switch (type) { 167 | case BannerConfig.LEFT: 168 | this.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; 169 | break; 170 | case BannerConfig.CENTER: 171 | this.gravity = Gravity.CENTER; 172 | break; 173 | case BannerConfig.RIGHT: 174 | this.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; 175 | break; 176 | } 177 | return this; 178 | } 179 | 180 | public Banner setBannerAnimation(Class transformer) { 181 | try { 182 | setPageTransformer(true, transformer.newInstance()); 183 | } catch (Exception e) { 184 | Log.e(tag, "Please set the PageTransformer class"); 185 | } 186 | return this; 187 | } 188 | 189 | /** 190 | * Set the number of pages that should be retained to either side of the 191 | * current page in the view hierarchy in an idle state. Pages beyond this 192 | * limit will be recreated from the adapter when needed. 193 | * 194 | * @param limit How many pages will be kept offscreen in an idle state. 195 | * @return Banner 196 | */ 197 | public Banner setOffscreenPageLimit(int limit) { 198 | if (viewPager != null) { 199 | viewPager.setOffscreenPageLimit(limit); 200 | } 201 | return this; 202 | } 203 | 204 | /** 205 | * Set a {@link PageTransformer} that will be called for each attached page whenever 206 | * the scroll position is changed. This allows the application to apply custom property 207 | * transformations to each page, overriding the default sliding look and feel. 208 | * 209 | * @param reverseDrawingOrder true if the supplied PageTransformer requires page views 210 | * to be drawn from last to first instead of first to last. 211 | * @param transformer PageTransformer that will modify each page's animation properties 212 | * @return Banner 213 | */ 214 | public Banner setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) { 215 | viewPager.setPageTransformer(reverseDrawingOrder, transformer); 216 | return this; 217 | } 218 | 219 | public Banner setBannerTitles(List titles) { 220 | this.titles = titles; 221 | return this; 222 | } 223 | 224 | public Banner setBannerStyle(int bannerStyle) { 225 | this.bannerStyle = bannerStyle; 226 | return this; 227 | } 228 | 229 | public Banner setViewPagerIsScroll(boolean isScroll) { 230 | this.isScroll = isScroll; 231 | return this; 232 | } 233 | 234 | public Banner setImages(List imageUrls) { 235 | this.imageUrls = imageUrls; 236 | this.count = imageUrls.size(); 237 | return this; 238 | } 239 | 240 | public void update(List imageUrls, List titles) { 241 | this.titles.clear(); 242 | this.titles.addAll(titles); 243 | update(imageUrls); 244 | } 245 | 246 | public void update(List imageUrls) { 247 | this.imageUrls.clear(); 248 | this.imageViews.clear(); 249 | this.indicatorImages.clear(); 250 | this.imageUrls.addAll(imageUrls); 251 | this.count = this.imageUrls.size(); 252 | start(); 253 | } 254 | 255 | public void updateBannerStyle(int bannerStyle) { 256 | indicator.setVisibility(GONE); 257 | numIndicator.setVisibility(GONE); 258 | numIndicatorInside.setVisibility(GONE); 259 | indicatorInside.setVisibility(GONE); 260 | bannerTitle.setVisibility(View.GONE); 261 | titleView.setVisibility(View.GONE); 262 | this.bannerStyle = bannerStyle; 263 | start(); 264 | } 265 | 266 | public Banner start() { 267 | setBannerStyleUI(); 268 | setImageList(imageUrls); 269 | setData(); 270 | return this; 271 | } 272 | 273 | private void setTitleStyleUI() { 274 | if (titles.size() != imageUrls.size()) { 275 | throw new RuntimeException("[Banner] --> The number of titles and images is different"); 276 | } 277 | if (titleBackground != -1) { 278 | titleView.setBackgroundColor(titleBackground); 279 | } 280 | if (titleHeight != -1) { 281 | titleView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, titleHeight)); 282 | } 283 | if (titleTextColor != -1) { 284 | bannerTitle.setTextColor(titleTextColor); 285 | } 286 | if (titleTextSize != -1) { 287 | bannerTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize); 288 | } 289 | if (titles != null && titles.size() > 0) { 290 | bannerTitle.setText(titles.get(0)); 291 | bannerTitle.setVisibility(View.VISIBLE); 292 | titleView.setVisibility(View.VISIBLE); 293 | } 294 | } 295 | 296 | private void setBannerStyleUI() { 297 | int visibility =count > 1 ? View.VISIBLE :View.GONE; 298 | switch (bannerStyle) { 299 | case BannerConfig.CIRCLE_INDICATOR: 300 | indicator.setVisibility(visibility); 301 | break; 302 | case BannerConfig.NUM_INDICATOR: 303 | numIndicator.setVisibility(visibility); 304 | break; 305 | case BannerConfig.NUM_INDICATOR_TITLE: 306 | numIndicatorInside.setVisibility(visibility); 307 | setTitleStyleUI(); 308 | break; 309 | case BannerConfig.CIRCLE_INDICATOR_TITLE: 310 | indicator.setVisibility(visibility); 311 | setTitleStyleUI(); 312 | break; 313 | case BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE: 314 | indicatorInside.setVisibility(visibility); 315 | setTitleStyleUI(); 316 | break; 317 | } 318 | } 319 | 320 | private void initImages() { 321 | imageViews.clear(); 322 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR || 323 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE || 324 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) { 325 | createIndicator(); 326 | } else if (bannerStyle == BannerConfig.NUM_INDICATOR_TITLE) { 327 | numIndicatorInside.setText("1/" + count); 328 | } else if (bannerStyle == BannerConfig.NUM_INDICATOR) { 329 | numIndicator.setText("1/" + count); 330 | } 331 | } 332 | 333 | private void setImageList(List imagesUrl) { 334 | if (imagesUrl == null || imagesUrl.size() <= 0) { 335 | bannerDefaultImage.setVisibility(VISIBLE); 336 | Log.e(tag, "The image data set is empty."); 337 | return; 338 | } 339 | bannerDefaultImage.setVisibility(GONE); 340 | initImages(); 341 | for (int i = 0; i <= count + 1; i++) { 342 | View imageView = null; 343 | if (imageLoader != null) { 344 | imageView = imageLoader.createImageView(context); 345 | } 346 | if (imageView == null) { 347 | imageView = new ImageView(context); 348 | } 349 | setScaleType(imageView); 350 | Object url = null; 351 | if (i == 0) { 352 | url = imagesUrl.get(count - 1); 353 | } else if (i == count + 1) { 354 | url = imagesUrl.get(0); 355 | } else { 356 | url = imagesUrl.get(i - 1); 357 | } 358 | imageViews.add(imageView); 359 | if (imageLoader != null) 360 | imageLoader.displayImage(context, url, imageView); 361 | else 362 | Log.e(tag, "Please set images loader."); 363 | } 364 | } 365 | 366 | private void setScaleType(View imageView) { 367 | if (imageView instanceof ImageView) { 368 | ImageView view = ((ImageView) imageView); 369 | switch (scaleType) { 370 | case 0: 371 | view.setScaleType(ScaleType.CENTER); 372 | break; 373 | case 1: 374 | view.setScaleType(ScaleType.CENTER_CROP); 375 | break; 376 | case 2: 377 | view.setScaleType(ScaleType.CENTER_INSIDE); 378 | break; 379 | case 3: 380 | view.setScaleType(ScaleType.FIT_CENTER); 381 | break; 382 | case 4: 383 | view.setScaleType(ScaleType.FIT_END); 384 | break; 385 | case 5: 386 | view.setScaleType(ScaleType.FIT_START); 387 | break; 388 | case 6: 389 | view.setScaleType(ScaleType.FIT_XY); 390 | break; 391 | case 7: 392 | view.setScaleType(ScaleType.MATRIX); 393 | break; 394 | } 395 | 396 | } 397 | } 398 | 399 | private void createIndicator() { 400 | indicatorImages.clear(); 401 | indicator.removeAllViews(); 402 | indicatorInside.removeAllViews(); 403 | for (int i = 0; i < count; i++) { 404 | ImageView imageView = new ImageView(context); 405 | imageView.setScaleType(ScaleType.CENTER_CROP); 406 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIndicatorWidth, mIndicatorHeight); 407 | params.leftMargin = mIndicatorMargin; 408 | params.rightMargin = mIndicatorMargin; 409 | if (i == 0) { 410 | imageView.setImageResource(mIndicatorSelectedResId); 411 | } else { 412 | imageView.setImageResource(mIndicatorUnselectedResId); 413 | } 414 | indicatorImages.add(imageView); 415 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR || 416 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE) 417 | indicator.addView(imageView, params); 418 | else if (bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) 419 | indicatorInside.addView(imageView, params); 420 | } 421 | } 422 | 423 | 424 | private void setData() { 425 | currentItem = 1; 426 | if (adapter == null) { 427 | adapter = new BannerPagerAdapter(); 428 | viewPager.addOnPageChangeListener(this); 429 | } 430 | viewPager.setAdapter(adapter); 431 | viewPager.setFocusable(true); 432 | viewPager.setCurrentItem(1); 433 | if (gravity != -1) 434 | indicator.setGravity(gravity); 435 | if (isScroll && count > 1) { 436 | viewPager.setScrollable(true); 437 | } else { 438 | viewPager.setScrollable(false); 439 | } 440 | if (isAutoPlay) 441 | startAutoPlay(); 442 | } 443 | 444 | 445 | public void startAutoPlay() { 446 | handler.removeCallbacks(task); 447 | handler.postDelayed(task, delayTime); 448 | } 449 | 450 | public void stopAutoPlay() { 451 | handler.removeCallbacks(task); 452 | } 453 | 454 | private final Runnable task = new Runnable() { 455 | @Override 456 | public void run() { 457 | if (count > 1 && isAutoPlay) { 458 | currentItem = currentItem % (count + 1) + 1; 459 | // Log.i(tag, "curr:" + currentItem + " count:" + count); 460 | if (currentItem == 1) { 461 | viewPager.setCurrentItem(currentItem, false); 462 | handler.post(task); 463 | } else { 464 | viewPager.setCurrentItem(currentItem); 465 | handler.postDelayed(task, delayTime); 466 | } 467 | } 468 | } 469 | }; 470 | 471 | @Override 472 | public boolean dispatchTouchEvent(MotionEvent ev) { 473 | // Log.i(tag, ev.getAction() + "--" + isAutoPlay); 474 | if (isAutoPlay) { 475 | int action = ev.getAction(); 476 | if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL 477 | || action == MotionEvent.ACTION_OUTSIDE) { 478 | startAutoPlay(); 479 | } else if (action == MotionEvent.ACTION_DOWN) { 480 | stopAutoPlay(); 481 | } 482 | } 483 | return super.dispatchTouchEvent(ev); 484 | } 485 | 486 | /** 487 | * 返回真实的位置 488 | * 489 | * @param position 490 | * @return 下标从0开始 491 | */ 492 | public int toRealPosition(int position) { 493 | int realPosition = (position - 1) % count; 494 | if (realPosition < 0) 495 | realPosition += count; 496 | return realPosition; 497 | } 498 | 499 | class BannerPagerAdapter extends PagerAdapter { 500 | 501 | @Override 502 | public int getCount() { 503 | return imageViews.size(); 504 | } 505 | 506 | @Override 507 | public boolean isViewFromObject(View view, Object object) { 508 | return view == object; 509 | } 510 | 511 | @Override 512 | public Object instantiateItem(ViewGroup container, final int position) { 513 | container.addView(imageViews.get(position)); 514 | View view = imageViews.get(position); 515 | if (bannerListener != null) { 516 | view.setOnClickListener(new OnClickListener() { 517 | @Override 518 | public void onClick(View v) { 519 | Log.e(tag, "你正在使用旧版点击事件接口,下标是从1开始," + 520 | "为了体验请更换为setOnBannerListener,下标从0开始计算"); 521 | bannerListener.OnBannerClick(position); 522 | } 523 | }); 524 | } 525 | if (listener != null) { 526 | view.setOnClickListener(new OnClickListener() { 527 | @Override 528 | public void onClick(View v) { 529 | listener.OnBannerClick(toRealPosition(position)); 530 | } 531 | }); 532 | } 533 | return view; 534 | } 535 | 536 | @Override 537 | public void destroyItem(ViewGroup container, int position, Object object) { 538 | container.removeView((View) object); 539 | } 540 | 541 | } 542 | 543 | @Override 544 | public void onPageScrollStateChanged(int state) { 545 | if (mOnPageChangeListener != null) { 546 | mOnPageChangeListener.onPageScrollStateChanged(state); 547 | } 548 | // Log.i(tag,"currentItem: "+currentItem); 549 | switch (state) { 550 | case 0://No operation 551 | if (currentItem == 0) { 552 | viewPager.setCurrentItem(count, false); 553 | } else if (currentItem == count + 1) { 554 | viewPager.setCurrentItem(1, false); 555 | } 556 | break; 557 | case 1://start Sliding 558 | if (currentItem == count + 1) { 559 | viewPager.setCurrentItem(1, false); 560 | } else if (currentItem == 0) { 561 | viewPager.setCurrentItem(count, false); 562 | } 563 | break; 564 | case 2://end Sliding 565 | break; 566 | } 567 | } 568 | 569 | @Override 570 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 571 | if (mOnPageChangeListener != null) { 572 | mOnPageChangeListener.onPageScrolled(toRealPosition(position), positionOffset, positionOffsetPixels); 573 | } 574 | } 575 | 576 | @Override 577 | public void onPageSelected(int position) { 578 | currentItem=position; 579 | if (mOnPageChangeListener != null) { 580 | mOnPageChangeListener.onPageSelected(toRealPosition(position)); 581 | } 582 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR || 583 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE || 584 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) { 585 | indicatorImages.get((lastPosition - 1 + count) % count).setImageResource(mIndicatorUnselectedResId); 586 | indicatorImages.get((position - 1 + count) % count).setImageResource(mIndicatorSelectedResId); 587 | lastPosition = position; 588 | } 589 | if (position == 0) position = count; 590 | if (position > count) position = 1; 591 | switch (bannerStyle) { 592 | case BannerConfig.CIRCLE_INDICATOR: 593 | break; 594 | case BannerConfig.NUM_INDICATOR: 595 | numIndicator.setText(position + "/" + count); 596 | break; 597 | case BannerConfig.NUM_INDICATOR_TITLE: 598 | numIndicatorInside.setText(position + "/" + count); 599 | bannerTitle.setText(titles.get(position - 1)); 600 | break; 601 | case BannerConfig.CIRCLE_INDICATOR_TITLE: 602 | bannerTitle.setText(titles.get(position - 1)); 603 | break; 604 | case BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE: 605 | bannerTitle.setText(titles.get(position - 1)); 606 | break; 607 | } 608 | 609 | } 610 | 611 | @Deprecated 612 | public Banner setOnBannerClickListener(OnBannerClickListener listener) { 613 | this.bannerListener = listener; 614 | return this; 615 | } 616 | 617 | /** 618 | * 废弃了旧版接口,新版的接口下标是从1开始,同时解决下标越界问题 619 | * 620 | * @param listener 621 | * @return 622 | */ 623 | public Banner setOnBannerListener(OnBannerListener listener) { 624 | this.listener = listener; 625 | return this; 626 | } 627 | 628 | public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) { 629 | mOnPageChangeListener = onPageChangeListener; 630 | } 631 | 632 | public void releaseBanner() { 633 | handler.removeCallbacksAndMessages(null); 634 | } 635 | } 636 | --------------------------------------------------------------------------------