RecyclerView实现广告轮播图
平时都是用RecyclerView实现列表,RecyclerView的强大毋庸置疑,今天就用它来实现广告轮播图。
效果如下
首先,在activity_main.xml里定义布局
<RelativeLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="144dp">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
然后定义列表的item布局—-item_image.xml:
<LinearLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
然后是Adapter,在Adapter初始化时要传入图片列表list:
public class BannerAdapter extends RecyclerView.Adapter<BannerAdapter.ViewHolder> {
private List<Integer> list;
private Context context;
public BannerAdapter(Context context,List<Integer> list){
this.list=list;
this.context=context;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Glide.with(context).load(list.get(position%list.size())).into(holder.imageView);
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
public ViewHolder(View itemView) {
super(itemView);
imageView= (ImageView) itemView.findViewById(R.id.item_image);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
上面代码里需要注意的地方有两处。
第一处是getItemCount() 返回的是Integer.MAX_VALUE。这是因为广告轮播图是无限轮播,getItemCount() 返回的是Adapter中的总项目数,这样才能使RecyclerView能一直滚动。
第二处是onBindViewHolder()中的 position%list.size() ,表示position对图片列表list取余,这样list.get(position%list.size())才能按顺序循环展示图片。
MainActivity.java代码如下:
public class MainActivity extends AppCompatActivity {
private List<Integer> list = new ArrayList<>(4);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//存入图片
list.add(R.drawable.b1);
list.add(R.drawable.b2);
list.add(R.drawable.b3);
list.add(R.drawable.b4);
BannerAdapter adapter = new BannerAdapter(this, list);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
recyclerView.scrollToPosition(list.size()*10);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
需要注意的是LinearLayoutManager 第二个参数表示布局方向,平时默认是垂直的,也就是我们常见的列表样式。这里轮播广告要用为LinearLayoutManager.HORIZONTAL,水平方向。
还有一点需要注意,recyclerView.scrollToPosition(list.size()*10)这句使RecyclerView一开始位于 list.size()*10 处,避免了一开始position为0不能前滑的尴尬。
由于广告是一页一页的划过去,所以我们还需要用到一个类,SnapHelper的子类PagerSnapHelper,用起来很简单,两句话。直接追加到上面的recyclerView.setAdapter(adapter) 后面。
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
recyclerView.setAdapter(adapter);
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
然后加入自动轮播,此处使用ScheduledExecutorService 来完成。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
recyclerView.smoothScrollToPosition(layoutManager.findFirstVisibleItemPosition() + 1);
}
}, 2000, 2000, TimeUnit.MILLISECONDS);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这段代码表示2秒后每过2秒运行一次run()里的程序 。layoutManager.findFirstVisibleItemPosition() 表示得到当前RecyclerView第一个能看到的item的位置。由于广告是每次展示一张,所以得到的就是当前图片的position。recyclerView.smoothScrollToPosition(int position)表示滑动到某个position。所以上面的代码就表示每过2秒滑动到下个position,以此来完成自动轮播。
还有一点还要改进,程序运行起来后会发现自动轮播切换图片时速度太快了,这个怎么解决呢?
点开LinearLayoutManager的源码,查找smoothScrollToPosition()方法。
这里滑动用到了LinearSmoothScroller,继续点击去,里面有一个calculateSpeedPerPixel()方法。
官方注释:计算滚动速度。如果返回值是2毫秒,这表示着滚动1000像素需要2秒。
所以我们继承LinearLayoutManager,重写smoothScrollToPosition()方法,并将里面的修改LinearSmoothScroller的这个方法返回值修改。
新建SmoothLinearLayoutManager.java 文件,代码如下:
public class SmoothLinearLayoutManager extends LinearLayoutManager {
public SmoothLinearLayoutManager(Context context) {
super(context);
}
public SmoothLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 0.2f; //返回0.2
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
程序写到这里,广告轮播图已初具雏形了,最后还差个指示器。
显示指示器上的红点需要得到当前展示的广告轮播图片的position。RecyclerView有个addOnScrollListener()方法,可以监听当前滑动状态。所以代码如下:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int i = layoutManager.findFirstVisibleItemPosition() % list.size();
//得到指示器红点的位置
}
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
onScrollStateChangedd的 newState 参数有三种状态SCROLL_STATE_IDLE、SCROLL_STATE_DRAGGING和SCROLL_STATE_SETTLING,分布表示静止状态,拖拽状态和手指离开后的惯性滚动状态。所以这里当RecyclerView的状态为SCROLL_STATE_IDLE时得到当前图片的position,然后与图片列表取余就得到指示器红点的位置。
上一篇: RecyclerView 使用 一次只能选择一个条目 到下一个页面
下一篇: 异步加载基础