引导界面滑动导航 + 大于等于1页时无限轮播 + 各种切换动画轮播效果

Overview

🏃 BGABanner-Android 🏃

目录

功能介绍

  • 引导界面导航效果
  • 支持根据服务端返回的数据动态设置广告条的总页数
  • 支持大于等于1页时的无限循环自动轮播、手指按下暂停轮播、抬起手指开始轮播
  • 支持自定义指示器位置和广告文案位置
  • 支持图片指示器和数字指示器
  • 支持 ViewPager 各种切换动画
  • 支持选中特定页面
  • 支持监听 item 点击事件
  • 加载网络数据时支持占位图设置,避免出现整个广告条空白的情况
  • 多个 ViewPager 跟随滚动

效果图与示例 apk

banner

点击下载 BGABannerDemo.apk 或扫描下面的二维码安装

BGABannerDemo apk文件二维

常见问题

  1. 结合 Fresco 加载图片请参考 FrescoDemoActivity
  2. 自定义 item 布局文件请参考 FrescoDemoActivity

使用

1.添加 Gradle 依赖

  • maven { url 'https://jitpack.io' } 添加到 root build.gradle 的 repositories 中
  • 在 app build.gradle 中添加如下依赖,末尾的「latestVersion」指的是徽章 里的版本名称,请自行替换
implementation 'androidx.legacy:legacy-support-v4:latestVersion'
implementation 'com.github.bingoogolapple:BGABanner-Android:latestVersion'

2.在布局文件中添加 BGABanner

<cn.bingoogolapple.bgabanner.BGABanner
    android:id="@+id/banner_guide_content"
    style="@style/MatchMatch"
    app:banner_pageChangeDuration="1000"
    app:banner_pointAutoPlayAble="false"
    app:banner_pointContainerBackground="@android:color/transparent"
    app:banner_pointDrawable="@drawable/bga_banner_selector_point_hollow"
    app:banner_pointTopBottomMargin="15dp"
    app:banner_transitionEffect="alpha" />

3.在 Activity 或者 Fragment 中配置 BGABanner 的数据源

有多种配置数据源的方式,这里仅列出三种方式。更多初始化方式请查看 demo

配置数据源的方式1:通过传入数据模型并结合 Adapter 的方式配置数据源。这种方式主要用于加载网络图片,以及实现少于3页时的无限轮播

mContentBanner.setAdapter(new BGABanner.Adapter<ImageView, String>() {
    @Override
    public void fillBannerItem(BGABanner banner, ImageView itemView, String model, int position) {
        Glide.with(MainActivity.this)
                .load(model)
                .placeholder(R.drawable.holder)
                .error(R.drawable.holder)
                .centerCrop()
                .dontAnimate()
                .into(itemView);
    }
});

mContentBanner.setData(Arrays.asList("网络图片路径1", "网络图片路径2", "网络图片路径3"), Arrays.asList("提示文字1", "提示文字2", "提示文字3"));

配置数据源的方式2:通过直接传入视图集合的方式配置数据源,主要用于自定义引导页每个页面布局的情况

List<View> views = new ArrayList<>();
views.add(View.inflate(context, R.layout.layout_guide_one, null));
views.add(View.inflate(context, R.layout.layout_guide_two, null));
views.add(View.inflate(context, R.layout.layout_guide_three, null));
mContentBanner.setData(views);

配置数据源的方式3:通过传入图片资源 id 的方式配置数据源,主要用于引导页每一页都是只显示图片的情况

// Bitmap 的宽高在 maxWidth maxHeight 和 minWidth minHeight 之间
BGALocalImageSize localImageSize = new BGALocalImageSize(720, 1280, 320, 640);
// 设置数据源
mContentBanner.setData(localImageSize, ImageView.ScaleType.CENTER_CROP,
        R.drawable.uoko_guide_background_1,
        R.drawable.uoko_guide_background_2,
        R.drawable.uoko_guide_background_3);

4.监听广告 item 的单击事件,在 BGABanner 里已经帮开发者处理了防止重复点击事件

mContentBanner.setDelegate(new BGABanner.Delegate<ImageView, String>() {
    @Override
    public void onBannerItemClick(BGABanner banner, ImageView itemView, String model, int position) {
        Toast.makeText(banner.getContext(), "点击了" + position, Toast.LENGTH_SHORT).show();
    }
});

5.设置「进入按钮」和「跳过按钮」控件资源 id 及其点击事件,如果进入按钮和跳过按钮有一个不存在的话就传 0,在 BGABanner 里已经帮开发者处理了防止重复点击事件,在 BGABanner 里已经帮开发者处理了「跳过按钮」和「进入按钮」的显示与隐藏

mContentBanner.setEnterSkipViewIdAndDelegate(R.id.btn_guide_enter, R.id.tv_guide_skip, new BGABanner.GuideDelegate() {
    @Override
    public void onClickEnterOrSkip() {
        startActivity(new Intent(GuideActivity.this, MainActivity.class));
        finish();
    }
});

自定义属性说明

<declare-styleable name="BGABanner">
    <!-- 指示点容器背景 -->
    <attr name="banner_pointContainerBackground" format="reference|color" />
    <!-- 指示点背景 -->
    <attr name="banner_pointDrawable" format="reference" />
    <!-- 指示点容器左右内间距 -->
    <attr name="banner_pointContainerLeftRightPadding" format="dimension" />
    <!-- 指示点上下外间距 -->
    <attr name="banner_pointTopBottomMargin" format="dimension" />
    <!-- 指示点左右外间距 -->
    <attr name="banner_pointLeftRightMargin" format="dimension" />
    <!-- 指示器的位置 -->
    <attr name="banner_indicatorGravity">
        <flag name="top" value="0x30" />
        <flag name="bottom" value="0x50" />
        <flag name="left" value="0x03" />
        <flag name="right" value="0x05" />
        <flag name="center_horizontal" value="0x01" />
    </attr>
    <!-- 是否开启自动轮播 -->
    <attr name="banner_pointAutoPlayAble" format="boolean" />
    <!-- 自动轮播的时间间隔 -->
    <attr name="banner_pointAutoPlayInterval" format="integer" />
    <!-- 页码切换过程的时间长度 -->
    <attr name="banner_pageChangeDuration" format="integer" />
    <!-- 页面切换的动画效果 -->
    <attr name="banner_transitionEffect" format="enum">
        <enum name="defaultEffect" value="0" />
        <enum name="alpha" value="1" />
        <enum name="rotate" value="2" />
        <enum name="cube" value="3" />
        <enum name="flip" value="4" />
        <enum name="accordion" value="5" />
        <enum name="zoomFade" value="6" />
        <enum name="fade" value="7" />
        <enum name="zoomCenter" value="8" />
        <enum name="zoomStack" value="9" />
        <enum name="stack" value="10" />
        <enum name="depth" value="11" />
        <enum name="zoom" value="12" />
    </attr>
    <!-- 提示文案的文字颜色 -->
    <attr name="banner_tipTextColor" format="reference|color" />
    <!-- 提示文案的文字大小 -->
    <attr name="banner_tipTextSize" format="dimension" />
    <!-- 加载网络数据时覆盖在 BGABanner 最上层的占位图 -->
    <attr name="banner_placeholderDrawable" format="reference" />
    <!-- 是否是数字指示器 -->
    <attr name="banner_isNumberIndicator" format="boolean" />
    <!-- 数字指示器文字颜色 -->
    <attr name="banner_numberIndicatorTextColor" format="reference|color" />
    <!-- 数字指示器文字大小 -->
    <attr name="banner_numberIndicatorTextSize" format="dimension" />
    <!-- 数字指示器背景 -->
    <attr name="banner_numberIndicatorBackground" format="reference" />
    <!-- 当只有一页数据时是否显示指示器,默认值为 false -->
    <attr name="banner_isNeedShowIndicatorOnOnlyOnePage" format="boolean" />
    <!-- 自动轮播区域距离 BGABanner 底部的距离,用于使指示器区域与自动轮播区域不重叠 -->
    <attr name="banner_contentBottomMargin" format="dimension"/>
    <!-- 宽高比例,如果大于 0,则会根据宽度来计算高度,否则使用 android:layout_height 指定的高度 -->
    <attr name="banner_aspectRatio" format="float"/>
    <!-- 占位图和资源图片缩放模式 -->
    <attr name="android:scaleType"/>
</declare-styleable>

代码是最好的老师,更多详细用法请查看 demo 🐾

关于我

个人主页 邮箱 BGA 系列开源库 QQ 群 GitHub 喵(专注于 GitHub 等一切与程序员有关的内容)
bingoogolapple.cn [email protected] BGA_CODE_CLUB GitHub喵

打赏支持

如果您觉得 BGA 系列开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 🐵 就太 👍 了。您的支持将鼓励我继续创作:octocat:

如果您目前正打算购买通往墙外的梯子,可以使用我的邀请码「YFQ9Q3B」购买 Lantern,双方都赠送三个月的专业版使用时间 🍻

License

Copyright 2015 bingoogolapple

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • banner用在Viewpager多个Fragment中 切换Fragment后 首次播放失去动画

    banner用在Viewpager多个Fragment中 切换Fragment后 首次播放失去动画

    我的业务场景 : 在ViewPager中每个Fragment都有一个自动播放的Banner, 当当前Fragment用户不可见时( setUserVisibleHint )我就stopBanner播放,当Fragment用户可见时我就startBanner播放。

    Banner能正常如我所愿地播放与暂停,但是如果从当前页切换到别的页的时候,首次播放Banner时会没有动画直接 跳过Banner第一张图片。当Banner第二张图片开始轮播时,轮播又恢复正常有切换动画。看了很旧源码不知道哪里出问题了。

    opened by envyfan 5
  • 当view数量少于4个时,滑动抛出异常。

    当view数量少于4个时,滑动抛出异常。

    当view数量少于4个时,滑动抛出异常。

    java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:3562) at android.view.ViewGroup.addView(ViewGroup.java:3415) at android.support.v4.view.ViewPager.addView(ViewPager.java:1309) at android.view.ViewGroup.addView(ViewGroup.java:3360) at android.view.ViewGroup.addView(ViewGroup.java:3336) at cn.bingoogolapple.bgabanner.BGABanner$MyAdapter.instantiateItem(BGABanner.java:262)

    opened by jiandao7114 5
  • 如何使用你的这个框架实现网络图片自动滚动

    如何使用你的这个框架实现网络图片自动滚动

    我想使用你的这个框架并且使用Android Universal Image Loader来加载图片,做出类似于淘宝、京东那些活动Banner,但是会报错 java.lang.ArithmeticException: divide by zero 代码如下:

    @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            options = new DisplayImageOptions.Builder()
                    .showImageForEmptyUri(R.drawable.ic_empty)
                    .showImageOnFail(R.drawable.ic_error)
                    .resetViewBeforeLoading(true)
                    .cacheOnDisk(true)
                    .imageScaleType(ImageScaleType.EXACTLY)
                    .bitmapConfig(Bitmap.Config.RGB_565)
                    .considerExifParams(true)
                    .displayer(new FadeInBitmapDisplayer(300))
                    .build();
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            View view = inflater.inflate(R.layout.fragment_index1, container, false);
            ButterKnife.inject(this, view);
    
            final List<View> banners = new ArrayList<>();
            View imageLayout = LayoutInflater.from(getActivity()).inflate(R.layout.listitem_banner, container, false);
            final ImageView imageView = (ImageView) imageLayout.findViewById(R.id.imageView);
    
            for (int position = 0; position < imageUrls.length; position++) {
                ImageLoader.getInstance().loadImage(imageUrls[position], new SimpleImageLoadingListener() {
                    @Override
                    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                        super.onLoadingComplete(imageUri, view, loadedImage);
                        imageView.setImageBitmap(loadedImage);
                        banners.add(view);
                    }
                });
            }
            banner.setViewPagerViews(banners);
    
            return view;
        }
    
    opened by angelen10 5
  • 小米note内存溢出OOM【已自己解决】

    小米note内存溢出OOM【已自己解决】

    在小米note上,放一张大小为20k的图片就崩溃。引发oom,java.lang.OutOfMemoryError: Failed to allocate a 6482940 byte allocation with 2818408 free bytes and 2MB until OOM 原因是你的BGABannerUtil下的getItemImageViewimageView.setImageResource(placeholderResId);出问题了。。 以下是解决方法

    imageView.setImageBitmap(readBitMap(context, placeholderResId));

    public static Bitmap readBitMap(Context context, int resId){
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
    //获取资源图片
    InputStream is = context.getResources().openRawResource(resId);
    return BitmapFactory.decodeStream(is,null,opt);
    } http://263229365.iteye.com/blog/1562924 这是解决链接,希望帮到你

    opened by Dvtion 4
  • 为何我集成到我的项目里,轮播会有问题,我完全按照你的DEMO来用BGABanner的啊?

    为何我集成到我的项目里,轮播会有问题,我完全按照你的DEMO来用BGABanner的啊?

    我添加了三张轮播图的url,开始轮播,但是每次刚到第二张,还没有等待一点间隔时间,立马就切换到第三张了,不知道怎么回事,你的DEMO好好的啊,我只是在我的项目里作为listview的headView了啊 调用代码如下: ` BGABanner vp_view ; vp_view = (BGABanner) headView.findViewById(R.id.vp);

    vp_view.setAdapter(this); List ads=new ArrayList<>(); List tips=new ArrayList<>(); tips.add(""); tips.add(""); tips.add(""); ads.add("http://www.pc6.com/up/2014-9/14113709517423674.jpg"); ads.add("http://pic0.mofang.com/2014/0922/20140922094300855.jpg"); ads.add("http://pic0.mofang.com/2014/1113/20141113110312116.jpg"); vp_view.setData(ads,tips);

    @Override public void fillBannerItem(BGABanner banner, View view, Object model, int position) { Picasso.with(getActivity()) .load((String)model) .placeholder(R.drawable.placeholder_ad) .into((ImageView) view); }

    xml如下: <cn.bingoogolapple.bgabanner.BGABanner android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" app:banner_indicatorGravity="center_horizontal" app:banner_pointDrawable="@drawable/bga_banner_selector_point_solid" app:banner_transitionEffect="stack" />` 作者求解啊,,真心觉得你这库很好,,为什么到我这里就出问题了呢,,,我昨天还给你打赏了,,帮帮我吧。。。

    opened by yaochangliang159 4
  • 【BUG】ViewGroup﹕ addInArray been called....

    【BUG】ViewGroup﹕ addInArray been called....

    D/ViewGroup﹕ addInArray been called, this = ui.banner.BGAViewPager{41cfd120 VFED.... ......ID 0,0-1280,752}call stack = java.lang.Throwable: addInArray at android.view.ViewGroup.addInArray(ViewGroup.java:3786) at android.view.ViewGroup.addViewInner(ViewGroup.java:3740) at android.view.ViewGroup.addViewInLayout(ViewGroup.java:3687) at android.view.ViewGroup.addViewInLayout(ViewGroup.java:3668) at android.support.v4.view.ViewPager.addView(ViewPager.java:1339) at android.view.ViewGroup.addView(ViewGroup.java:3509) at android.view.ViewGroup.addView(ViewGroup.java:3485) at ui.banner.BGABanner$PageAdapter.instantiateItem(BGABanner.java:504) at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:869) at android.support.v4.view.ViewPager.populate(ViewPager.java:1053) at android.support.v4.view.ViewPager.populate(ViewPager.java:951) at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1473) at android.view.View.measure(View.java:16834) at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:824) at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:500) at android.view.View.measure(View.java:16834) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5374) at android.widget.FrameLayout.onMeasure(FrameLayout.java:340) at android.view.View.measure(View.java:16834) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5374) at android.widget.FrameLayout.onMeasure(FrameLayout.java:340) at android.view.View.measure(View.java:16834) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5374) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1621) at android.widget.LinearLayout.measureVertical(LinearLayout.java:742) at android.widget.LinearLayout.onMeasure(LinearLayout.java:607) at android.view.View.measure(View.java:16834) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5374) at android.widget.FrameLayout.onMeasure(FrameLayout.java:340) at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2368) at android.view.View.measure(View.java:16834) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2252) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1315) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1513) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1192) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6231) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:788) at android.view.Choreographer.doCallbacks(Choreographer.java:591) at android.view.Choreographer.doFrame(Choreographer.java:560) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:774) at android.os.Handler.handleCallback(Handler.java:808) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640) at dalvik.system.NativeStart.main(Native Method)

    opened by xiaomeixw 4
  • 当Banner里的view大于两个才会自动轮播,介绍里说的是大于1个就会自动轮播。

    当Banner里的view大于两个才会自动轮播,介绍里说的是大于1个就会自动轮播。

    从源码看是当小于3条数据就不自动轮播了,为什么介绍里说的是大于1条就自动轮播呢?这里设置为3条是基于什么考虑呢? public void setData(List views, List<? extends Object> models, List tips) { ...

        if (mAutoPlayAble && views.size() < 3 && mHackyViews == null) {
            mAutoPlayAble = false;
        }
    
    opened by WarriorYu 3
  • Caused by: java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity

    Caused by: java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity

    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.handy.app/com.handy.frame.module.guide.GuideActivity}: java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2679) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2740) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1486) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6166) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) Caused by: java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity at cn.bingoogolapple.bgabanner.BGABanner.setEnterSkipViewIdAndDelegate(BGABanner.java:447) at com.handy.frame.module.guide.GuideActivity.initViewHDB(GuideActivity.java:85) at com.handy.base.app.BaseActivity.onStart(BaseActivity.java:137) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249) at android.app.Activity.performStart(Activity.java:6768) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2642) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2740)  at android.app.ActivityThread.-wrap12(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1486)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:154)  at android.app.ActivityThread.main(ActivityThread.java:6166)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) 

    opened by Andy045 3
  • 关于指示器选中和未选中图片大小不一样时的问题解决

    关于指示器选中和未选中图片大小不一样时的问题解决

    楼主代码中指示器选中与否的写法是 for (int i = 0; i < mPointRealContainerLl.getChildCount(); i++) { mPointRealContainerLl.getChildAt(i).setEnabled(i == newCurrentPoint); // 处理指示器选中和未选中状态图片尺寸不相等 mPointRealContainerLl.getChildAt(i).requestLayout(); } 这样会有问题,如果选中图片比未选中图片大的话,未选中图片会被拉伸。只需要改一点就可以了: mPointRealContainerLl.getChildAt(i).setSelected(i == newCurrentPoint); 把setEnabled改成setSelected就可以。相应的selector里的“state_enabled”也要改成“state_selected”

    opened by androidliuzemin 3
  • 关于调用setData的不同重载方法造成的无限轮播问题

    关于调用setData的不同重载方法造成的无限轮播问题

    有看过这个问题,「为什么必须大于2条数据 才可以自动轮播 #130」,但场景不同

    问题同样是:2条数据不能自动轮播

    当我调用 setData(List views, List<? extends Object> models, List tips) 该方法进行设置数据时,

    if (mAutoPlayAble && views.size() < 3 && mHackyViews == null) { mAutoPlayAble = false; }

    该判断中的第三个条件就会一值为null,导致mAutoPlayAble会被设置为false

    动态的为 mHackyViews添加 view 又会出现ViewPager滑动空白的问题

    opened by Yis92 0
Owner
王浩
自助者天助之,学习ing
王浩