轮播图也是一个很常见的功能了,通常都置于App首页,每隔几秒就会切换下一张图片,等轮播完毕,则又会从第一张周而复始。这篇博文对该功能的实现做下归纳和整理。
首先看下效果图
接下来看一下实现步骤
1.分析下界面,由可滑动的viewpager,标题textview,图片ImageView,右下角的小圆点(由LinearLayout线性布局包裹),包裹文字和圆点布局的透明黑色的布局。因此我们先来画xml布局文件,其中包裹圆点的linearlayout为空,我们之后在代码中添加圆点。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/icon_banner_default"
tools:context=".MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/main_vp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:gravity="center_vertical"
android:padding="8dp"
android:background="#7F000000"
>
<TextView
android:id="@+id/main_tv"
android:layout_width="0dp"
android:layout_weight="1"
android:text="今天天气不错啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="#ffffff"
/>
<LinearLayout
android:id="@+id/main_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:orientation="horizontal" />
</LinearLayout>
</RelativeLayout>
2.先新建数据的bean,里面有每个页面的图片pic和标题title
package com.fantasychong.bannertest0907;
import java.io.Serializable;
/*
*Crated by yedona on 2018/9/7
*/
public class BannerBean implements Serializable {
private int pic;
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BannerBean(int pic, String title) {
this.pic = pic;
this.title = title;
}
public int getPic() {
return pic;
}
public void setPic(int pic) {
this.pic = pic;
}
}
3.新建用于适配viewpager的继承于PagerAdapter的自定义adapter。
首先生成三个方法
@Override
public int getCount() {
if(bannerList!= null){
return Integer.MAX_VALUE;
}
return 0;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view== o;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
分析代码,getCount()用于设置返回个数,因为我们要滑到最后一张后还要返回第一张从而无限循环,所以这里设为Integer整形的最大值Inter.MAX_VALUE。其余两个方法是固定写法。
手动重写instantiateItem()方法,这个方法我们设置每个页面的图片赋值。
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
final int newPosition= position% bannerList.size();
ImageView imageView= new ImageView(context);
imageView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setImageResource(bannerList.get(newPosition).getPic());
container.addView(imageView);
return imageView;
}
然后我们通过接口回调来设置每张图片的点击事件。
首先写一个interface。
public interface OnBannerClickListener{
void onBannerClick(BannerBean bean);
}
接下来写setOnBannerClickListener方法。
public void setOnBannerClickListener(OnBannerClickListener listener){
this.onBannerClickListener= listener;
}
然后在instantiateItem中调用这个listener。
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
final int newPosition= position% bannerList.size();
ImageView imageView= new ImageView(context);
imageView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setImageResource(bannerList.get(newPosition).getPic());
imageView.setOnClickListener(new OnClickListener(){
public void onClick(View view){
if(onBannerClickListener!= null){
onBannerClickListener.onBannerClick(bannerList.get(newPosition));
}
}
});
container.addView(imageView);
return imageView;
}
4.接下来我们打开MainActivity.class,首先进行初始化控件。
/**
* 初始化控件
*/
private void initView() {
layout = findViewById(R.id.main_layout);
vp = findViewById(R.id.main_vp);
tv = findViewById(R.id.main_tv);
}
5.接下来初始化图片和标题数据
/**
* 初始化图片数据
*/
private void initPicList() {
BannerBean bean = new BannerBean(R.mipmap.ic_launcher, "我看过沙漠下暴雨,看过大海亲吻鲨鱼");
picList.add(bean);
BannerBean bean1 = new BannerBean(R.mipmap.ic_launcher_round, "看过黄昏追逐黎明,没看过你");
picList.add(bean1);
BannerBean bean2 = new BannerBean(R.drawable.ic_launcher_background, "我听过空境的回忆,雨水浇绿孤山岭");
picList.add(bean2);
BannerBean bean3 = new BannerBean(R.drawable.ic_launcher_foreground, "听过被诅咒的秘密,没听过你");
picList.add(bean3);
}
6.给标题默认设置第一条数据
//textview默认设置标题数组的第一条数据
tv.setText(picList.get(0).getTitle());
7.设置自定义adapter适配器。
//设置adapter
adapter = new MainAdapter(MainActivity.this, picList, tv);
vp.setAdapter(adapter);
8.通过接口回调设置imageview图片的点击事件。
adapter.setOnBannerClickListener(new MainAdapter.OnBannerClickListener() {
@Override
public void onBannerClick(BannerBean bean) {
Toast.makeText(MainActivity.this, bean.getTitle(), Toast.LENGTH_SHORT).show();
}
});
9.初始化右下角的圆点指示器。
/**
* 初始化右边圆点指示器
*/
private void initPoint() {
layout.removeAllViews();
if (picList.size() != 1) {
for (int i = 0; i < picList.size(); i++) {
View pointView = new View(this);
int width = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
int height = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
int leftMargin = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height);
layoutParams.leftMargin = leftMargin;
pointView.setLayoutParams(layoutParams);
if (i == 0) {
pointView.setBackgroundResource(R.drawable.oval_point_white);
} else {
pointView.setBackgroundResource(R.drawable.oval_point_gray);
}
layout.addView(pointView);
}
adapter.notifyDataSetChanged();
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int position) {
int newPosition = position % picList.size();
tv.setText(picList.get(newPosition).getTitle());
for (int i = 0; i < layout.getChildCount(); i++) {
View currentPoint = layout.getChildAt(i);
currentPoint.setBackgroundResource(i == newPosition ? R.drawable.oval_point_white : R.drawable.oval_point_gray);
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
}
该方法是比较重要的一环,接下来做下分析。
1)首先,如果图片只有一张时,将不显示圆点布局。图片有多张,将显示图片相等数量的圆点布局。
2)新建圆点背景文件,选中为白色,未选中为灰色。
oval_point_white.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#ffffff"/>
</shape>
oval_point_gray.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#aaaaaa"/>
</shape>
3)按图片数量新建等量的View,默认第一个圆点为白色即选中状态。
for (int i = 0; i < picList.size(); i++) {
View pointView = new View(this);
int width = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
int height = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
int leftMargin = (int) (getResources().getDisplayMetrics().density * 6 + 0.5f);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height);
layoutParams.leftMargin = leftMargin;
pointView.setLayoutParams(layoutParams);
if (i == 0) {
pointView.setBackgroundResource(R.drawable.oval_point_white);
} else {
pointView.setBackgroundResource(R.drawable.oval_point_gray);
}
layout.addView(pointView);
}
adapter.notifyDataSetChanged();
4)设置viewpager的滑动监听。生成三个方法,仅在onPageSelected方法中做处理
@Override
public void onPageSelected(int position) {
int newPosition = position % picList.size();
tv.setText(picList.get(newPosition).getTitle());
for (int i = 0; i < layout.getChildCount(); i++) {
View currentPoint = layout.getChildAt(i);
currentPoint.setBackgroundResource(i == newPosition ? R.drawable.oval_point_white : R.drawable.oval_point_gray);
}
}
该方法中,因为小圆点的position会随时改动,并且切换到最后一个圆点会重新回到第一个圆点依次循环,因而设置newPosition为
int newPosition = position % picList.size();
滑动到一个位置,就把对应位置的title赋到标题TextView上。
tv.setText(picList.get(newPosition).getTitle());
每滑动一个新位置,就给圆点设置白色,其余均为灰色。
for (int i = 0; i < layout.getChildCount(); i++) {
View currentPoint = layout.getChildAt(i);
currentPoint.setBackgroundResource(i == newPosition ? R.drawable.oval_point_white : R.drawable.oval_point_gray);
}
10.现在已经可以正常显示图片和标题了,手动滑动可以切换。接下来我们给它加入自动轮播功能。
这里想到RxJava的interval的定时输出功能,我们可以每隔三秒让viewpager自动切换一次。
/**
* 启动自动轮播
*/
private void AutoSwitch() {
disposable = Flowable.interval(3, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d("chongtimer", aLong + "");
vp.setCurrentItem(vp.getCurrentItem() + 1);
}
})
.subscribe();
}
11.别忘了在onDestroy()方法中dispose掉disposable,否则即使退出应用了,还是会持续输出的。
@Override
protected void onDestroy() {
super.onDestroy();
if (disposable != null) {
disposable.dispose();
}
}
至此全部完成,Demo附上!