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

xiaoxiao2021-02-27  492

一、简介

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

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

此项目的Github地址:链接。

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

二、添加依赖

build.gradle中添加依赖:

dependencies{ compile 'com.youth.banner:banner:1.4.9' //最新版本 }

添加权限:

<!-- if you want to load images from the internet --> <uses-permission android:name="android.permission.INTERNET" /> <!-- if you want to load images from a file OR from the internet --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

三、解锁技能

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

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

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" tools:context="com.ping.bannerdemo.activity.MainActivity"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

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

//header.xml <?xml version="1.0" encoding="utf-8"?> <com.youth.banner.Banner xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/banner" android:layout_width="match_parent" android:layout_height="match_parent"/> //item_recycler.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:gravity="center" android:id="@+id/tv_item" android:textSize="20dp" android:textColor="#000" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>

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

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { 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<String> 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<String> 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的属性:

Attributesformadescribedelay_timeinteger轮播间隔时间,默认2000scroll_timeinteger轮播滑动执行时间,默认800is_auto_playboolean是否自动轮播,默认truetitle_backgroundcolorreferencetitle_textcolorcolor标题字体颜色title_textsizedimension标题字体大小title_heightdimension标题栏高度indicator_widthdimension指示器圆形按钮的宽度indicator_heightdimension指示器圆形按钮的高度indicator_margindimension指示器之间的间距indicator_drawable_selectedreference指示器选中效果indicator_drawable_unselectedreference指示器未选中效果image_scale_typeenum和imageview的ScaleType作用一样

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

<com.youth.banner.Banner xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/banner" android:layout_width="match_parent" android:layout_height="match_parent" app:indicator_drawable_selected="@color/colorAccent" app:indicator_drawable_unselected="@android:color/white" app:indicator_height="4dp" app:indicator_margin="4dp" app:indicator_width="20dp"/> 实现效果:

四、Demo下载:

      源码下载

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

转载请注明原文地址: https://www.6miu.com/read-458.html

最新回复(0)