Android 轮播图从 0 到 1

简介: 轮播图是 Android 常用功能之一,效果大概是这样的:之前我封装写了一个,基本达到了要求,是继承了 Fragment(当时脑袋肯定锈掉了),里面 Viewpager add Fragment,这次项目多处有轮播...

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

之前我封装写了一个,基本达到了要求,是继承了 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:

<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();
    }
}

最后

欢迎加入Android进阶交流群;701740775。进群可免费领取一份最新技术大纲和Android进阶资料。请备注csdn

相关文章
|
1月前
|
Android开发
Android实战之如何快速实现自动轮播图
本文介绍了在 Android 中使用 `ViewPager2` 和自定义适配器实现轮播图的方法,包括添加依赖、布局配置、创建适配器及实现自动轮播等步骤。
33 0
|
Android开发
Android 使用ViewPager和自定义PagerAdapter实现轮播图效果
Android 使用ViewPager和自定义PagerAdapter实现轮播图效果
120 0
|
Android开发 容器
|
Android开发
Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图
原文:Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图    前面两期学习了 ViewAnimator及其子类ViewSwitcher的使用,以及ViewSwitcher的子类ImageSwitcher和TextSwitcher的使用,你都掌握了吗?本期我们一起来学习ViewAnimator另一个子类 ViewFlipper组件的使用。
1736 0
|
6天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
11天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
13天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
15天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
13天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
14天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
24 2