Android框架之路——Banner实现轮播图(RecyclerView添加Header)

一、简介

Banner能实现循环播放多个广告图片和手动滑动循环等功能。因为原生ViewPager并不支持循环翻页, 要实现循环还得需要自己去动手。Banner框架可以进行不同样式、不同动画设置, 以及完善的api方法能满足大部分软件首页轮播图效果的需求。

有一篇博客讲了一些,可以参考banner框架,一个比较齐全的框架;

此项目的Github地址:链接。

本篇教程依然涉及一些其他方面的东西,如果有问题,可以参考我之前的系列文章。

二、添加依赖

  1. build.gradle中添加依赖:

    dependencies{
        compile 'com.youth.banner:banner:1.4.9'  //最新版本
    }
    
  2. 添加权限:

    
     
    
    
    
    

三、解锁技能

1. 加载数据和刷新数据:

  • 首先,添加我们MainActivity的布局文件,里面含有一个SwipeRefreshLayout,包裹RecyclerView,SwipeRefreshLayout用来实现下拉刷新。

    
    
    
        
    
            
    
            
    
        
    
    
    
  • 然后,我们要在RecyclerView中添加俩种布局,一个Header存放Banner,另一个则是正常的item布局。先放上俩个布局的内容。

    //header.xml
    
    
    
    //item_recycler.xml
    
    
        
    
    
    

    要添加俩种布局,得在Adapter中实现,这里可以全面的看一下各种LayoutManager中怎么添加header布局,戳这。这里,我就用了文中的第一个,主要方法就是根据不同的ViewType返回不同的ViewHolder。通过setter方法将header的view注入进adapter。先来看一下adapter吧。

    public class MyAdapter extends RecyclerView.Adapter {
    
        public static final int TYPE_HEADER = 0;
        public static final int TYPE_NORMAL = 1;
    
        private View mHeaderView;
        private String[] mData;
        private Context mContext;
    
        public void setHeaderView(View headerView) {
            mHeaderView = headerView;
        }
    
        public MyAdapter(Context context, String[] data) {
            mContext = context;
            mData = data;
        }
    
        //根据pos返回不同的ItemViewType
        @Override
        public int getItemViewType(int position) {
            if (mHeaderView == null) return TYPE_NORMAL;
            if (position == 0) return TYPE_HEADER;
            return TYPE_NORMAL;
        }
    
        //在此根据ItemViewType来决定返回何种ViewHolder
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (mHeaderView != null && viewType == TYPE_HEADER)
                return new MyViewHolder(mHeaderView);
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);
            return new MyViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            if(getItemViewType(position) == TYPE_HEADER) return;
    
            final int pos = getRealPosition(holder);
            final String data = mData[pos];
            if(holder instanceof MyViewHolder) {
                ((MyViewHolder) holder).mTvItem.setText(data);
            }
        }
    
        private int getRealPosition(RecyclerView.ViewHolder holder) {
            int position = holder.getLayoutPosition();
            return mHeaderView == null ? position : position - 1;
        }
    
        //返回正确的item个数
        @Override
        public int getItemCount() {
            return mHeaderView == null ? mData.length : mData.length + 1;
        }
    
        public class MyViewHolder extends RecyclerView.ViewHolder {
    
            @BindView(R.id.tv_item)
            TextView mTvItem;
    
            public MyViewHolder(View itemView) {
                super(itemView);
                if(itemView == mHeaderView)
                    return;
                ButterKnife.bind(this, itemView);
            }
        }
    }
    
  • 看完Adapter,我们要看看在哪里对headerview进行set了。MainActivity中我们渲染出header的布局,然后通过setHeaderView将view传递进去即可。

    //渲染header布局
    View header = LayoutInflater.from(this).inflate(R.layout.header, null);
    mBanner = (Banner) header.findViewById(R.id.banner);
    //设置banner的高度为手机屏幕的四分之一
    mBanner.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, App.H/4));
    //arrays资源文件存放banner图片的url地址
    String[] data = getResources().getStringArray(R.array.demo_list);
    
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    myAdapter = new MyAdapter(this, data);
    //设置headerview
    myAdapter.setHeaderView(mBanner);
    mRecyclerView.setAdapter(myAdapter);
    
  • 然而启动banner:

    mBanner.setImages(App.images)
           .setImageLoader(new GlideImageLoader())
           .start();
    

    这里new了一个GlideImageLoader的实例,官方给出的各种图片加载库加载的方式如下:

    public class GlideImageLoader extends ImageLoader {
        @Override
        public void displayImage(Context context, Object path, ImageView imageView) {
            /**
              注意:
              1.图片加载器由自己选择,这里不限制,只是提供几种使用方法
              2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器,
              传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行,
              切记不要胡乱强转!
             */
            eg:
    
            //Glide 加载图片简单用法
            Glide.with(context).load(path).into(imageView);
    
            //Picasso 加载图片简单用法
            Picasso.with(context).load(path).into(imageView);
    
            //用fresco加载图片简单用法,记得要写下面的createImageView方法
            Uri uri = Uri.parse((String) path);
            imageView.setImageURI(uri);
        }
    
        //提供createImageView 方法,如果不用可以不重写这个方法,主要是方便自定义ImageView的创建
        @Override
        public ImageView createImageView(Context context) {
            //使用fresco,需要创建它提供的ImageView,当然你也可以用自己自定义的具有图片加载功能的ImageView
            SimpleDraweeView simpleDraweeView=new SimpleDraweeView(context);
            return simpleDraweeView;
        }
    }
    
  • 下拉时,我们需要在轮播图中更新数据,我们需要为SwipeRefreshLayout添加onRefresh()事件:

    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case REFRESH_COMPLETE:
                    String[] urls = getResources().getStringArray(R.array.url);
                    List list = Arrays.asList(urls);
                    List arrayList = new ArrayList(list);
                    //把新的图片地址加载到Banner
                    mBanner.update(arrayList);
                    //下拉刷新控件隐藏
                    mSwipeRefreshLayout.setRefreshing(false);
                    break;
            }
        }
    };
    
    .......
    
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            mHandler.sendEmptyMessageDelayed(REFRESH_COMPLETE, 2000);
        }
    });
    
  • 在Banner中,我们设置了高度为四分之一屏幕,屏幕的高度是在app一初始化就获取到的,同时,在这里我们用了一个Android Crash的框架Recovery,也没必要用,就是可以把错误反馈到手机上。可以参考这个博客。

    public class App extends Application{
    
        public static int H;
        public static List images=new ArrayList<>();
        public static List titles=new ArrayList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            initBanner();
        }
    
        private void initBanner() {
            H = getScreenH(this);
    
            Recovery.getInstance()
                    .debug(true)
                    .recoverInBackground(false)
                    .recoverStack(true)
                    .mainPage(MainActivity.class)
                    .init(this);
    
            String[] urls = getResources().getStringArray(R.array.url4);
            String[] tips = getResources().getStringArray(R.array.title);
            List list = Arrays.asList(urls);
            images = new ArrayList<>(list);
            titles= Arrays.asList(tips);
        }
    
        /**
         * 获取屏幕高度
         * @param aty
         * @return
         */
        public int getScreenH(Context aty){
            DisplayMetrics dm = aty.getResources().getDisplayMetrics();
            return dm.heightPixels;
        }
    }
    
  • 最后,看一下我们的运行效果:

2. Banner的各种动画:

  • Banner的动画都在com.youth.banner.transformer包下,包含下面的这些,相信看名字就知道大概什么效果。

    com.youth.banner.transformer.AccordionTransformer;
    com.youth.banner.transformer.BackgroundToForegroundTransformer;
    com.youth.banner.transformer.CubeInTransformer;
    com.youth.banner.transformer.CubeOutTransformer;
    com.youth.banner.transformer.DefaultTransformer;
    com.youth.banner.transformer.DepthPageTransformer;
    com.youth.banner.transformer.FlipHorizontalTransformer;
    com.youth.banner.transformer.FlipVerticalTransformer;
    com.youth.banner.transformer.ForegroundToBackgroundTransformer;
    com.youth.banner.transformer.RotateDownTransformer;
    com.youth.banner.transformer.RotateUpTransformer;
    com.youth.banner.transformer.ScaleInOutTransformer;
    com.youth.banner.transformer.StackTransformer;
    com.youth.banner.transformer.TabletTransformer;
    com.youth.banner.transformer.ZoomInTransformer;
    com.youth.banner.transformer.ZoomOutSlideTransformer;
    com.youth.banner.transformer.ZoomOutTranformer;
    
  • 通过setBannerAnimation()来设置动画。例如,设置为方块滚动;

     ......
    
     mBanner.setImages(App.images)
            .setBannerAnimation(CubeOutTransformer.class)
            .setImageLoader(new GlideImageLoader())
            .start();
    
     ......
    
  • 效果如下:

3. Banner的各种内置样式 :

  • Banner内置的样式有以下几种,

    BannerConfig.NOT_INDICATOR       //没有指示器
    BannerConfig.CIRCLE_INDICATOR    //圆圈指示器(默认)
    BannerConfig.NUM_INDICATOR       //数字圆盘指示器
    BannerConfig.NUM_INDICATOR_TITLE //数字圆盘带标题的
    BannerConfig.CIRCLE_INDICATOR_TITLE  //圆圈指示器附带标题
    BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE  //区别于上一个,圆圈指示器在标题栏里
    
  • 这里我们实现一下圆圈指示器附带标题的样式:

    ArrayList titles = new ArrayList<>(Arrays.asList(new String[]{"first title", "second title", "third title", "fourth title"}));
    mBanner.setImages(App.images)
            .setBannerAnimation(CubeOutTransformer.class)
            .setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE)
            .setBannerTitles(titles)
            .setImageLoader(new GlideImageLoader())
            .start();
    
  • 效果如下:

4. Banner自定义样式:

  • 自定义banner的属性:

    Attributes forma describe
    delay_time integer 轮播间隔时间,默认2000
    scroll_time integer 轮播滑动执行时间,默认800
    is_auto_play boolean 是否自动轮播,默认true
    title_background color reference
    title_textcolor color 标题字体颜色
    title_textsize dimension 标题字体大小
    title_height dimension 标题栏高度
    indicator_width dimension 指示器圆形按钮的宽度
    indicator_height dimension 指示器圆形按钮的高度
    indicator_margin dimension 指示器之间的间距
    indicator_drawable_selected reference 指示器选中效果
    indicator_drawable_unselected reference 指示器未选中效果
    image_scale_type enum 和imageview的ScaleType作用一样

    参照上述属性,现在对指示器设置高宽和选中颜色:

    
    
  • 实现效果:

四、Demo下载:

      源码下载


个人公众号:每日推荐一篇技术博客,坚持每日进步一丢丢…欢迎关注,想建个微信群,主要讨论安卓和Java语言,一起打基础、用框架、学设计模式,菜鸡变菜鸟,菜鸟再起飞,愿意一起努力的话可以公众号留言,谢谢…

你可能感兴趣的:(Android,Android框架之路)