Android 輪播圖從 0 到 1

從 0 到 1
輪播圖是 Android 常用功能之一,效果大概是這樣的:

漏洞百出
之前我封裝寫了一個(gè),基本達(dá)到了要求,是繼承了 Fragment(當(dāng)時(shí)腦袋肯定銹掉了),里面 Viewpager add Fragment,這次項(xiàng)目多處有輪播圖,發(fā)現(xiàn)之前封裝的不夠用,簡(jiǎn)直漏洞百出:
1、比如底部 point 的位置,之前固定在中間,現(xiàn)在可能要放在右下角,point 最好也能動(dòng)態(tài)改圖片;
2、現(xiàn)在項(xiàng)目跟微信一樣,底部 tab 切換,中間是 Fragment 替換,發(fā)現(xiàn)輪播圖有問(wèn)題,F(xiàn)ragment A 循環(huán)的 point 的 positoin 居然影響到了 Fragment B,照理,這是兩個(gè) BannerFragment,不會(huì)影響的啊,報(bào)以下錯(cuò)誤:
java.lang.IllegalStateException:?The?application's?PagerAdapter?changed?the?adapter's?contents?without?calling?PagerAdapter#notifyDataSetChanged!
經(jīng)過(guò)排查,找到了原因,因?yàn)?Viewpager add Fragment 我全部放在一個(gè)類,因此:
public?static?List這里 static 壞事了,之前一個(gè) banner 沒(méi)有暴露出來(lái)。
3、繼承了 Fragment,引用比較麻煩,F(xiàn)ragment 有兩者引用方法,xml 和代碼,兩者方式 addData 卻報(bào)錯(cuò);
4、banner 沒(méi)有寫點(diǎn)擊回調(diào)。
再次封裝
綜合以上問(wèn)題,我進(jìn)行了優(yōu)化,繼承 LinearLayout,當(dāng)一個(gè)控件來(lái)引用,省去不必要的麻煩,底部 point 的位置可以設(shè)置:
pointLayout.setGravity(bannerPointGravity);
另外自定義了屬性,動(dòng)態(tài)設(shè)置 point 大小和圖片,輪播圖循環(huán)時(shí)間,也能代碼設(shè)置,完整代碼示例:
/**
?*?Created?by?WuXiaolong?on?2017/8/24.
?*?個(gè)人博客:http://wuxiaolong.me
?*/
public?class?BannerLayout?extends?LinearLayout?{
????private?ViewPager?viewPager;
????private?LinearLayout?pointLayout;
????private?ScheduledExecutorService?scheduler;
????private?int?mPosition?=?0;
????private?int?mBannerCount?=?1;
????private?Context?context;
????private?Activity?activity;
????private?int?bannerPointSize;
????private?int?bannerPointGravity;
????private?int?bannerPointDrawableSelected,?bannerPointDrawableUnselected;
????private?int?bannerDelaySecond;
????public?BannerLayout(Context?context)?{
????????this(context,?null);
????}
????public?BannerLayout(Context?context,?@Nullable?AttributeSet?attrs)?{
????????this(context,?attrs,?0);
????}
????public?BannerLayout(Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{
????????super(context,?attrs,?defStyleAttr);
????????initView(context,?attrs);
????}
????private?void?initView(Context?context,?AttributeSet?attrs)?{
????????this.context?=?context;
????????activity?=?(Activity)?context;
????????TypedArray?typedArray?=?context.obtainStyledAttributes(attrs,?R.styleable.BannerLayout);
????????bannerPointSize?=?typedArray.getDimensionPixelSize(R.styleable.BannerLayout_bannerPointSize,?10);
????????bannerPointGravity?=?typedArray.getInt(R.styleable.BannerLayout_bannerPointGravity,?Gravity.CENTER);
????????bannerDelaySecond?=?typedArray.getInt(R.styleable.BannerLayout_bannerDelaySecond,?5);
????????bannerPointDrawableSelected?=?typedArray.getResourceId(R.styleable.BannerLayout_bannerPointDrawableSelected,?R.mipmap.point01);
????????bannerPointDrawableUnselected?=?typedArray.getResourceId(R.styleable.BannerLayout_bannerPointDrawableUnselected,?R.mipmap.point02);
????????typedArray.recycle();
????????View?view?=?View.inflate(context,?R.layout.banner_view_pager,?null);
????????addView(view);
????????viewPager?=?(ViewPager)?view.findViewById(R.id.viewPager);
????????pointLayout?=?(LinearLayout)?view.findViewById(R.id.pointLayout);
????????pointLayout.setGravity(bannerPointGravity);
????????viewPager.addOnPageChangeListener(new?ViewPager.OnPageChangeListener()?{
????????????@Override
????????????public?void?onPageScrolled(int?position,?float?positionOffset,?int?positionOffsetPixels)?{
????????????}
????????????@Override
????????????public?void?onPageSelected(int?position)?{
????????????????addPointLayout(position);
????????????}
????????????@Override
????????????public?void?onPageScrollStateChanged(int?state)?{
????????????}
????????});
????}
????public?void?start(List?{
????????bannerShutdown();
????????mBannerCount?=?bannerList.size();
????????BannerPagerAdapter?bannerPagerAdapter?=?new?BannerPagerAdapter(context,?bannerList);
????????viewPager.setAdapter(bannerPagerAdapter);
????????addPointLayout(0);
????????startScheduler();
????}
????private?void?addPointLayout(int?position)?{
????????pointLayout.removeAllViews();
????????for?(int?i?=?0;?i?????????????ImageView?imageView?=?new?ImageView(context);
????????????LinearLayout.LayoutParams?layoutParams?=?new?LinearLayout.LayoutParams(bannerPointSize,?bannerPointSize);
????????????layoutParams.setMargins(10,?0,?0,?0);
????????????imageView.setLayoutParams(layoutParams);
????????????if?(position?==?i)?{
????????????????imageView.setImageResource(bannerPointDrawableSelected);
????????????}?else?{
????????????????imageView.setImageResource(bannerPointDrawableUnselected);
????????????}
????????????pointLayout.addView(imageView);
????????}
????}
????private?void?startScheduler()?{
????????scheduler?=?Executors.newSingleThreadScheduledExecutor();
????????scheduler.scheduleAtFixedRate(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????mPosition?=?viewPager.getCurrentItem();
????????????????if?(mPosition?1)?{
????????????????????mPosition++;
????????????????}?else?{
????????????????????mPosition?=?0;
????????????????}
????????????????activity.runOnUiThread(new?Runnable()?{
????????????????????@Override
????????????????????public?void?run()?{
????????????????????????viewPager.setCurrentItem(mPosition);
????????????????????}
????????????????});
????????????}
????????},?1,?bannerDelaySecond,?TimeUnit.SECONDS);
????}
????public?void?bannerShutdown()?{
????????if?(scheduler?!=?null)
????????????scheduler.shutdown();
????}
????private?class?BannerPagerAdapter?extends?PagerAdapter?{
????????private?List其中自定義屬性的 attrs.xml:
<resources>
????<declare-styleable?name="BannerLayout">
????????
????????<attr?name="bannerPointSize"?format="dimension"?/>
????????
????????<attr?name="bannerPointGravity"?format="enum">
????????????<enum?name="left"?value="3"?/>
????????????<enum?name="center"?value="17"?/>
????????????<enum?name="right"?value="5"?/>
????????attr>
????????
????????<attr?name="bannerPointDrawableSelected"?format="reference"?/>
????????
????????<attr?name="bannerPointDrawableUnselected"?format="reference"?/>
????????
????????<attr?name="bannerDelaySecond"?format="integer"?/>
????declare-styleable>
resources>
使用說(shuō)明xml
<com.wuxiaolong.bannersample.BannerLayout
????android:id="@+id/bannerView"
????android:layout_width="match_parent"
????android:layout_height="198dp"
????app:bannerDelaySecond="3"
????app:bannerPointDrawableSelected="@drawable/gray_radius"
????app:bannerPointDrawableUnselected="@drawable/white_radius"
????app:bannerPointGravity="right"
????app:bannerPointSize="10dp"?/>
調(diào)用:
public?class?MainActivity?extends?AppCompatActivity?{
????private?BannerLayout?bannerView;
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
????????bannerView?=?(BannerLayout)?findViewById(R.id.bannerView);
????????List?bannerList?=?new?ArrayList<>();
????????bannerList.add(R.mipmap.horizontal_default);
????????bannerList.add("http://pic1.win4000.com/wallpaper/5/598161750eddb.jpg");
????????bannerList.add("http://pic1.win4000.com/wallpaper/4/597efb5b6aae8.jpg");
????????bannerView.setBannerPointSize(10);
????????bannerView.setBannerPointGravity(Gravity.CENTER);
????????bannerView.setBannerPointDrawableSelected(R.drawable.gray_radius);
????????bannerView.setBannerPointDrawableUnselected(R.mipmap.point01);
????????bannerView.setBannerDelaySecond(5);
????????//banner?設(shè)置方法完畢時(shí)最后調(diào)用?start?方法
????????bannerView.start(bannerList);
????}
????@Override
????protected?void?onStop()?{
????????super.onStop();
????????bannerView.bannerShutdown();
????}
}
如果以上還滿足不了你的需求,可以使用 GitHub 上的輪子。
現(xiàn)有輪子
這個(gè)庫(kù)使用了 ViewPager2 為基礎(chǔ)控件,支持了 androidx 兼容包,方便了 UI、Indicator 自定義,支持畫廊效果、魅族效果,兼容了水平和垂直輪播,也可以實(shí)現(xiàn)類似淘寶頭條的效果,依賴包目前只需要導(dǎo)入了 ViewPager2。




還內(nèi)置了多種 PageTransformer 效果

如何下載源碼?在公眾號(hào) 「Android 指南」 后臺(tái)回復(fù) banner 獲取源碼。
