现在很多app的首页顶部都有一个可以滑动切换的ViewPager,或是显示广告图片,或是显示入口推荐信息等等。很多这些ViewPager都带有无限滑动的功能,也就是当滑动到最后一张图片时,继续向右滑动会转到第一张图片;当在第一张图片向左滑动时,会转到最后一张图片。这种功能的实现提升了用户的体验,使用户能更方便的切换,而不用每次都要滑动N次来导航到第一张或是最后一张图片。这种功能的实现很简单,可以通过一些小技巧来实现。下面看看实现后的效果图:
主要的思路:假设现在ViewPager中有5个ImageView的Item,分别编号1,2,3,4,5。然后在第一个元素之前加一个编号5的Item,在最后一个元素之后加一个编号1的Item。现在ViewPager中有7个Item,分别为5,1,2,3,4,5,1。如图所示:
现在有两个场景需要来进行分析。首先假设第二个元素1为当前Item,当在第二个元素1的Item向左滑动时,就到了第一个元素5的Item,当没有进行任何处理时,继续向左滑动是没效果的。我们可以利用ViewPager的setCurrentItem的方法,当从第二个元素1的Item滑动到第一个元素5的Item位置时,就调用setCurrentItem方法使ViewPager跳转到第六个元素5的Item位置处,然后继续向左滑动就从元素5到元素4到元素3了,这样就可以无限向左滑动了。
第二个场景是无限右滑了。首先假设第六个元素5为当前Item,当向右滑动时,就到了第七个元素也就是最后一个元素1的Item了。此时如果没有任何处理,当继续向右滑动时时没有任何效果的。所以当到了第七个元素1的时候,我们就调用ViewPager的setCurrentItem方法跳转到第二个元素1的Item位置,然后继续向右滑动就到了元素2到元素3了,这样就可以无限向右滑动了。
其实无限向右滑动和向左滑动都是一种障眼法的实现。现在来看看ViewPager提供的setCurrentItem方法。setCurrentItem方法有两个参数,第一个参数是跳转到第几个item,第二个参数是否开启当跳转的时候慢慢滑动过去,设置为false就完全感觉不到跳转的影子了,障眼法就是这样实现的,如果设置为true,你就会看到一次跳转中间很多Item的效果了。如GIF图显示:
public void setCurrentItem(int item, boolean smoothScroll) { mPopulatePending = false; setCurrentItemInternal(item, smoothScroll, false); }
最后还有一个就是对ViewPager进行监听了,当从第二个元素1向左滑动到第一个元素5和从倒数第二个元素5向右滑动到倒数第一个元素1的时候进行处理。监听ViewPager主要是利用了OnPageChangeListener。OnPageChangeListener里面有3个需要重写的方法,我们主要利用onPageSelected和onPageScrollStateChanged。onPageSelected在onPageScrollStateChanged之前先被调用,所以在onPageSelected中判断当前位置,并进行处理,设置当前位置值。在onPageScrollStateChanged中调用setCurrentItem方法跳转到当前位置值。
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if(position>LAST_ITEM_INDEX){
isChanged=true;
currentPos=FIRST_ITEM_INDEX;
}else if(position<FIRST_ITEM_INDEX){
isChanged=true;
currentPos=LAST_ITEM_INDEX;
}
}
@Override
public void onPageScrollStateChanged(int state) {
if(ViewPager.SCROLL_STATE_IDLE==state){
if(isChanged){
isChanged=false;
viewPager.setCurrentItem(currentPos,false);
}
}
}
});
}
注意必须设置FIRST_ITEM_INDEX为第二个元素1的位置,即1。LAST_ITEM_INDEX为倒数第二个元素5的位置,即images.size()-2;,就是你的源数组倒数第二个元素的位置。isChanged是用来判断当前是否需要跨越中间多个item进行跳转,也就是上面所说的技巧了。
完整代码:
public class MainActivity extends BaseActivity {
private ViewPager viewPager;
private ImageAdapter imageAdapter;
private int FIRST_ITEM_INDEX;
private int LAST_ITEM_INDEX;
private int currentPos;
private boolean isChanged;
@Override
protected void initView() {
setContentView(R.layout.activity_main);
viewPager= (ViewPager) findViewById(R.id.MainActivity_ViewPager);
}
@Override
protected void initData() {
int[] resIds=new int[]{R.drawable.img_one,R.drawable.img_two,R.drawable.img_three,R.drawable.img_four,R.drawable.img_five};
ArrayList<ImageView> images=new ArrayList<>();
initImages(images,resIds);
imageAdapter=new ImageAdapter(this,images);
viewPager.setAdapter(imageAdapter);
viewPager.setCurrentItem(1);
FIRST_ITEM_INDEX=1;
LAST_ITEM_INDEX=images.size()-2;
}
@Override
protected void initListener() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if(position>LAST_ITEM_INDEX){
isChanged=true;
currentPos=FIRST_ITEM_INDEX;
}else if(position<FIRST_ITEM_INDEX){
isChanged=true;
currentPos=LAST_ITEM_INDEX;
}
}
@Override
public void onPageScrollStateChanged(int state) {
if(ViewPager.SCROLL_STATE_IDLE==state){
if(isChanged){
isChanged=false;
viewPager.setCurrentItem(currentPos,false);
}
}
}
});
}
private void initImages(ArrayList<ImageView> images,int[] resIds){
images.add(createImage(resIds[resIds.length-1]));
for(int i=0;i<resIds.length;i++){
images.add(createImage(resIds[i]));
}
images.add(createImage(resIds[0]));
}
private ImageView createImage(int resId){
ImageView img=new ImageView(this);
img.setScaleType(ImageView.ScaleType.CENTER_CROP);
Glide.with(this)
.load(resId)
.dontAnimate()
.into(img);
return img;
}
}
PagerAdapter实现:
public class ImageAdapter extends PagerAdapter {
private List<ImageView> images;
private Context context;
public ImageAdapter(Context context, ArrayList<ImageView> images){
this.context=context;
this.images=images;
}
@Override
public int getCount() {
return images.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
}
@Override
public void destroyItem(ViewGroup container,int position,Object object){
container.removeView(images.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position){
View view=images.get(position);
final int pos=position;
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, String.valueOf(pos), Toast.LENGTH_SHORT).show();
}
});
ViewPager viewPager= (ViewPager) container;
viewPager.addView(view);
return images.get(position);
}
}