datas) {
578 | mDatas = datas;
579 | }
580 |
581 | @Override
582 | public int getCount() {
583 | // 2017.6.10 bug fix
584 | // 如果getCount 的返回值为Integer.MAX_VALUE 的话,那么在setCurrentItem的时候会ANR(除了在onCreate 调用之外)
585 | return canLoop ? getRealCount() * mLooperCountFactor : getRealCount();//ViewPager返回int 最大值
586 | }
587 |
588 | @Override
589 | public boolean isViewFromObject(View view, Object object) {
590 | return view == object;
591 | }
592 |
593 | @Override
594 | public Object instantiateItem(ViewGroup container, final int position) {
595 | View view = getView(position,container);
596 | container.addView(view);
597 | return view;
598 | }
599 |
600 | @Override
601 | public void destroyItem(ViewGroup container, int position, Object object) {
602 | container.removeView((View) object);
603 | }
604 |
605 | @Override
606 | public void finishUpdate(ViewGroup container) {
607 | // 轮播模式才执行
608 | if(canLoop){
609 | int position = mViewPager.getCurrentItem();
610 | if (position == getCount() - 1) {
611 | position = 0;
612 | setCurrentItem(position);
613 | }
614 | }
615 |
616 | }
617 |
618 | private void setCurrentItem(int position){
619 | try {
620 | mViewPager.setCurrentItem(position, false);
621 | }catch (IllegalStateException e){
622 | e.printStackTrace();
623 | }
624 | }
625 |
626 | /**
627 | * 获取真实的Count
628 | * @return
629 | */
630 | private int getRealCount(){
631 | return mDatas==null ? 0:mDatas.size();
632 | }
633 |
634 | /**
635 | *
636 | * @param position
637 | * @param container
638 | * @return
639 | */
640 | private View getView(int position,ViewGroup container){
641 |
642 | final int realPosition = position % getRealCount();
643 | MZViewHolder holder =null;
644 | // create holder
645 | holder = mMZHolderCreator.createViewHolder();
646 |
647 | if(holder == null){
648 | throw new RuntimeException("can not return a null holder");
649 | }
650 | // create View
651 | View view = holder.createView(container.getContext());
652 |
653 | if( mDatas!=null && mDatas.size()>0){
654 | holder.onBind(container.getContext(),realPosition,mDatas.get(realPosition));
655 | }
656 |
657 | // 添加点击事件
658 | view.setOnClickListener(new OnClickListener() {
659 | @Override
660 | public void onClick(View v) {
661 | if(mPageClickListener!=null){
662 | mPageClickListener.onPageClick(v,realPosition);
663 | }
664 | }
665 | });
666 |
667 | return view;
668 | }
669 |
670 |
671 | }
672 |
673 | /**
674 | *
675 | *由于ViewPager 默认的切换速度有点快,因此用一个Scroller 来控制切换的速度
676 | * 而实际上ViewPager 切换本来就是用的Scroller来做的,因此我们可以通过反射来
677 | * 获取取到ViewPager 的 mScroller 属性,然后替换成我们自己的Scroller
678 | */
679 | public static class ViewPagerScroller extends Scroller{
680 | private int mDuration = 800;// ViewPager默认的最大Duration 为600,我们默认稍微大一点。值越大越慢。
681 | private boolean mIsUseDefaultDuration = false;
682 |
683 | public ViewPagerScroller(Context context) {
684 | super(context);
685 | }
686 |
687 | public ViewPagerScroller(Context context, Interpolator interpolator) {
688 | super(context, interpolator);
689 | }
690 |
691 | public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
692 | super(context, interpolator, flywheel);
693 | }
694 |
695 | @Override
696 | public void startScroll(int startX, int startY, int dx, int dy) {
697 | super.startScroll(startX, startY, dx, dy,mDuration);
698 | }
699 |
700 | @Override
701 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
702 | super.startScroll(startX, startY, dx, dy, mIsUseDefaultDuration?duration:mDuration);
703 | }
704 |
705 | public void setUseDefaultDuration(boolean useDefaultDuration) {
706 | mIsUseDefaultDuration = useDefaultDuration;
707 | }
708 |
709 | public boolean isUseDefaultDuration() {
710 | return mIsUseDefaultDuration;
711 | }
712 |
713 | public void setDuration(int duration) {
714 | mDuration = duration;
715 | }
716 |
717 |
718 | public int getScrollDuration() {
719 | return mDuration;
720 | }
721 | }
722 |
723 | /**
724 | * Banner page 点击回调
725 | */
726 | public interface BannerPageClickListener{
727 | void onPageClick(View view, int position);
728 | }
729 |
730 | public static int dpToPx(int dp) {
731 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
732 | }
733 |
734 | }
735 |
--------------------------------------------------------------------------------
/mzbanner/src/main/java/com/zhouwei/mzbanner/holder/MZHolderCreator.java:
--------------------------------------------------------------------------------
1 | package com.zhouwei.mzbanner.holder;
2 |
3 | /**
4 | * Created by zhouwei on 17/5/26.
5 | */
6 |
7 | public interface MZHolderCreator {
8 | /**
9 | * 创建ViewHolder
10 | * @return
11 | */
12 | public VH createViewHolder();
13 | }
14 |
--------------------------------------------------------------------------------
/mzbanner/src/main/java/com/zhouwei/mzbanner/holder/MZViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.zhouwei.mzbanner.holder;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by zhouwei on 17/5/26.
8 | */
9 |
10 | public interface MZViewHolder {
11 | /**
12 | * 创建View
13 | * @param context
14 | * @return
15 | */
16 | View createView(Context context);
17 |
18 | /**
19 | * 绑定数据
20 | * @param context
21 | * @param position
22 | * @param data
23 | */
24 | void onBind(Context context, int position, T data);
25 | }
26 |
--------------------------------------------------------------------------------
/mzbanner/src/main/java/com/zhouwei/mzbanner/transformer/CoverModeTransformer.java:
--------------------------------------------------------------------------------
1 | package com.zhouwei.mzbanner.transformer;
2 |
3 | import android.support.v4.view.ViewPager;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by zhouwei on 17/8/20.
8 | */
9 |
10 | public class CoverModeTransformer implements ViewPager.PageTransformer {
11 |
12 | private float reduceX = 0.0f;
13 | private float itemWidth = 0;
14 | private float offsetPosition = 0f;
15 | private int mCoverWidth;
16 | private float mScaleMax = 1.0f;
17 | private float mScaleMin = 0.9f;
18 | private ViewPager mViewPager;
19 | public CoverModeTransformer(ViewPager pager){
20 | mViewPager = pager;
21 | }
22 |
23 | @Override
24 | public void transformPage(View view, float position) {
25 | if (offsetPosition == 0f) {
26 | float paddingLeft = mViewPager.getPaddingLeft();
27 | float paddingRight = mViewPager.getPaddingRight();
28 | float width = mViewPager.getMeasuredWidth();
29 | offsetPosition = paddingLeft / (width - paddingLeft - paddingRight);
30 | }
31 | float currentPos = position - offsetPosition;
32 | if (itemWidth == 0) {
33 | itemWidth = view.getWidth();
34 | //由于左右边的缩小而减小的x的大小的一半
35 | reduceX = (2.0f - mScaleMax - mScaleMin) * itemWidth / 2.0f;
36 | }
37 | if (currentPos <= -1.0f) {
38 | view.setTranslationX(reduceX + mCoverWidth);
39 | view.setScaleX(mScaleMin);
40 | view.setScaleY(mScaleMin);
41 | } else if (currentPos <= 1.0) {
42 | float scale = (mScaleMax - mScaleMin) * Math.abs(1.0f - Math.abs(currentPos));
43 | float translationX = currentPos * -reduceX;
44 | if (currentPos <= -0.5) {//两个view中间的临界,这时两个view在同一层,左侧View需要往X轴正方向移动覆盖的值()
45 | view.setTranslationX(translationX + mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
46 | } else if (currentPos <= 0.0f) {
47 | view.setTranslationX(translationX);
48 | } else if (currentPos >= 0.5) {//两个view中间的临界,这时两个view在同一层
49 | view.setTranslationX(translationX - mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
50 | } else {
51 | view.setTranslationX(translationX);
52 | }
53 | view.setScaleX(scale + mScaleMin);
54 | view.setScaleY(scale + mScaleMin);
55 | } else {
56 | view.setScaleX(mScaleMin);
57 | view.setScaleY(mScaleMin);
58 | view.setTranslationX(-reduceX - mCoverWidth);
59 | }
60 |
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/mzbanner/src/main/java/com/zhouwei/mzbanner/transformer/ScaleYTransformer.java:
--------------------------------------------------------------------------------
1 | package com.zhouwei.mzbanner.transformer;
2 |
3 | import android.support.v4.view.ViewPager;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by zhouwei on 17/5/26.
8 | */
9 |
10 | public class ScaleYTransformer implements ViewPager.PageTransformer {
11 | private static final float MIN_SCALE = 0.9F;
12 | @Override
13 | public void transformPage(View page, float position) {
14 |
15 | if(position < -1){
16 | page.setScaleY(MIN_SCALE);
17 | }else if(position<= 1){
18 | //
19 | float scale = Math.max(MIN_SCALE,1 - Math.abs(position));
20 | page.setScaleY(scale);
21 | /*page.setScaleX(scale);
22 |
23 | if(position<0){
24 | page.setTranslationX(width * (1 - scale) /2);
25 | }else{
26 | page.setTranslationX(-width * (1 - scale) /2);
27 | }*/
28 |
29 | }else{
30 | page.setScaleY(MIN_SCALE);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/drawable/indicator_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/drawable/indicator_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/layout/mz_banner_effect_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
16 |
17 |
23 |
30 |
31 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/layout/mz_banner_normal_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
12 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/mzbanner/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MZBanner
3 |
4 |
--------------------------------------------------------------------------------
/mzbanner/src/test/java/com/zhouwei/mzbanner/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.zhouwei.mzbanner;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ### MZBannerView
2 |
3 | [](https://github.com/pinguo-zhouwei/MZBannerView/releases)
4 | [](https://github.com/pinguo-zhouwei/MZBannerView)
5 | [](https://github.com/pinguo-zhouwei/MZBannerView)
6 | [](https://github.com/pinguo-zhouwei/MZBannerView)
7 | []()
8 | [](https://www.jianshu.com/u/35167a70aa39)
9 | [](https://mp.weixin.qq.com/mp/homepage?__biz=MzIxMTg5NjQyMA==&hid=1&sn=6914e8d560a0d524f937af629488b6b5&scene=1&devicetype=iMac+MacBookPro12%2C1+OSX+OSX+10.11.5+build(15F34)&version=12020810&lang=zh_CN&nettype=WIFI&ascene=0&session_us=gh_d32aef65c1b3&fontScale=100&pass_ticket=Wty8DxZurpU2xxUkFPtXL5oqMrigpoVVh1agsDKXukmezsMUUJaWb%2FdIlcrhpQOq&wx_header=1&uin=OTU2MjA1MzIy&key=ebe51358ecae289391e767e9274e1a2bf8576a9e4fd5bc2b31956a627346413f84f752a9dd302e2a157b6fb84991a4142881a8143d21cdd25b805b8ba327e3ba78443337a68c9a1b32095d9adc30ec4b)
10 |
11 | 现在的APP Banner大多数千篇一律,前几天看到魅族手机上所有魅族自家APP上的Banner效果不错,于是就想着来仿着做一个类似的效果。因此就有了这个库。但是为了使用方便,这个库不仅仅只有仿魅族效果的BannerView 来使用,还可以当作普通的BannerView 来使用,还可以当作一个ViewPager 来使用。使用很方便,具体使用方法和API 请看后面的示例。
12 |
13 |
14 |  --- 
15 | 左图为魅族APP上的Banner效果,右图是高仿效果。
16 |
17 |
18 |
19 | **MZBannerView 有以下功能:**
20 |
21 | 1 . 仿魅族BannerView 效果。
22 |
23 | 2 . 当普通Banner 使用
24 |
25 | 3 . 当普通ViewPager 使用。
26 |
27 | 4 . 当普通ViewPager使用(有魅族Banner效果)
28 |
29 | 5 . 仿爱奇艺Banner效果。
30 |
31 | ### Demo APK
32 |
33 | gif图片有点模糊,可以扫描下方二维码下载APK体验
34 |
35 | 
36 |
37 | ### 相关博客
38 |
39 | [ViewPager系列之 仿魅族应用的广告BannerView](http://www.jianshu.com/p/653680cfe877)
40 |
41 | ### 更新日志
42 |
43 | **v1.1.1 :** 增加按住Banner 停止轮播,松开开始自动轮播的功能
44 |
45 | **v1.1.0 :** fix 在从网上获取数据后,banner 显示 造成 ANR 的bug(如果在onCreate()中设置资源显示则没问题)
46 |
47 | **v1.1.2 :** fix 更改数据之后,调用setPages重新刷新数据会crush的bug
48 |
49 | **v2.0.0 :**
50 |
51 | 1,add: 添加仿魅族Banner效果,中间Page覆盖两边。
52 |
53 | 2,fix 部分bug: 添加OnPageChangeListener 回调 pisition 不对的bug.
54 |
55 | **v2.0.1**
56 |
57 | 1 , 部分代码优化
58 | 2,添加设置Indicator的api,代码中和xml中都可以设置:
59 | 代码中:
60 | ```java
61 | mMZBanner.setIndicatorAlign(MZBannerView.IndicatorAlign.LEFT);
62 | mMZBanner.setIndicatorPadding(10,0,0,150);
63 |
64 | ```
65 | xml中:
66 | ```java
67 | app:indicatorAlign="center"
68 | app:indicatorPaddingLeft="10dp"
69 | app:indicatorPaddingBottom="50dp"
70 | app:indicatorPaddingRight="10dp"
71 | app:indicatorPaddingTop="50dp"
72 | ```
73 | **v2.0.2**
74 |
75 | 1,修复部分bug
76 | 2,代码优化,增加在代码中可以设置是否自动轮播
77 |
78 |
79 | >重要: 代码中所有的配置项都应该在`setPages()` 之前调用,不然有可能会无效
80 |
81 |
82 | ### Dependency
83 | Add it in your root build.gradle at the end of repositories:
84 |
85 | ```java
86 | allprojects {
87 | repositories {
88 | ...
89 | maven { url 'https://jitpack.io' }
90 | }
91 | }
92 | ```
93 | Step 2. Add the dependency
94 | ```
95 | dependencies {
96 | compile 'com.github.pinguo-zhouwei:MZBannerView:v2.0.2'
97 | }
98 | ```
99 |
100 | ### 自定义属性
101 |
102 | | 属性名 | 属性意义 | 取值 |
103 | | -------- | -----: | :----: |
104 | | open_mz_mode | 是否开启魅族模式 | true 为魅族Banner效果,false 则普通Banner效果 |
105 | | canLoop | 是否轮播 | true 轮播,false 则为普通ViewPager |
106 | | indicatorPaddingLeft | 设置指示器距离左侧的距离 | 单位为 dp 的值 |
107 | | indicatorPaddingRight | 设置指示器距离右侧的距离 | 单位为 dp 的值 |
108 | | indicatorAlign | 设置指示器的位置 | 有三个取值:left 左边,center 剧中显示,right 右侧显示 |
109 | | middle_page_cover | 设置中间Page是否覆盖(真正的魅族Banner效果) | true 覆盖,false 无覆盖效果 |
110 |
111 |
112 | ### 使用方法
113 |
114 | 1 . xml 布局文件
115 | ```java
116 |
126 | ```
127 | 2 . activity中代码:
128 | ```java
129 | mMZBanner = (MZBannerView) view.findViewById(R.id.banner);
130 |
131 | // 设置数据
132 | mMZBanner.setPages(list, new MZHolderCreator() {
133 | @Override
134 | public BannerViewHolder createViewHolder() {
135 | return new BannerViewHolder();
136 | }
137 | });
138 |
139 | public static class BannerViewHolder implements MZViewHolder {
140 | private ImageView mImageView;
141 | @Override
142 | public View createView(Context context) {
143 | // 返回页面布局
144 | View view = LayoutInflater.from(context).inflate(R.layout.banner_item,null);
145 | mImageView = (ImageView) view.findViewById(R.id.banner_image);
146 | return view;
147 | }
148 |
149 | @Override
150 | public void onBind(Context context, int position, Integer data) {
151 | // 数据绑定
152 | mImageView.setImageResource(data);
153 | }
154 | }
155 | ```
156 | 3 .如果是当Banner使用,注意在onResume 中调用start()方法,在onPause中调用 pause() 方法。如果当普通ViewPager使用,则不需要。
157 | ```java
158 | @Override
159 | public void onPause() {
160 | super.onPause();
161 | mMZBanner.pause();//暂停轮播
162 | }
163 |
164 | @Override
165 | public void onResume() {
166 | super.onResume();
167 | mMZBanner.start();//开始轮播
168 | }
169 | ```
170 |
171 | **通过`open_mz_mode `、`middle_page_cover`和`canLoop `这3个属性来控制MZBannerView 是用作Banner还是普通ViewPager,能控制多种Banner展示效果:**
172 |
173 | 1 . 魅族Banner 效果,中间Page覆盖两边。
174 |
175 | ```java
176 | app:open_mz_mode="true"
177 | app:canLoop="true"
178 | app:middle_page_cover="true"
179 | ```
180 |
181 | 
182 |
183 | 2 普通banner 使用。
184 |
185 | ```java
186 | app:open_mz_mode="false"
187 | app:canLoop="true"
188 | ```
189 | 
190 |
191 | 上图中的底部BannerView 示例。
192 |
193 | 3 仿魅族Banner 效果,中间Page不覆盖。
194 |
195 | ```java
196 | app:open_mz_mode="true"
197 | app:canLoop="true"
198 | app:middle_page_cover="false"
199 | ```
200 |
201 | 
202 |
203 | 4 仿爱奇艺Banner效果,Page 之间有间隔。
204 |
205 | ```java
206 |
216 | ```
217 |
218 | 除了上面的代码外,还需要在Page 的item 布局里面设置左右Margin:
219 |
220 | ```java
221 |
222 |
226 |
234 |
235 | ```
236 | 效果如下:
237 |
238 | 
239 |
240 | 5 有魅族Banner 效果的普通ViewPager 使用
241 |
242 |
243 | ```java
244 | app:open_mz_mode="true"
245 | app:canLoop="false"
246 | ```
247 |
248 | 
249 |
250 | 6 普通ViewPager 使用
251 |
252 | ```java
253 | app:canLoop="false"
254 | app:open_mz_mode="false"
255 | ```
256 |
257 | 
258 |
259 | 上面都是用Banner 展示的本地数据,但是项目中我们一般都是从网络获取Banner 数据,具体参考:`RemoteTestFragment.java`
260 |
261 | 
262 |
263 |
264 |
265 |
266 | ### 其他对外API
267 | ```java
268 | /******************************************************************************************************/
269 | /** 对外API **/
270 | /******************************************************************************************************/
271 | //开始轮播
272 | start()
273 | //停止轮播
274 | pause()
275 |
276 | //设置BannerView 的切换时间间隔
277 | setDelayedTime(int delayedTime)
278 | // 设置页面改变监听器
279 | addPageChangeLisnter(ViewPager.OnPageChangeListener onPageChangeListener)
280 |
281 | //添加Page点击事件
282 | setBannerPageClickListener(BannerPageClickListener bannerPageClickListener)
283 | //设置是否显示Indicator
284 | setIndicatorVisible(boolean visible)
285 | // 获取ViewPager
286 | ViewPager getViewPager()
287 | // 设置 Indicator资源
288 | setIndicatorRes(int unSelectRes,int selectRes)
289 | //设置页面数据
290 | setPages(List datas,MZHolderCreator mzHolderCreator)
291 | //设置指示器显示位置
292 | setIndicatorAlign(IndicatorAlign indicatorAlign)
293 | //设置ViewPager(Banner)切换速度
294 | setDuration(int duration)
295 | ```
296 | 因为是对ViewPager的包装,所有要设置某些ViewPager的属性,可以通过getViewPager 获取到ViewPager再设置对应属性
297 |
298 | ### 效果图:
299 |
300 | 1, BannerView 轮播效果图:
301 |
302 | 
303 |
304 |
305 | ### Thanks
306 | 感谢[Android-ConvenientBanner](https://github.com/saiwu-bigkoo/Android-ConvenientBanner),Android-ConvenientBanner 是一个不错的Banner库,我也参考了其中的部分代码
307 | 有兴趣的可以去看一下这个库。
308 | 感谢[ScaleViewPager](https://github.com/liuyuejinqiu/ScaleViewPager) 提供中间Page覆盖效果的思路。
309 |
310 | ### 联系方式
311 | 简书:[http://www.jianshu.com/u/35167a70aa39](http://www.jianshu.com/u/35167a70aa39)
312 |
313 | 掘金:[https://juejin.im/user/56949a9960b2e058a42be0ba](https://juejin.im/user/56949a9960b2e058a42be0ba)
314 |
315 | 公众号:**Android技术杂货铺**
316 |
317 | 欢迎关注我的公众号,第一时间获取我的博客更新提醒,以及更多有价值的原创Android干货文章、职场经验、面试技巧等等。
318 | 长按下方二维码即可关注。
319 |
320 | 
321 |
322 |
323 | ### License
324 |
325 | ```
326 | Copyright (C) 2017 zhouwei
327 |
328 | Licensed under the Apache License, Version 2.0 (the "License");
329 | you may not use this file except in compliance with the License.
330 | You may obtain a copy of the License at
331 |
332 | http://www.apache.org/licenses/LICENSE-2.0
333 |
334 | Unless required by applicable law or agreed to in writing, software
335 | distributed under the License is distributed on an "AS IS" BASIS,
336 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
337 | See the License for the specific language governing permissions and
338 | limitations under the License.
339 | ```
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':mzbanner'
2 |
--------------------------------------------------------------------------------