├── .gitignore ├── README.md ├── build.gradle ├── carouselbanner ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── iwdael │ │ └── carouselbanner │ │ ├── Banner.java │ │ ├── CarouselBanner.java │ │ ├── CoolCarouselBanner.java │ │ ├── adapter │ │ ├── CarouselAdapter.java │ │ └── CoolCarouselAdapter.java │ │ ├── base │ │ ├── BaseBannerAdapter.java │ │ ├── BaseViewHolder.java │ │ └── CarouselBannerBase.java │ │ ├── interfaces │ │ ├── CarouselImageFactory.java │ │ ├── OnCarouselItemChangeListener.java │ │ └── OnCarouselItemClickListener.java │ │ ├── layoutmanager │ │ ├── BannerLayoutManager.java │ │ ├── CenterScrollListener.java │ │ ├── CoolBannerLayoutManager.java │ │ └── OrientationHelper.java │ │ └── viewholder │ │ ├── CarouselViewHolder.java │ │ └── CoolCarouselViewHolder.java │ └── res │ ├── layout │ └── item_image.xml │ └── values │ ├── attr.xml │ └── colors.xml ├── config.gradle ├── example ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── iwdael │ │ └── carouselbanner │ │ └── example │ │ ├── ImageFactory.java │ │ ├── Main2Activity.java │ │ └── MainActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ └── activity_main2.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── CarouselBanner.gif ├── CoolCarouselBanner.gif └── qq_group.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CarouselBanner 2 | ![](https://img.shields.io/badge/platform-android-orange.svg) 3 | ![](https://img.shields.io/badge/language-java-yellow.svg) 4 | ![](https://jitpack.io/v/com.iwdael/carouselbanner.svg) 5 | ![](https://img.shields.io/badge/build-passing-brightgreen.svg) 6 | ![](https://img.shields.io/badge/license-apache--2.0-green.svg) 7 | ![](https://img.shields.io/badge/api-19+-green.svg) 8 | 9 | CarouselBanner 是一款展示图片或者广告的控件。它使用简单,不依赖其他第三方框架,而且把图片加载的过程通过接口的方式让使用者自行实现。 10 |
11 | ![Image Text](screenshot/CarouselBanner.gif) 12 |

13 | ![Image Text](screenshot/CoolCarouselBanner.gif) 14 |
15 | 16 | ## 使用说明 17 | CarouselBanner可以设置滚动的方向,例如水平和垂直,也可以设置滚动的速度,滚动间隔时间,以及指示器的位置。CarouselBanner是普通的轮播控件,如果你想使用带有放缩效果的轮播控件,请使用CoolCarouselBanner。 18 | 19 | ### 代码示例 20 | #### 自定义加载器 21 | ``` 22 | public class ImageFactory implements CarouselImageFactory { 23 | @Override 24 | public void onLoadFactory(String url, ImageView view) { 25 | Glide.with(view).load(url).into(view); 26 | } 27 | } 28 | ``` 29 | #### 初始化 30 | ``` 31 | Banner.init(new ImageFactory()); 32 | ``` 33 | #### 添加到布局 34 | ``` 35 | 36 | 46 | ``` 47 | #### 绑定数据 48 | ``` 49 | banner = findViewById(R.id.banner); 50 | banner.setOnCarouselItemChangeListener(...);//滚动监听 51 | banner.setOnCarouselItemClickListener(...);//点击监听 52 | list.add("http://.............jpg"); 53 | banner.initBanner(list); 54 | ``` 55 | 56 | ## 快速引入项目 57 | 合并以下代码到需要使用的Module的dependencies中。 58 | ```Java 59 | dependencies { 60 | ... 61 | implementation 'com.iwdael:carouselbanner:$version' 62 | } 63 | ``` 64 | 65 | ## 感谢浏览 66 | 如果你有任何疑问,请加入QQ群,我将竭诚为你解答。欢迎Star和Fork本仓库,当然也欢迎你关注我。 67 |
68 | ![Image Text](screenshot/qq_group.png) 69 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.4.21" 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath "com.android.tools.build:gradle:4.0.2" 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } -------------------------------------------------------------------------------- /carouselbanner/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /carouselbanner/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: './../config.gradle' 2 | apply plugin: 'com.android.library' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-android-extensions' 5 | android { 6 | buildToolsVersion buidConfig.buildToolsVersion 7 | compileSdkVersion buidConfig.compileSdkVersion 8 | defaultConfig { 9 | minSdkVersion buidConfig.minSdkVersion 10 | targetSdkVersion buidConfig.targetSdkVersion 11 | versionCode buidConfig.versionCode 12 | versionName buidConfig.versionName 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation dependent.kotlinJdk7 18 | compileOnly dependent.appcompat 19 | compileOnly dependent.recyclerview 20 | compileOnly dependent.cardview 21 | } -------------------------------------------------------------------------------- /carouselbanner/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/Banner.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner; 2 | 3 | import com.iwdael.carouselbanner.interfaces.CarouselImageFactory; 4 | 5 | public class Banner { 6 | public static volatile Banner sBanner; 7 | private CarouselImageFactory factory; 8 | 9 | private Banner() { 10 | } 11 | 12 | public static void init(CarouselImageFactory factory) { 13 | if (sBanner == null) { 14 | synchronized (Banner.class) { 15 | if (sBanner == null) 16 | sBanner = new Banner(); 17 | } 18 | } 19 | sBanner.factory = factory; 20 | } 21 | 22 | public static CarouselImageFactory factory() { 23 | if (sBanner == null) { 24 | synchronized (Banner.class) { 25 | if (sBanner == null) 26 | sBanner = new Banner(); 27 | } 28 | } 29 | if (sBanner.factory == null) 30 | throw new RuntimeException("CarouselBanner must be initialized by \"Banner.init\" method !!!"); 31 | return sBanner.factory; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/CarouselBanner.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import com.iwdael.carouselbanner.adapter.CarouselAdapter; 10 | import com.iwdael.carouselbanner.base.CarouselBannerBase; 11 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 12 | import com.iwdael.carouselbanner.layoutmanager.BannerLayoutManager; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * author : Iwdael 18 | * e-mail : iwdael@outlook.com 19 | * github : http://github.com/iwdael 20 | * project : CarouselBanner 21 | */ 22 | public class CarouselBanner extends CarouselBannerBase { 23 | 24 | public CarouselBanner(Context context) { 25 | this(context, null); 26 | } 27 | 28 | public CarouselBanner(Context context, AttributeSet attrs) { 29 | this(context, attrs, 0); 30 | } 31 | 32 | public CarouselBanner(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | } 35 | 36 | @Override 37 | protected void onBannerScrolled(RecyclerView recyclerView, int dx, int dy) { 38 | //解决连续滑动时指示器不更新的问题 39 | if (bannerSize < 2) return; 40 | int firstReal = mLayoutManager.findFirstVisibleItemPosition(); 41 | View viewFirst = mLayoutManager.findViewByPosition(firstReal); 42 | float width = getWidth(); 43 | if (width != 0 && viewFirst != null) { 44 | float right = viewFirst.getRight(); 45 | float ratio = right / width; 46 | if (ratio > 0.8) { 47 | if (currentIndex != firstReal) { 48 | currentIndex = firstReal; 49 | refreshIndicator(); 50 | } 51 | } else if (ratio < 0.2) { 52 | if (currentIndex != firstReal + 1) { 53 | currentIndex = firstReal + 1; 54 | refreshIndicator(); 55 | } 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | protected void onBannerScrollStateChanged(RecyclerView recyclerView, int newState) { 62 | int first = mLayoutManager.findFirstVisibleItemPosition(); 63 | int last = mLayoutManager.findLastVisibleItemPosition(); 64 | if (currentIndex != first && first == last) { 65 | currentIndex = first; 66 | refreshIndicator(); 67 | } 68 | } 69 | 70 | @Override 71 | protected BannerLayoutManager getLayoutManager(Context context, int orientation) { 72 | return new BannerLayoutManager(context, orientation, false, speedPerPixelMillisecond); 73 | } 74 | 75 | @Override 76 | protected CarouselAdapter getAdapter(List list, OnCarouselItemClickListener onBannerItemClickListener) { 77 | return new CarouselAdapter(list, onBannerItemClickListener); 78 | } 79 | 80 | 81 | } -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/CoolCarouselBanner.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import com.iwdael.carouselbanner.adapter.CoolCarouselAdapter; 9 | import com.iwdael.carouselbanner.base.CarouselBannerBase; 10 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 11 | import com.iwdael.carouselbanner.layoutmanager.CoolBannerLayoutManager; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * author : Iwdael 17 | * e-mail : iwdael@outlook.com 18 | * github : http://github.com/iwdael 19 | * project : CarouselBanner 20 | */ 21 | public class CoolCarouselBanner extends CarouselBannerBase { 22 | 23 | 24 | public CoolCarouselBanner(Context context) { 25 | this(context, null); 26 | } 27 | 28 | public CoolCarouselBanner(Context context, AttributeSet attrs) { 29 | this(context, attrs, 0); 30 | } 31 | 32 | public CoolCarouselBanner(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | } 35 | 36 | @Override 37 | protected void onBannerScrolled(RecyclerView recyclerView, int dx, int dy) { 38 | 39 | } 40 | 41 | @Override 42 | protected void onBannerScrollStateChanged(RecyclerView recyclerView, int newState) { 43 | int first = mLayoutManager.getCurrentPosition(); 44 | if (currentIndex != first) { 45 | currentIndex = first; 46 | refreshIndicator(); 47 | } 48 | } 49 | 50 | @Override 51 | protected CoolBannerLayoutManager getLayoutManager(Context context, int orientation) { 52 | return new CoolBannerLayoutManager(orientation, dp2px(10),speedPerPixelMillisecond); 53 | } 54 | 55 | @Override 56 | protected CoolCarouselAdapter getAdapter(List list, OnCarouselItemClickListener onBannerItemClickListener) { 57 | return new CoolCarouselAdapter(list, onBannerItemClickListener); 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/adapter/CarouselAdapter.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.adapter; 2 | 3 | import android.view.ViewGroup; 4 | import android.widget.ImageView; 5 | 6 | import com.iwdael.carouselbanner.base.BaseBannerAdapter; 7 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 8 | import com.iwdael.carouselbanner.viewholder.CarouselViewHolder; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * author : Iwdael 14 | * e-mail : iwdael@outlook.com 15 | * github : http://github.com/iwdael 16 | * project : CarouselBanner 17 | */ 18 | public class CarouselAdapter extends BaseBannerAdapter { 19 | 20 | public CarouselAdapter(List urlList, OnCarouselItemClickListener onBannerItemClickListener) { 21 | super(urlList, onBannerItemClickListener); 22 | } 23 | 24 | @Override 25 | public CarouselViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 26 | return new CarouselViewHolder(new ImageView(parent.getContext())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/adapter/CoolCarouselAdapter.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.adapter; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import com.iwdael.carouselbanner.R; 7 | import com.iwdael.carouselbanner.base.BaseBannerAdapter; 8 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 9 | import com.iwdael.carouselbanner.viewholder.CoolCarouselViewHolder; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * author : Iwdael 15 | * e-mail : iwdael@outlook.com 16 | * github : http://github.com/iwdael 17 | * project : CarouselBanner 18 | */ 19 | public class CoolCarouselAdapter extends BaseBannerAdapter { 20 | 21 | public CoolCarouselAdapter(List urlList, OnCarouselItemClickListener onBannerItemClickListener) { 22 | super(urlList, onBannerItemClickListener); 23 | } 24 | 25 | @Override 26 | public CoolCarouselViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 27 | return new CoolCarouselViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/base/BaseBannerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.base; 2 | 3 | 4 | import androidx.recyclerview.widget.RecyclerView; 5 | 6 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * author : Iwdael 12 | * e-mail : iwdael@outlook.com 13 | * github : http://github.com/iwdael 14 | * project : CarouselBanner 15 | */ 16 | public abstract class BaseBannerAdapter extends RecyclerView.Adapter { 17 | protected List urlList; 18 | protected OnCarouselItemClickListener onClickListener; 19 | 20 | public BaseBannerAdapter(List urlList, OnCarouselItemClickListener onBannerItemClickListener) { 21 | this.urlList = urlList; 22 | this.onClickListener = onBannerItemClickListener; 23 | } 24 | 25 | @Override 26 | public void onBindViewHolder(VH holder, int position) { 27 | holder.bindData(urlList.get(position % urlList.size()), position % urlList.size(), onClickListener); 28 | } 29 | 30 | @Override 31 | public int getItemCount() { 32 | return urlList.size() < 2 ? 1 : Integer.MAX_VALUE; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/base/BaseViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.base; 2 | 3 | import android.view.View; 4 | import android.widget.ImageView; 5 | 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import com.iwdael.carouselbanner.Banner; 9 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 10 | 11 | /** 12 | * author : Iwdael 13 | * e-mail : iwdael@outlook.com 14 | * github : http://github.com/iwdael 15 | * project : CarouselBanner 16 | */ 17 | 18 | public class BaseViewHolder extends RecyclerView.ViewHolder { 19 | protected ImageView imageView; 20 | 21 | public BaseViewHolder(View itemView) { 22 | super(itemView); 23 | } 24 | 25 | public void bindData(final String url, final int position, final OnCarouselItemClickListener onClickListener) { 26 | Banner.factory().onLoadFactory(url, imageView); 27 | if (onClickListener != null) { 28 | imageView.setOnClickListener(new View.OnClickListener() { 29 | @Override 30 | public void onClick(View v) { 31 | onClickListener.onItemClick(position, url); 32 | } 33 | }); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/base/CarouselBannerBase.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.base; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.drawable.Drawable; 7 | import android.graphics.drawable.GradientDrawable; 8 | import android.graphics.drawable.LayerDrawable; 9 | import android.os.Handler; 10 | import android.os.Message; 11 | import android.text.TextUtils; 12 | import android.util.AttributeSet; 13 | import android.util.TypedValue; 14 | import android.view.Gravity; 15 | import android.view.MotionEvent; 16 | import android.view.ViewGroup; 17 | import android.widget.FrameLayout; 18 | import android.widget.ImageView; 19 | 20 | import androidx.annotation.ColorRes; 21 | import androidx.core.content.ContextCompat; 22 | import androidx.core.view.GravityCompat; 23 | import androidx.recyclerview.widget.LinearLayoutManager; 24 | import androidx.recyclerview.widget.PagerSnapHelper; 25 | import androidx.recyclerview.widget.RecyclerView; 26 | 27 | import com.iwdael.carouselbanner.R; 28 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemChangeListener; 29 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 30 | 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | 34 | /** 35 | * author : Iwdael 36 | * e-mail : iwdael@outlook.com 37 | * github : http://github.com/iwdael 38 | * project : CarouselBanner 39 | */ 40 | public abstract class CarouselBannerBase extends FrameLayout { 41 | 42 | protected int autoPlayDuration;//刷新间隔时间 43 | 44 | protected boolean showIndicator;//是否显示指示器 45 | protected RecyclerView indicatorContainer; 46 | protected Drawable mSelectedDrawable; 47 | protected Drawable mUnselectedDrawable; 48 | protected IndicatorAdapter indicatorAdapter; 49 | protected float speedPerPixelMillisecond; 50 | protected int indicatorMargin;//指示器间距 51 | 52 | protected RecyclerView mRecyclerView; 53 | protected A adapter; 54 | protected L mLayoutManager; 55 | 56 | protected int WHAT_AUTO_PLAY = 1000; 57 | 58 | protected boolean hasInit; 59 | protected int bannerSize = 1; 60 | protected int currentIndex; 61 | protected boolean isPlaying; 62 | 63 | protected boolean isAutoPlaying; 64 | protected List tempUrlList = new ArrayList<>(); 65 | 66 | protected OnCarouselItemClickListener onBannerItemClickListener; 67 | protected OnCarouselItemChangeListener onCarouselItemChangeListener; 68 | 69 | 70 | protected Handler mHandler = new Handler(new Handler.Callback() { 71 | @Override 72 | public boolean handleMessage(Message msg) { 73 | if (msg.what == WHAT_AUTO_PLAY) { 74 | mRecyclerView.smoothScrollToPosition(++currentIndex); 75 | refreshIndicator(); 76 | mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); 77 | } 78 | return false; 79 | } 80 | }); 81 | 82 | public CarouselBannerBase(Context context) { 83 | this(context, null); 84 | } 85 | 86 | public CarouselBannerBase(Context context, AttributeSet attrs) { 87 | this(context, attrs, 0); 88 | } 89 | 90 | public CarouselBannerBase(Context context, AttributeSet attrs, int defStyleAttr) { 91 | super(context, attrs, defStyleAttr); 92 | initView(context, attrs); 93 | } 94 | 95 | protected void initView(Context context, AttributeSet attrs) { 96 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CarouselBannerBase); 97 | showIndicator = a.getBoolean(R.styleable.CarouselBannerBase_showIndicator, true); 98 | autoPlayDuration = a.getInt(R.styleable.CarouselBannerBase_interval, 4000); 99 | isAutoPlaying = a.getBoolean(R.styleable.CarouselBannerBase_autoPlaying, true); 100 | mSelectedDrawable = a.getDrawable(R.styleable.CarouselBannerBase_indicatorSelectedSrc); 101 | mUnselectedDrawable = a.getDrawable(R.styleable.CarouselBannerBase_indicatorUnselectedSrc); 102 | speedPerPixelMillisecond = a.getFloat(R.styleable.CarouselBannerBase_speedPerPixelMillisecond, 0.8f); 103 | if (mSelectedDrawable == null) { 104 | //绘制默认选中状态图形 105 | GradientDrawable selectedGradientDrawable = new GradientDrawable(); 106 | selectedGradientDrawable.setShape(GradientDrawable.OVAL); 107 | selectedGradientDrawable.setColor(getColor(R.color.selectIndicationColor)); 108 | selectedGradientDrawable.setSize(dp2px(5), dp2px(5)); 109 | selectedGradientDrawable.setCornerRadius(dp2px(5) / 2); 110 | mSelectedDrawable = new LayerDrawable(new Drawable[]{selectedGradientDrawable}); 111 | } 112 | if (mUnselectedDrawable == null) { 113 | //绘制默认未选中状态图形 114 | GradientDrawable unSelectedGradientDrawable = new GradientDrawable(); 115 | unSelectedGradientDrawable.setShape(GradientDrawable.OVAL); 116 | unSelectedGradientDrawable.setColor(getColor(R.color.unSelectIndicationColor)); 117 | unSelectedGradientDrawable.setSize(dp2px(5), dp2px(5)); 118 | unSelectedGradientDrawable.setCornerRadius(dp2px(5) / 2); 119 | mUnselectedDrawable = new LayerDrawable(new Drawable[]{unSelectedGradientDrawable}); 120 | } 121 | indicatorMargin = a.getDimensionPixelSize(R.styleable.CarouselBannerBase_indicatorSpace, dp2px(4)); 122 | int marginLeft = a.getDimensionPixelSize(R.styleable.CarouselBannerBase_indicatorMarginLeft, dp2px(16)); 123 | int marginRight = a.getDimensionPixelSize(R.styleable.CarouselBannerBase_indicatorMarginRight, dp2px(0)); 124 | int marginBottom = a.getDimensionPixelSize(R.styleable.CarouselBannerBase_indicatorMarginBottom, dp2px(11)); 125 | int g = a.getInt(R.styleable.CarouselBannerBase_indicatorGravity, 0); 126 | int gravity; 127 | if (g == 0) { 128 | gravity = GravityCompat.START; 129 | } else if (g == 2) { 130 | gravity = GravityCompat.END; 131 | } else { 132 | gravity = Gravity.CENTER; 133 | } 134 | int o = a.getInt(R.styleable.CarouselBannerBase_orientation, 0); 135 | int orientation = 0; 136 | if (o == 0) { 137 | orientation = LinearLayoutManager.HORIZONTAL; 138 | } else if (o == 1) { 139 | orientation = LinearLayoutManager.VERTICAL; 140 | } 141 | a.recycle(); 142 | //recyclerView部分 143 | mRecyclerView = new RecyclerView(context); 144 | new PagerSnapHelper().attachToRecyclerView(mRecyclerView); 145 | mLayoutManager = getLayoutManager(context, orientation); 146 | mRecyclerView.setLayoutManager(mLayoutManager); 147 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 148 | 149 | @Override 150 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 151 | onBannerScrolled(recyclerView, dx, dy); 152 | } 153 | 154 | @Override 155 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 156 | onBannerScrollStateChanged(recyclerView, newState); 157 | } 158 | }); 159 | LayoutParams vpLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 160 | ViewGroup.LayoutParams.MATCH_PARENT); 161 | addView(mRecyclerView, vpLayoutParams); 162 | //指示器部分 163 | indicatorContainer = new RecyclerView(context); 164 | 165 | LinearLayoutManager indicatorLayoutManager = new LinearLayoutManager(context, orientation, false); 166 | indicatorContainer.setLayoutManager(indicatorLayoutManager); 167 | indicatorAdapter = new IndicatorAdapter(); 168 | indicatorContainer.setAdapter(indicatorAdapter); 169 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 170 | params.gravity = Gravity.BOTTOM | gravity; 171 | params.setMargins(marginLeft, 0, marginRight, marginBottom); 172 | addView(indicatorContainer, params); 173 | if (!showIndicator) { 174 | indicatorContainer.setVisibility(GONE); 175 | } 176 | } 177 | 178 | protected abstract void onBannerScrolled(RecyclerView recyclerView, int dx, int dy); 179 | 180 | protected abstract void onBannerScrollStateChanged(RecyclerView recyclerView, int newState); 181 | 182 | protected abstract L getLayoutManager(Context context, int orientation); 183 | 184 | protected abstract A getAdapter(List list, OnCarouselItemClickListener onBannerItemClickListener); 185 | 186 | public void setIndicatorInterval(int millisecond) { 187 | this.autoPlayDuration = millisecond; 188 | } 189 | 190 | 191 | protected void setPlaying(boolean playing) { 192 | if (isAutoPlaying && hasInit) { 193 | if (!isPlaying && playing && adapter != null && adapter.getItemCount() > 2) { 194 | mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); 195 | isPlaying = true; 196 | } else if (isPlaying && !playing) { 197 | mHandler.removeMessages(WHAT_AUTO_PLAY); 198 | isPlaying = false; 199 | } 200 | } 201 | } 202 | 203 | public void setAutoPlaying(boolean isAutoPlaying) { 204 | this.isAutoPlaying = isAutoPlaying; 205 | setPlaying(this.isAutoPlaying); 206 | } 207 | 208 | public boolean isPlaying() { 209 | return isPlaying; 210 | } 211 | 212 | public void setShowIndicator(boolean showIndicator) { 213 | this.showIndicator = showIndicator; 214 | indicatorContainer.setVisibility(showIndicator ? VISIBLE : GONE); 215 | } 216 | 217 | public void setOnCarouselItemClickListener(OnCarouselItemClickListener onBannerItemClickListener) { 218 | this.onBannerItemClickListener = onBannerItemClickListener; 219 | } 220 | 221 | public void setOnCarouselItemChangeListener(OnCarouselItemChangeListener onCarouselItemChangeListener) { 222 | this.onCarouselItemChangeListener = onCarouselItemChangeListener; 223 | } 224 | 225 | public void initBanner(List newList) { 226 | //解决recyclerView嵌套问题 227 | if (compareListDifferent(newList, tempUrlList)) { 228 | hasInit = false; 229 | setVisibility(VISIBLE); 230 | setPlaying(false); 231 | adapter = getAdapter(newList, this.onBannerItemClickListener); 232 | mRecyclerView.setAdapter(adapter); 233 | tempUrlList = newList; 234 | bannerSize = tempUrlList.size(); 235 | if (bannerSize > 1) { 236 | indicatorContainer.setVisibility(VISIBLE); 237 | currentIndex = bannerSize * 10000; 238 | mRecyclerView.scrollToPosition(currentIndex); 239 | indicatorAdapter.notifyDataSetChanged(); 240 | } else { 241 | indicatorContainer.setVisibility(GONE); 242 | currentIndex = 0; 243 | } 244 | hasInit = true; 245 | setPlaying(true); 246 | } 247 | if (!showIndicator) { 248 | indicatorContainer.setVisibility(GONE); 249 | } 250 | } 251 | 252 | 253 | @Override 254 | public boolean dispatchTouchEvent(MotionEvent ev) { 255 | switch (ev.getAction()) { 256 | case MotionEvent.ACTION_DOWN: 257 | setPlaying(false); 258 | break; 259 | case MotionEvent.ACTION_UP: 260 | case MotionEvent.ACTION_CANCEL: 261 | setPlaying(true); 262 | break; 263 | } 264 | //解决recyclerView嵌套问题 265 | try { 266 | return super.dispatchTouchEvent(ev); 267 | } catch (IllegalArgumentException ex) { 268 | ex.printStackTrace(); 269 | } 270 | return false; 271 | } 272 | 273 | //解决recyclerView嵌套问题 274 | @Override 275 | public boolean onTouchEvent(MotionEvent ev) { 276 | try { 277 | return super.onTouchEvent(ev); 278 | } catch (IllegalArgumentException ex) { 279 | ex.printStackTrace(); 280 | } 281 | return false; 282 | } 283 | 284 | //解决recyclerView嵌套问题 285 | @Override 286 | public boolean onInterceptTouchEvent(MotionEvent ev) { 287 | try { 288 | return super.onInterceptTouchEvent(ev); 289 | } catch (IllegalArgumentException ex) { 290 | ex.printStackTrace(); 291 | } 292 | return false; 293 | } 294 | 295 | @Override 296 | protected void onAttachedToWindow() { 297 | super.onAttachedToWindow(); 298 | setPlaying(true); 299 | } 300 | 301 | @Override 302 | protected void onDetachedFromWindow() { 303 | super.onDetachedFromWindow(); 304 | setPlaying(false); 305 | } 306 | 307 | @Override 308 | protected void onWindowVisibilityChanged(int visibility) { 309 | super.onWindowVisibilityChanged(visibility); 310 | if (visibility == VISIBLE) { 311 | setPlaying(true); 312 | } else { 313 | setPlaying(false); 314 | } 315 | } 316 | 317 | 318 | protected class IndicatorAdapter extends RecyclerView.Adapter { 319 | 320 | int currentPosition = 0; 321 | 322 | public void setPosition(int currentPosition) { 323 | this.currentPosition = currentPosition; 324 | } 325 | 326 | @Override 327 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 328 | 329 | ImageView bannerPoint = new ImageView(getContext()); 330 | RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 331 | ViewGroup.LayoutParams.WRAP_CONTENT); 332 | lp.setMargins(indicatorMargin, indicatorMargin, indicatorMargin, indicatorMargin); 333 | bannerPoint.setLayoutParams(lp); 334 | return new RecyclerView.ViewHolder(bannerPoint) { 335 | }; 336 | } 337 | 338 | @Override 339 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 340 | ImageView bannerPoint = (ImageView) holder.itemView; 341 | bannerPoint.setImageDrawable(currentPosition == position ? mSelectedDrawable : mUnselectedDrawable); 342 | 343 | } 344 | 345 | @Override 346 | public int getItemCount() { 347 | return bannerSize; 348 | } 349 | } 350 | 351 | 352 | /** 353 | * author : Iwdael 354 | * e-mail : iwdael@outlook.com 355 | * github : http://github.com/iwdael 356 | * project : CarouselBanner 357 | */ 358 | protected void refreshIndicator() { 359 | if (showIndicator && bannerSize > 1) { 360 | indicatorAdapter.setPosition(currentIndex % bannerSize); 361 | indicatorAdapter.notifyDataSetChanged(); 362 | } 363 | if (onCarouselItemChangeListener != null) 364 | onCarouselItemChangeListener.onItemChange(currentIndex % bannerSize); 365 | } 366 | 367 | 368 | protected int dp2px(int dp) { 369 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); 370 | } 371 | 372 | /** 373 | * author : Iwdael 374 | * e-mail : iwdael@outlook.com 375 | * github : http://github.com/iwdael 376 | * project : CarouselBanner 377 | */ 378 | protected int getColor(@ColorRes int color) { 379 | return ContextCompat.getColor(getContext(), color); 380 | } 381 | 382 | protected boolean compareListDifferent(List newTabList, List oldTabList) { 383 | if (oldTabList == null || oldTabList.isEmpty()) 384 | return true; 385 | if (newTabList.size() != oldTabList.size()) 386 | return true; 387 | for (int i = 0; i < newTabList.size(); i++) { 388 | if (TextUtils.isEmpty(newTabList.get(i))) 389 | return true; 390 | if (!newTabList.get(i).equals(oldTabList.get(i))) { 391 | return true; 392 | } 393 | } 394 | return false; 395 | } 396 | 397 | } 398 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/interfaces/CarouselImageFactory.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.interfaces; 2 | 3 | import android.widget.ImageView; 4 | 5 | /** 6 | * author : Iwdael 7 | * e-mail : iwdael@outlook.com 8 | * github : http://github.com/iwdael 9 | * project : CarouselBanner 10 | */ 11 | 12 | public interface CarouselImageFactory { 13 | void onLoadFactory(String url, ImageView view); 14 | } 15 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/interfaces/OnCarouselItemChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.interfaces; 2 | 3 | /** 4 | * author : Iwdael 5 | * e-mail : iwdael@outlook.com 6 | * github : http://github.com/iwdael 7 | * project : CarouselBanner 8 | */ 9 | 10 | public interface OnCarouselItemChangeListener { 11 | void onItemChange(int position); 12 | } 13 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/interfaces/OnCarouselItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.interfaces; 2 | 3 | /** 4 | * author : Iwdael 5 | * e-mail : iwdael@outlook.com 6 | * github : http://github.com/iwdael 7 | * project : CarouselBanner 8 | */ 9 | 10 | public interface OnCarouselItemClickListener { 11 | 12 | void onItemClick(int position, String url); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/layoutmanager/BannerLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.layoutmanager; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.util.AttributeSet; 6 | import android.util.DisplayMetrics; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.recyclerview.widget.LinearLayoutManager; 10 | import androidx.recyclerview.widget.LinearSmoothScroller; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | /** 14 | * author : Iwdael 15 | * e-mail : iwdael@outlook.com 16 | * github : http://github.com/iwdael 17 | * project : CarouselBanner 18 | */ 19 | 20 | public class BannerLayoutManager extends LinearLayoutManager { 21 | 22 | private float speedPerPixelMillisecond; 23 | 24 | public BannerLayoutManager(Context context) { 25 | super(context); 26 | } 27 | 28 | public BannerLayoutManager(Context context, int orientation, boolean reverseLayout) { 29 | super(context, orientation, reverseLayout); 30 | } 31 | public BannerLayoutManager(Context context, int orientation, boolean reverseLayout , float speedPerPixelMillisecond) { 32 | super(context, orientation, reverseLayout); 33 | this.speedPerPixelMillisecond=speedPerPixelMillisecond; 34 | } 35 | 36 | public BannerLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 37 | super(context, attrs, defStyleAttr, defStyleRes); 38 | } 39 | 40 | @Override 41 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { 42 | CenterSmoothScroller centerSmoothScroller = new CenterSmoothScroller(recyclerView.getContext()); 43 | centerSmoothScroller.setTargetPosition(position); 44 | startSmoothScroll(centerSmoothScroller); 45 | } 46 | 47 | 48 | private class CenterSmoothScroller extends LinearSmoothScroller { 49 | 50 | CenterSmoothScroller(Context context) { 51 | super(context); 52 | } 53 | 54 | @Nullable 55 | @Override 56 | public PointF computeScrollVectorForPosition(int targetPosition) { 57 | return BannerLayoutManager.this.computeScrollVectorForPosition(targetPosition); 58 | } 59 | 60 | @Override 61 | public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) { 62 | return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2); 63 | } 64 | 65 | protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { 66 | return speedPerPixelMillisecond; 67 | } 68 | 69 | @Override 70 | protected int getVerticalSnapPreference() { 71 | return SNAP_TO_START; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/layoutmanager/CenterScrollListener.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.layoutmanager; 2 | 3 | 4 | import androidx.recyclerview.widget.RecyclerView; 5 | 6 | /** 7 | * author : Iwdael 8 | * e-mail : iwdael@outlook.com 9 | * github : http://github.com/iwdael 10 | * project : CarouselBanner 11 | */ 12 | public class CenterScrollListener extends RecyclerView.OnScrollListener { 13 | private boolean mAutoSet = false; 14 | 15 | @Override 16 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 17 | super.onScrollStateChanged(recyclerView, newState); 18 | final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 19 | if (!(layoutManager instanceof CoolBannerLayoutManager)) { 20 | mAutoSet = true; 21 | return; 22 | } 23 | 24 | final CoolBannerLayoutManager.OnPageChangeListener onPageChangeListener = ((CoolBannerLayoutManager) layoutManager).onPageChangeListener; 25 | if (onPageChangeListener != null) { 26 | onPageChangeListener.onPageScrollStateChanged(newState); 27 | } 28 | 29 | if (newState == RecyclerView.SCROLL_STATE_IDLE) { 30 | if (mAutoSet) { 31 | if (onPageChangeListener != null) { 32 | onPageChangeListener.onPageSelected(((CoolBannerLayoutManager) layoutManager).getCurrentPosition()); 33 | } 34 | mAutoSet = false; 35 | } else { 36 | final int delta; 37 | delta = ((CoolBannerLayoutManager) layoutManager).getOffsetToCenter(); 38 | if (delta != 0) { 39 | if (((CoolBannerLayoutManager) layoutManager).getOrientation() == CoolBannerLayoutManager.VERTICAL) 40 | recyclerView.smoothScrollBy(0, delta); 41 | else 42 | recyclerView.smoothScrollBy(delta, 0); 43 | mAutoSet = true; 44 | } else { 45 | if (onPageChangeListener != null) { 46 | onPageChangeListener.onPageSelected(((CoolBannerLayoutManager) layoutManager).getCurrentPosition()); 47 | } 48 | mAutoSet = false; 49 | } 50 | } 51 | } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) { 52 | mAutoSet = false; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/layoutmanager/CoolBannerLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.layoutmanager; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.util.DisplayMetrics; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.recyclerview.widget.LinearSmoothScroller; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; 14 | 15 | 16 | /** 17 | * author : Iwdael 18 | * e-mail : iwdael@outlook.com 19 | * github : http://github.com/iwdael 20 | * project : CarouselBanner 21 | */ 22 | public class CoolBannerLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider { 23 | 24 | private static final float SCALE_RATE = 1.2f; 25 | private float speedPerPixelMillisecond; 26 | private int mOrientation = HORIZONTAL; 27 | 28 | private static final int HORIZONTAL = OrientationHelper.HORIZONTAL; 29 | static final int VERTICAL = OrientationHelper.VERTICAL; 30 | 31 | private int mDecoratedMeasurement; 32 | 33 | private int mDecoratedMeasurementInOther; 34 | 35 | private int itemSpace; 36 | 37 | private int mSpaceMain; 38 | 39 | private int mSpaceInOther; 40 | 41 | 42 | private float mOffset; 43 | 44 | 45 | private OrientationHelper mOrientationHelper; 46 | 47 | 48 | private boolean mSmoothScrollbarEnabled = true; 49 | 50 | 51 | private int mPendingScrollPosition = NO_POSITION; 52 | 53 | 54 | private float mInterval; //the mInterval of each item's mOffset 55 | 56 | OnPageChangeListener onPageChangeListener; 57 | 58 | private boolean mInfinite = false; 59 | 60 | private int mLeftItems; 61 | 62 | private int mRightItems; 63 | 64 | 65 | public CoolBannerLayoutManager() { 66 | this(HORIZONTAL, 0,0.8f); 67 | } 68 | 69 | 70 | public CoolBannerLayoutManager(int orientation, int itemSpace,float speedPerPixelMillisecond) { 71 | this.itemSpace = itemSpace; 72 | setOrientation(orientation); 73 | setAutoMeasureEnabled(true); 74 | this.speedPerPixelMillisecond=speedPerPixelMillisecond; 75 | } 76 | 77 | 78 | private float setInterval() { 79 | return mDecoratedMeasurement * ((1.2f - 1) / 2 + 1) + itemSpace; 80 | } 81 | 82 | 83 | 84 | private float calculateScale(float x) { 85 | float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f); 86 | float diff = 0f; 87 | if ((mDecoratedMeasurement - deltaX) > 0) diff = mDecoratedMeasurement - deltaX; 88 | return (SCALE_RATE - 1f) / mDecoratedMeasurement * diff + 1; 89 | } 90 | 91 | 92 | private float setViewElevation(View itemView, float targetOffset) { 93 | return 0; 94 | } 95 | 96 | @Override 97 | public RecyclerView.LayoutParams generateDefaultLayoutParams() { 98 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 99 | ViewGroup.LayoutParams.WRAP_CONTENT); 100 | } 101 | 102 | 103 | 104 | @Override 105 | public boolean canScrollHorizontally() { 106 | return mOrientation == HORIZONTAL; 107 | } 108 | 109 | 110 | @Override 111 | public boolean canScrollVertically() { 112 | return mOrientation == VERTICAL; 113 | } 114 | 115 | 116 | int getOrientation() { 117 | return mOrientation; 118 | } 119 | 120 | 121 | void setOrientation(int orientation) { 122 | if (orientation != HORIZONTAL && orientation != VERTICAL) { 123 | throw new IllegalArgumentException("invalid orientation:" + orientation); 124 | } 125 | assertNotInLayoutOrScroll(null); 126 | if (orientation == mOrientation) { 127 | return; 128 | } 129 | mOrientation = orientation; 130 | mOrientationHelper = null; 131 | removeAllViews(); 132 | } 133 | 134 | @Override 135 | public View findViewByPosition(int position) { 136 | final int childCount = getChildCount(); 137 | if (childCount == 0) { 138 | return null; 139 | } 140 | final int firstChild = getPosition(getChildAt(0)); 141 | final int viewPosition = position - firstChild; 142 | if (viewPosition >= 0 && viewPosition < childCount) { 143 | final View child = getChildAt(viewPosition); 144 | if (getPosition(child) == position) { 145 | return child; // in pre-layout, this may not match 146 | } 147 | } 148 | return super.findViewByPosition(position); 149 | } 150 | 151 | @Override 152 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { 153 | CenterSmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext()); 154 | smoothScroller.setTargetPosition(position); 155 | startSmoothScroll(smoothScroller); 156 | } 157 | 158 | 159 | private class CenterSmoothScroller extends LinearSmoothScroller { 160 | 161 | CenterSmoothScroller(Context context) { 162 | super(context); 163 | } 164 | 165 | @Nullable 166 | @Override 167 | public PointF computeScrollVectorForPosition(int targetPosition) { 168 | return CoolBannerLayoutManager.this.computeScrollVectorForPosition(targetPosition); 169 | } 170 | 171 | @Override 172 | public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) { 173 | return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2); 174 | } 175 | 176 | protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { 177 | return speedPerPixelMillisecond; 178 | } 179 | 180 | @Override 181 | protected int getVerticalSnapPreference() { 182 | return SNAP_TO_START; 183 | } 184 | } 185 | 186 | 187 | public PointF computeScrollVectorForPosition(int targetPosition) { 188 | if (getChildCount() == 0) { 189 | return null; 190 | } 191 | final int firstChildPos = getPosition(getChildAt(0)); 192 | final float direction = targetPosition < firstChildPos ? 193 | -1 / getDistanceRatio() : 1 / getDistanceRatio(); 194 | if (mOrientation == HORIZONTAL) { 195 | return new PointF(direction, 0); 196 | } else { 197 | return new PointF(0, direction); 198 | } 199 | } 200 | 201 | @Override 202 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 203 | if (state.getItemCount() == 0) { 204 | removeAndRecycleAllViews(recycler); 205 | mOffset = 0; 206 | return; 207 | } 208 | if (mOrientationHelper == null) { 209 | mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation); 210 | } 211 | if (getChildCount() == 0) { 212 | View scrap = recycler.getViewForPosition(0); 213 | measureChildWithMargins(scrap, 0, 0); 214 | mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap); 215 | mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap); 216 | mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2; 217 | mSpaceInOther = (mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2; 218 | mInterval = setInterval(); 219 | mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1; 220 | mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1; 221 | } 222 | if (mPendingScrollPosition != NO_POSITION) { 223 | mOffset = mPendingScrollPosition * mInterval; 224 | } 225 | detachAndScrapAttachedViews(recycler); 226 | layoutItems(recycler); 227 | } 228 | 229 | @Override 230 | public void onLayoutCompleted(RecyclerView.State state) { 231 | super.onLayoutCompleted(state); 232 | mPendingScrollPosition = NO_POSITION; 233 | } 234 | 235 | 236 | private float getProperty(int position) { 237 | return position * mInterval; 238 | } 239 | 240 | @Override 241 | public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) { 242 | removeAllViews(); 243 | mOffset = 0; 244 | } 245 | 246 | @Override 247 | public void scrollToPosition(int position) { 248 | mPendingScrollPosition = position; 249 | mOffset = position * mInterval; 250 | requestLayout(); 251 | } 252 | 253 | @Override 254 | public int computeHorizontalScrollOffset(RecyclerView.State state) { 255 | return computeScrollOffset(); 256 | } 257 | 258 | @Override 259 | public int computeVerticalScrollOffset(RecyclerView.State state) { 260 | return computeScrollOffset(); 261 | } 262 | 263 | @Override 264 | public int computeHorizontalScrollExtent(RecyclerView.State state) { 265 | return computeScrollExtent(); 266 | } 267 | 268 | @Override 269 | public int computeVerticalScrollExtent(RecyclerView.State state) { 270 | return computeScrollExtent(); 271 | } 272 | 273 | @Override 274 | public int computeHorizontalScrollRange(RecyclerView.State state) { 275 | return computeScrollRange(); 276 | } 277 | 278 | @Override 279 | public int computeVerticalScrollRange(RecyclerView.State state) { 280 | return computeScrollRange(); 281 | } 282 | 283 | private int computeScrollOffset() { 284 | if (getChildCount() == 0) { 285 | return 0; 286 | } 287 | 288 | if (!mSmoothScrollbarEnabled) { 289 | return getItemCount() - getCurrentPosition() - 1; 290 | } 291 | 292 | final float realOffset = getOffsetOfRightAdapterPosition(); 293 | return (int) realOffset; 294 | } 295 | 296 | private int computeScrollExtent() { 297 | if (getChildCount() == 0) { 298 | return 0; 299 | } 300 | 301 | if (!mSmoothScrollbarEnabled) { 302 | return 1; 303 | } 304 | 305 | return (int) mInterval; 306 | } 307 | 308 | private int computeScrollRange() { 309 | if (getChildCount() == 0) { 310 | return 0; 311 | } 312 | 313 | if (!mSmoothScrollbarEnabled) { 314 | return getItemCount(); 315 | } 316 | 317 | return (int) (getItemCount() * mInterval); 318 | } 319 | 320 | @Override 321 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 322 | if (mOrientation == VERTICAL) { 323 | return 0; 324 | } 325 | return scrollBy(dx, recycler, state); 326 | } 327 | 328 | @Override 329 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 330 | if (mOrientation == HORIZONTAL) { 331 | return 0; 332 | } 333 | return scrollBy(dy, recycler, state); 334 | } 335 | 336 | private int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 337 | if (getChildCount() == 0 || dy == 0) { 338 | return 0; 339 | } 340 | if (mOrientationHelper == null) { 341 | mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation); 342 | } 343 | int willScroll = dy; 344 | 345 | float realDx = dy / getDistanceRatio(); 346 | if (Math.abs(realDx) < 0.00000001f) { 347 | return 0; 348 | } 349 | float targetOffset = mOffset + realDx; 350 | 351 | //handle the boundary 352 | if (!mInfinite && targetOffset < getMinOffset()) { 353 | willScroll -= (targetOffset - getMinOffset()) * getDistanceRatio(); 354 | } else if (!mInfinite && targetOffset > getMaxOffset()) { 355 | willScroll = (int) ((getMaxOffset() - mOffset) * getDistanceRatio()); 356 | } 357 | 358 | 359 | realDx = willScroll / getDistanceRatio(); 360 | 361 | mOffset += realDx; 362 | 363 | // we re-layout all current views in the right place 364 | for (int i = 0; i < getChildCount(); i++) { 365 | final View scrap = getChildAt(i); 366 | final float delta = propertyChangeWhenScroll(scrap) - realDx; 367 | layoutScrap(scrap, delta); 368 | } 369 | 370 | //handle recycle 371 | layoutItems(recycler); 372 | 373 | return willScroll; 374 | } 375 | 376 | private void layoutItems(RecyclerView.Recycler recycler) { 377 | final float rightOffset = getOffsetOfRightAdapterPosition(); 378 | for (int i = 0; i < getChildCount(); i++) { 379 | final View view = getChildAt(i); 380 | final int position = getPosition(view); 381 | if (removeCondition(getProperty(position) - rightOffset)) { 382 | removeAndRecycleView(view, recycler); 383 | } 384 | } 385 | 386 | //make sure that current position start from 0 to 1 387 | final int currentPos = getCurrentPositionOffset(); 388 | int start = currentPos - mLeftItems; 389 | int end = currentPos + mRightItems; 390 | 391 | final int itemCount = getItemCount(); 392 | if (!mInfinite) { 393 | if (start < 0) start = 0; 394 | if (end > itemCount) end = itemCount; 395 | } 396 | 397 | float lastOrderWeight = Float.MIN_VALUE; 398 | for (int i = start; i < end; i++) { 399 | if (!removeCondition(getProperty(i) - mOffset)) { 400 | // start and end zero base on current position, 401 | // so we need to calculate the adapter position 402 | int adapterPosition = i; 403 | if (i >= itemCount) { 404 | adapterPosition %= itemCount; 405 | } else if (i < 0) { 406 | int delta = (-adapterPosition) % itemCount; 407 | if (delta == 0) delta = itemCount; 408 | adapterPosition = itemCount - delta; 409 | } 410 | if (findViewByPosition(adapterPosition) == null) { 411 | final View scrap = recycler.getViewForPosition(adapterPosition); 412 | measureChildWithMargins(scrap, 0, 0); 413 | resetViewProperty(scrap); 414 | // we need i to calculate the real offset of current view 415 | final float targetOffset = getProperty(i) - mOffset; 416 | layoutScrap(scrap, targetOffset); 417 | if ((float) adapterPosition > lastOrderWeight) { 418 | addView(scrap); 419 | } else { 420 | addView(scrap, 0); 421 | } 422 | lastOrderWeight = (float) adapterPosition; 423 | } 424 | } 425 | } 426 | } 427 | 428 | private boolean removeCondition(float targetOffset) { 429 | return targetOffset > maxRemoveOffset() || targetOffset < minRemoveOffset(); 430 | } 431 | 432 | private void resetViewProperty(View v) { 433 | v.setRotation(0); 434 | v.setRotationY(0); 435 | v.setRotationX(0); 436 | v.setScaleX(1f); 437 | v.setScaleY(1f); 438 | v.setAlpha(1f); 439 | } 440 | 441 | private float getMaxOffset() { 442 | return (getItemCount() - 1) * mInterval; 443 | } 444 | 445 | private float getMinOffset() { 446 | return 0; 447 | } 448 | 449 | private void layoutScrap(View scrap, float targetOffset) { 450 | final int left = calItemLeft(scrap, targetOffset); 451 | final int top = calItemTop(scrap, targetOffset); 452 | if (mOrientation == VERTICAL) { 453 | layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top, 454 | mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement); 455 | } else { 456 | layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top, 457 | mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther); 458 | } 459 | setItemViewProperty(scrap, targetOffset); 460 | } 461 | 462 | private void setItemViewProperty(View itemView, float targetOffset) { 463 | float scale = calculateScale(targetOffset + mSpaceMain); 464 | itemView.setScaleX(scale); 465 | itemView.setScaleY(scale); 466 | } 467 | 468 | private int calItemLeft(View itemView, float targetOffset) { 469 | return mOrientation == VERTICAL ? 0 : (int) targetOffset; 470 | } 471 | 472 | private int calItemTop(View itemView, float targetOffset) { 473 | return mOrientation == VERTICAL ? (int) targetOffset : 0; 474 | } 475 | 476 | /** 477 | * author : Iwdael 478 | * e-mail : iwdael@outlook.com 479 | * github : http://github.com/iwdael 480 | * project : CarouselBanner 481 | */ 482 | private float maxRemoveOffset() { 483 | return mOrientationHelper.getTotalSpace() - mSpaceMain; 484 | } 485 | 486 | /** 487 | * author : Iwdael 488 | * e-mail : iwdael@outlook.com 489 | * github : http://github.com/iwdael 490 | * project : CarouselBanner 491 | */ 492 | private float minRemoveOffset() { 493 | return -mDecoratedMeasurement - mOrientationHelper.getStartAfterPadding() - mSpaceMain; 494 | } 495 | 496 | private float propertyChangeWhenScroll(View itemView) { 497 | if (mOrientation == VERTICAL) 498 | return itemView.getTop() - mSpaceMain; 499 | return itemView.getLeft() - mSpaceMain; 500 | } 501 | 502 | private float getDistanceRatio() { 503 | return 1f; 504 | } 505 | 506 | public int getCurrentPosition() { 507 | int position = getCurrentPositionOffset(); 508 | if (!mInfinite) return Math.abs(position); 509 | position = (position >= 0 ? position % getItemCount() : getItemCount() + position % getItemCount()); 510 | return position; 511 | } 512 | 513 | private int getCurrentPositionOffset() { 514 | return Math.round(mOffset / mInterval); 515 | } 516 | 517 | /** 518 | * author : Iwdael 519 | * e-mail : iwdael@outlook.com 520 | * github : http://github.com/iwdael 521 | * project : CarouselBanner 522 | */ 523 | private float getOffsetOfRightAdapterPosition() { 524 | return mInfinite ? 525 | (mOffset >= 0 ? 526 | (mOffset % (mInterval * getItemCount())) : 527 | (getItemCount() * mInterval + mOffset % (mInterval * getItemCount()))) : 528 | mOffset; 529 | } 530 | 531 | /** 532 | * author : Iwdael 533 | * e-mail : iwdael@outlook.com 534 | * github : http://github.com/iwdael 535 | * project : CarouselBanner 536 | */ 537 | public int getOffsetToCenter() { 538 | if (mInfinite) 539 | return (int) ((getCurrentPositionOffset() * mInterval - mOffset) * getDistanceRatio()); 540 | return (int) ((getCurrentPosition() * 541 | (mInterval) - mOffset) * getDistanceRatio()); 542 | } 543 | 544 | public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) { 545 | this.onPageChangeListener = onPageChangeListener; 546 | } 547 | 548 | public interface OnPageChangeListener { 549 | void onPageSelected(int position); 550 | 551 | void onPageScrollStateChanged(int state); 552 | } 553 | } 554 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/layoutmanager/OrientationHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 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.iwdael.carouselbanner.layoutmanager; 18 | 19 | import android.graphics.Rect; 20 | import android.view.View; 21 | import android.widget.LinearLayout; 22 | 23 | import androidx.recyclerview.widget.RecyclerView; 24 | 25 | /** 26 | * author : Iwdael 27 | * e-mail : iwdael@outlook.com 28 | * github : http://github.com/iwdael 29 | * project : CarouselBanner 30 | */ 31 | @SuppressWarnings({"WeakerAccess","unused"}) 32 | public abstract class OrientationHelper { 33 | 34 | private static final int INVALID_SIZE = Integer.MIN_VALUE; 35 | 36 | protected final RecyclerView.LayoutManager mLayoutManager; 37 | 38 | public static final int HORIZONTAL = LinearLayout.HORIZONTAL; 39 | 40 | public static final int VERTICAL = LinearLayout.VERTICAL; 41 | 42 | private int mLastTotalSpace = INVALID_SIZE; 43 | 44 | final Rect mTmpRect = new Rect(); 45 | 46 | private OrientationHelper(RecyclerView.LayoutManager layoutManager) { 47 | mLayoutManager = layoutManager; 48 | } 49 | 50 | 51 | public void onLayoutComplete() { 52 | mLastTotalSpace = getTotalSpace(); 53 | } 54 | 55 | 56 | public int getTotalSpaceChange() { 57 | return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace; 58 | } 59 | 60 | 61 | public abstract int getDecoratedStart(View view); 62 | 63 | 64 | 65 | public abstract int getDecoratedEnd(View view); 66 | 67 | 68 | public abstract int getTransformedEndWithDecoration(View view); 69 | 70 | 71 | public abstract int getTransformedStartWithDecoration(View view); 72 | 73 | 74 | public abstract int getDecoratedMeasurement(View view); 75 | 76 | 77 | public abstract int getDecoratedMeasurementInOther(View view); 78 | 79 | 80 | public abstract int getStartAfterPadding(); 81 | 82 | 83 | public abstract int getEndAfterPadding(); 84 | 85 | 86 | public abstract int getEnd(); 87 | 88 | 89 | public abstract int getTotalSpace(); 90 | 91 | 92 | public abstract int getTotalSpaceInOther(); 93 | 94 | 95 | public abstract int getEndPadding(); 96 | 97 | 98 | public abstract int getMode(); 99 | 100 | 101 | public abstract int getModeInOther(); 102 | 103 | 104 | public static OrientationHelper createOrientationHelper( 105 | RecyclerView.LayoutManager layoutManager, int orientation) { 106 | switch (orientation) { 107 | case HORIZONTAL: 108 | return createHorizontalHelper(layoutManager); 109 | case VERTICAL: 110 | return createVerticalHelper(layoutManager); 111 | } 112 | throw new IllegalArgumentException("invalid orientation"); 113 | } 114 | 115 | 116 | public static OrientationHelper createHorizontalHelper( 117 | RecyclerView.LayoutManager layoutManager) { 118 | return new OrientationHelper(layoutManager) { 119 | @Override 120 | public int getEndAfterPadding() { 121 | return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight(); 122 | } 123 | 124 | @Override 125 | public int getEnd() { 126 | return mLayoutManager.getWidth(); 127 | } 128 | 129 | @Override 130 | public int getStartAfterPadding() { 131 | return mLayoutManager.getPaddingLeft(); 132 | } 133 | 134 | @Override 135 | public int getDecoratedMeasurement(View view) { 136 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 137 | view.getLayoutParams(); 138 | return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + params.rightMargin; 139 | } 140 | 141 | @Override 142 | public int getDecoratedMeasurementInOther(View view) { 143 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 144 | view.getLayoutParams(); 145 | return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin 146 | + params.bottomMargin; 147 | } 148 | 149 | @Override 150 | public int getDecoratedEnd(View view) { 151 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 152 | view.getLayoutParams(); 153 | return mLayoutManager.getDecoratedRight(view) + params.rightMargin; 154 | } 155 | 156 | @Override 157 | public int getDecoratedStart(View view) { 158 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 159 | view.getLayoutParams(); 160 | return mLayoutManager.getDecoratedLeft(view) - params.leftMargin; 161 | } 162 | 163 | @Override 164 | public int getTransformedEndWithDecoration(View view) { 165 | mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect); 166 | return mTmpRect.right; 167 | } 168 | 169 | @Override 170 | public int getTransformedStartWithDecoration(View view) { 171 | mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect); 172 | return mTmpRect.left; 173 | } 174 | 175 | @Override 176 | public int getTotalSpace() { 177 | return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft() 178 | - mLayoutManager.getPaddingRight(); 179 | } 180 | 181 | @Override 182 | public int getTotalSpaceInOther() { 183 | return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop() 184 | - mLayoutManager.getPaddingBottom(); 185 | } 186 | 187 | @Override 188 | public int getEndPadding() { 189 | return mLayoutManager.getPaddingRight(); 190 | } 191 | 192 | @Override 193 | public int getMode() { 194 | return mLayoutManager.getWidthMode(); 195 | } 196 | 197 | @Override 198 | public int getModeInOther() { 199 | return mLayoutManager.getHeightMode(); 200 | } 201 | }; 202 | } 203 | 204 | 205 | public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) { 206 | return new OrientationHelper(layoutManager) { 207 | @Override 208 | public int getEndAfterPadding() { 209 | return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom(); 210 | } 211 | 212 | @Override 213 | public int getEnd() { 214 | return mLayoutManager.getHeight(); 215 | } 216 | 217 | @Override 218 | public int getStartAfterPadding() { 219 | return mLayoutManager.getPaddingTop(); 220 | } 221 | 222 | @Override 223 | public int getDecoratedMeasurement(View view) { 224 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 225 | view.getLayoutParams(); 226 | return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin 227 | + params.bottomMargin; 228 | } 229 | 230 | @Override 231 | public int getDecoratedMeasurementInOther(View view) { 232 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 233 | view.getLayoutParams(); 234 | return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin 235 | + params.rightMargin; 236 | } 237 | 238 | @Override 239 | public int getDecoratedEnd(View view) { 240 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 241 | view.getLayoutParams(); 242 | return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin; 243 | } 244 | 245 | @Override 246 | public int getDecoratedStart(View view) { 247 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 248 | view.getLayoutParams(); 249 | return mLayoutManager.getDecoratedTop(view) - params.topMargin; 250 | } 251 | 252 | @Override 253 | public int getTransformedEndWithDecoration(View view) { 254 | mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect); 255 | return mTmpRect.bottom; 256 | } 257 | 258 | @Override 259 | public int getTransformedStartWithDecoration(View view) { 260 | mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect); 261 | return mTmpRect.top; 262 | } 263 | 264 | @Override 265 | public int getTotalSpace() { 266 | return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop() 267 | - mLayoutManager.getPaddingBottom(); 268 | } 269 | 270 | @Override 271 | public int getTotalSpaceInOther() { 272 | return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft() 273 | - mLayoutManager.getPaddingRight(); 274 | } 275 | 276 | @Override 277 | public int getEndPadding() { 278 | return mLayoutManager.getPaddingBottom(); 279 | } 280 | 281 | @Override 282 | public int getMode() { 283 | return mLayoutManager.getHeightMode(); 284 | } 285 | 286 | @Override 287 | public int getModeInOther() { 288 | return mLayoutManager.getWidthMode(); 289 | } 290 | }; 291 | } 292 | } -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/viewholder/CarouselViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.viewholder; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.ImageView; 6 | 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import com.iwdael.carouselbanner.base.BaseViewHolder; 10 | 11 | /** 12 | * author : Iwdael 13 | * e-mail : iwdael@outlook.com 14 | * github : http://github.com/iwdael 15 | * project : CarouselBanner 16 | */ 17 | 18 | public class CarouselViewHolder extends BaseViewHolder { 19 | 20 | public CarouselViewHolder(View itemView) { 21 | super(itemView); 22 | imageView = (ImageView) itemView; 23 | RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 24 | imageView.setLayoutParams(params); 25 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /carouselbanner/src/main/java/com/iwdael/carouselbanner/viewholder/CoolCarouselViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.viewholder; 2 | 3 | import android.view.View; 4 | 5 | import com.iwdael.carouselbanner.R; 6 | import com.iwdael.carouselbanner.base.BaseViewHolder; 7 | 8 | /** 9 | * author : Iwdael 10 | * e-mail : iwdael@outlook.com 11 | * github : http://github.com/iwdael 12 | * project : CarouselBanner 13 | */ 14 | 15 | public class CoolCarouselViewHolder extends BaseViewHolder { 16 | 17 | public CoolCarouselViewHolder(View itemView) { 18 | super(itemView); 19 | imageView = itemView.findViewById(R.id.image); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /carouselbanner/src/main/res/layout/item_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /carouselbanner/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 | -------------------------------------------------------------------------------- /carouselbanner/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF4081 4 | #303F9F 5 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | buidConfig = [ 3 | 'compileSdkVersion': 30, 4 | 'targetSdkVersion' : 30, 5 | 'minSdkVersion' : 19, 6 | 'versionCode' : 1, 7 | 'versionName' : '1.0.0', 8 | 'buildToolsVersion': '30.0.3' 9 | ] 10 | dependent = [ 11 | 'appcompat' : 'androidx.appcompat:appcompat:1.3.1', 12 | 'constraintlayout': 'androidx.constraintlayout:constraintlayout:2.1.1', 13 | 'androidxCore' : 'androidx.core:core-ktx:1.7.0', 14 | 'kotlinJdk7' : 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21', 15 | 'glide' : 'com.github.bumptech.glide:glide:4.12.0', 16 | 'recyclerview' : 'androidx.recyclerview:recyclerview:1.1.0', 17 | 'cardview' : 'androidx.cardview:cardview:1.0.0', 18 | ] 19 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /example/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: './../config.gradle' 2 | apply plugin: 'com.android.application' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-android-extensions' 5 | 6 | android { 7 | buildToolsVersion buidConfig.buildToolsVersion 8 | compileSdkVersion buidConfig.compileSdkVersion 9 | 10 | defaultConfig { 11 | applicationId "com.iwdael.carouselbanner.example" 12 | minSdkVersion buidConfig.minSdkVersion 13 | targetSdkVersion buidConfig.targetSdkVersion 14 | versionCode buidConfig.versionCode 15 | versionName buidConfig.versionName 16 | } 17 | compileOptions { 18 | sourceCompatibility 1.8 19 | targetCompatibility 1.8 20 | } 21 | 22 | } 23 | 24 | dependencies { 25 | implementation dependent.kotlinJdk7 26 | implementation dependent.appcompat 27 | implementation dependent.constraintlayout 28 | implementation dependent.glide 29 | implementation project(":carouselbanner") 30 | implementation dependent.recyclerview 31 | implementation dependent.cardview 32 | } -------------------------------------------------------------------------------- /example/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /example/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /example/src/main/java/com/iwdael/carouselbanner/example/ImageFactory.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.example; 2 | 3 | import android.widget.ImageView; 4 | 5 | import com.bumptech.glide.Glide; 6 | import com.iwdael.carouselbanner.interfaces.CarouselImageFactory; 7 | 8 | public class ImageFactory implements CarouselImageFactory { 9 | @Override 10 | public void onLoadFactory(String url, ImageView view) { 11 | Glide.with(view).load(url).into(view); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/src/main/java/com/iwdael/carouselbanner/example/Main2Activity.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.example; 2 | 3 | import android.os.Bundle; 4 | import android.widget.Toast; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import com.iwdael.carouselbanner.CarouselBanner; 9 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemChangeListener; 10 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class Main2Activity extends AppCompatActivity { 16 | CarouselBanner banner, banner2; 17 | 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main2); 23 | banner = findViewById(R.id.banner); 24 | banner2 = findViewById(R.id.banner2); 25 | List list = new ArrayList<>(); 26 | list.add("http://g.hiphotos.baidu.com/image/pic/item/b17eca8065380cd78775def0ab44ad3459828147.jpg"); 27 | list.add("http://f.hiphotos.baidu.com/image/pic/item/a08b87d6277f9e2faa2048151530e924b899f392.jpg"); 28 | list.add("http://b.hiphotos.baidu.com/image/pic/item/03087bf40ad162d923621d011bdfa9ec8a13cd1b.jpg"); 29 | list.add("http://e.hiphotos.baidu.com/image/pic/item/b7fd5266d0160924d76acf06de0735fae6cd345b.jpg"); 30 | list.add("http://a.hiphotos.baidu.com/image/pic/item/c83d70cf3bc79f3d785ce62db0a1cd11728b2969.jpg"); 31 | list.add("http://f.hiphotos.baidu.com/image/pic/item/fcfaaf51f3deb48fd146bfc3fa1f3a292df578fb.jpg"); 32 | 33 | banner.setOnCarouselItemClickListener(new OnCarouselItemClickListener() { 34 | @Override 35 | public void onItemClick(int position, String url) { 36 | Toast.makeText(Main2Activity.this, url, Toast.LENGTH_LONG).show(); 37 | } 38 | }); 39 | banner.setOnCarouselItemChangeListener(new OnCarouselItemChangeListener() { 40 | @Override 41 | public void onItemChange(int position) { 42 | // Toast.makeText(Main2Activity.this, String.valueOf(position), Toast.LENGTH_LONG).show(); 43 | } 44 | }); 45 | banner.initBanner(list); 46 | banner2.initBanner(list); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /example/src/main/java/com/iwdael/carouselbanner/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.iwdael.carouselbanner.example; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import com.iwdael.carouselbanner.Banner; 11 | import com.iwdael.carouselbanner.CoolCarouselBanner; 12 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemChangeListener; 13 | import com.iwdael.carouselbanner.interfaces.OnCarouselItemClickListener; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | CoolCarouselBanner banner, banner2; 21 | 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | banner = findViewById(R.id.banner); 28 | banner2 = findViewById(R.id.banner2); 29 | List list = new ArrayList<>(); 30 | Banner.init(new ImageFactory()); 31 | list.add("http://g.hiphotos.baidu.com/image/pic/item/b17eca8065380cd78775def0ab44ad3459828147.jpg"); 32 | list.add("http://f.hiphotos.baidu.com/image/pic/item/a08b87d6277f9e2faa2048151530e924b899f392.jpg"); 33 | list.add("http://b.hiphotos.baidu.com/image/pic/item/03087bf40ad162d923621d011bdfa9ec8a13cd1b.jpg"); 34 | list.add("http://e.hiphotos.baidu.com/image/pic/item/b7fd5266d0160924d76acf06de0735fae6cd345b.jpg"); 35 | list.add("http://a.hiphotos.baidu.com/image/pic/item/c83d70cf3bc79f3d785ce62db0a1cd11728b2969.jpg"); 36 | list.add("http://f.hiphotos.baidu.com/image/pic/item/fcfaaf51f3deb48fd146bfc3fa1f3a292df578fb.jpg"); 37 | 38 | banner.setOnCarouselItemChangeListener(new OnCarouselItemChangeListener() { 39 | @Override 40 | public void onItemChange(int position) { 41 | // Toast.makeText(MainActivity.this, String.valueOf(position), Toast.LENGTH_LONG).show(); 42 | } 43 | }); 44 | banner.setOnCarouselItemClickListener(new OnCarouselItemClickListener() { 45 | @Override 46 | public void onItemClick(int position, String url) { 47 | Toast.makeText(MainActivity.this, url, Toast.LENGTH_LONG).show(); 48 | } 49 | }); 50 | banner.initBanner(list); 51 | banner2.initBanner(list); 52 | 53 | } 54 | 55 | 56 | public void jump(View view) { 57 | view.setVisibility(View.GONE); 58 | startActivity(new Intent(MainActivity.this, Main2Activity.class)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 19 | 20 | 21 | 27 | 28 |