作者:吴小龙同學

从 0 到 1

轮播图是 Android 常用功能之一,效果大概是这样的:

android中轮播图怎么完成(Android常用功能轮播图从)(1)

漏洞百出

之前我封装写了一个,基本达到了要求,是继承了 Fragment(当时脑袋肯定锈掉了),里面 Viewpager add Fragment,这次项目多处有轮播图,发现之前封装的不够用,简直漏洞百出:

1、比如底部 point 的位置,之前固定在中间,现在可能要放在右下角,point 最好也能动态改图片;

2、现在项目跟微信一样,底部 tab 切换,中间是 Fragment 替换,发现轮播图有问题,Fragment A 循环的 point 的 positoin 居然影响到了 Fragment B,照理,这是两个 BannerFragment,不会影响的啊,报以下错误:

java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged!

经过排查,找到了原因,因为 Viewpager add Fragment 我全部放在一个类,因此:

public static List<Object> bannerList = new ArrayList<>();

这里 static 坏事了,之前一个 banner 没有暴露出来。

3、继承了 Fragment,引用比较麻烦,Fragment 有两者引用方法,xml 和代码,两者方式 addData 却报错;

4、banner 没有写点击回调。

再次封装

综合以上问题,我进行了优化,继承 LinearLayout,当一个控件来引用,省去不必要的麻烦,底部 point 的位置可以设置:

pointLayout.setGravity(bannerPointGravity);

另外自定义了属性,动态设置 point 大小和图片,轮播图循环时间,也能代码设置,完整代码示例:

/** * Created by WuXiaolong on 2017/8/24. * 个人博客: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<Object> bannerList) { 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 < mBannerCount; 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 < mBannerCount - 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<Object> bannerList = new ArrayList<>(); private Context context; BannerPagerAdapter(Context context, List<Object> bannerList) { this.context = context; this.bannerList.clear(); this.bannerList.addAll(bannerList); } @Override public int getCount() { return bannerList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, final int position) { ImageView imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); Object object = bannerList.get(position); //这里我封装了 Glide 4.0 的工具类,用于显示图片 ImageLoaderUtil.load(context, object, imageView); container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } public int dp2px(float var0) { float var1 = context.getResources().getDisplayMetrics().density; return (int) (var0 * var1 0.5F); } public void setBannerPointSize(int bannerPointSize) { this.bannerPointSize = dp2px(bannerPointSize); } public void setBannerPointGravity(int bannerPointGravity) { this.bannerPointGravity = bannerPointGravity; pointLayout.setGravity(bannerPointGravity); } public void setBannerPointDrawableSelected(int bannerPointDrawableSelected) { this.bannerPointDrawableSelected = bannerPointDrawableSelected; } public void setBannerPointDrawableUnselected(int bannerPointDrawableUnselected) { this.bannerPointDrawableUnselected = bannerPointDrawableUnselected; } public void setBannerDelaySecond(int bannerDelaySecond) { this.bannerDelaySecond = bannerDelaySecond; } }

其中自定义属性的 attrs.xml:

<?xml version="1.0" encoding="utf-8"?> <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>

使用说明

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" />

调用:

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<Object> 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 设置方法完毕时最后调用 start 方法 bannerView.start(bannerList); } @Override protected void onStop() { super.onStop(); bannerView.bannerShutdown(); } }

如果以上还满足不了你的需求,可以使用 GitHub 上的轮子。

现有轮子

这个库使用了 ViewPager2 为基础控件,支持了 androidx 兼容包,方便了 UI、Indicator 自定义,支持画廊效果、魅族效果,兼容了水平和垂直轮播,也可以实现类似淘宝头条的效果,依赖包目前只需要导入了 ViewPager2。

android中轮播图怎么完成(Android常用功能轮播图从)(2)

android中轮播图怎么完成(Android常用功能轮播图从)(3)

android中轮播图怎么完成(Android常用功能轮播图从)(4)

android中轮播图怎么完成(Android常用功能轮播图从)(5)

还内置了多种 PageTransformer 效果

android中轮播图怎么完成(Android常用功能轮播图从)(6)

最后

在这里就还分享一份由大佬亲自收录整理的学习PDF 架构视频 面试文档 源码笔记高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料

这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。

当然,你也可以拿去查漏补缺,提升自身的竞争力。

真心希望可以帮助到大家,Android路漫漫,共勉!

如果你有需要的话,只需私信我【进阶】即可获取

android中轮播图怎么完成(Android常用功能轮播图从)(7)

android中轮播图怎么完成(Android常用功能轮播图从)(8)

android中轮播图怎么完成(Android常用功能轮播图从)(9)

,