开源项目学习----InfiniteViewPager

1.项目应用场景:官方提供的ViewPager不能循环滑动,当从左向右滑动到最后一页后,如果想再滑到第一页,则必须再从右向左滑动到第一页。本项目就是为了解决此问题而产生。

2.项目源码解析(主要包括InfinitePagerAdapter.java和InfiniteViewPager.java文件

首先自定义一个ViewPager
package com.antonyt.infiniteviewpager;


import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;

/**
 * A {@link ViewPager} that allows pseudo-infinite paging with a wrap-around effect. Should be used with an {@link
 * InfinitePagerAdapter}.
 */
public class InfiniteViewPager extends ViewPager {

    public InfiniteViewPager(Context context) {
        super(context);
    }

    public InfiniteViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setAdapter(PagerAdapter adapter) {
        super.setAdapter(adapter);
        // 在给ViewPager填充数据的时候初始化第一个显示的view
        setCurrentItem(0);
    }

    @Override
    public void setCurrentItem(int item) {
        setCurrentItem(item, false);
    }

    @Override
    public void setCurrentItem(int item, boolean smoothScroll) {<span style="font-family: Arial, Helvetica, sans-serif;">     </span>
<pre name="code" class="java">        // 这里是关键,将当前页面所处的位置对页面实际总数求余数,同时加上页面总数的100倍
item = getOffsetAmount() + (item % getAdapter().getCount());  
 
 
         super.setCurrentItem(item, smoothScroll);  
  }
</pre><pre name="code" class="java"><pre name="code" class="java">  // 获取当前页面的实际位置
@Override public int getCurrentItem() {  
 
 
    int position = super.getCurrentItem();      
  if (getAdapter() instanceof InfinitePagerAdapter) {       
     InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();     
       return (position % infAdapter.getRealCount());    
   } else { 
           return super.getCurrentItem();  
      }   
 }
</pre><pre name="code" class="java"><pre name="code" class="java">   // 设置可完整循环滑动的次数
private int getOffsetAmount() {  
 
 
   if (getAdapter() instanceof InfinitePagerAdapter) {    
        InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();       
     return infAdapter.getRealCount() * 100;     
   } else {        
    return 0;       
 }   
 }
}
从上可以看出,主要是重写  setCurrentItem(int item, boolean smoothScroll)函数,给当前页面的位置赋予一个新的索引值,为循环滑动留出足够的控件。然后就是增加了一个设置循环滑动次数的方法  getOffsetAmount(),上例是页面实际总数的100倍。
</pre><pre name="code" class="java">
接着是自定义一个<span class="pl-e" style="box-sizing: border-box; color: rgb(121, 93, 163); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;">PagerAdapter</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;"> ,源码如下:</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;"></span><pre name="code" class="java">package com.antonyt.infiniteviewpager;

import android.database.DataSetObserver;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * A PagerAdapter that wraps around another PagerAdapter to handle paging wrap-around.
 */
public class InfinitePagerAdapter extends PagerAdapter {

    private static final String TAG = "InfinitePagerAdapter";
    private static final boolean DEBUG = true;

    private PagerAdapter adapter;

    public InfinitePagerAdapter(PagerAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public int getCount() {
<pre name="code" class="java" style="color: rgb(51, 51, 51); line-height: 16.799999237060547px;">       // 填充ViewPager的的视图的虚拟总数 
return Integer.MAX_VALUE; } /** *
    // 返回填充ViewPager的的视图的实际总数 
*/ public int getRealCount() {  
 
 
       return adapter.getCount();   
     }  
     @Override    public Object instantiateItem(ViewGroup container, int position) { 
       int virtualPosition = position % getRealCount(); 
       debug("instantiateItem: real position: " + position); 
       debug("instantiateItem: virtual position: " + virtualPosition); 
       // only expose virtual position to the inner adapter 
       return adapter.instantiateItem(container, virtualPosition);
      @Override    public void destroyItem(ViewGroup container, int position, Object object) { 
       int virtualPosition = position % getRealCount(); 
       debug("destroyItem: real position: " + position);
        debug("destroyItem: virtual position: " + virtualPosition);
        // only expose virtual position to the inner adapter 
       adapter.destroyItem(container, virtualPosition, object);
    }  
  /*   
  * Delegate rest of methods directly to the inner adapter.  
   */    
@Override    public void finishUpdate(ViewGroup container) {      
  adapter.finishUpdate(container); 
   }   
 @Override    public boolean isViewFromObject(View view, Object object) {    
   return adapter.isViewFromObject(view, object); 
   }   
 @Override    public void restoreState(Parcelable bundle, ClassLoader classLoader) { 
       adapter.restoreState(bundle, classLoader);  
  }   
 @Override    public Parcelable saveState() {  
      return adapter.saveState();    
}    
@Override    public void startUpdate(ViewGroup container) { 
       adapter.startUpdate(container);  
  }  
  @Override    public CharSequence getPageTitle(int position) { 
       int virtualPosition = position % getRealCount();   
     return adapter.getPageTitle(virtualPosition);
    }   
 @Override    public float getPageWidth(int position) {  
      return adapter.getPageWidth(position);  
  }   
 @Override    public void setPrimaryItem(ViewGroup container, int position, Object object) {    
    adapter.setPrimaryItem(container, position, object);   
 }  
  @Override    public void unregisterDataSetObserver(DataSetObserver observer) { 
       adapter.unregisterDataSetObserver(observer); 
   }   
 @Override    public void registerDataSetObserver(DataSetObserver observer) { 
       adapter.registerDataSetObserver(observer);  
  }  
  @Override    public void notifyDataSetChanged() { 
       adapter.notifyDataSetChanged();
    }   
 @Override    public int getItemPosition(Object object) { 
       return adapter.getItemPosition(object);
    }   
 /*  
   * End delegation   
  */   
 private void debug(String message) { 
       if (DEBUG) { 
           Log.d(TAG, message);   
     }  
  }
}

从上可以看出:关键在于重写 getCount()函数,令它返回一个固定的足够大的整数,然后增加 getRealCount()方法,获取实际填充ViewPager的总数,之后就是将页面所处的虚拟位置的索引转换成实际位置的索引。
 
 
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">
</span>
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">
</span>
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">综上所述:实现循环滑动的原理就是在对ViewPager初始化的时候,给它填充固定数目的足够多的虚拟的页面空间,在视图渲染的时候利用虚拟位置对实际页面总数的求余实现虚拟位置与实际位置的转换。</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">
</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">举个例子:假设实际有4个页面需要滑动,编号为0,1,2,3. 令</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">getOffsetAmount()返回1000,那么0号页面的虚拟位置索引值就是1000,向右滑动3次,分别到达1001(1001%4=1号页面),1002(1002%4=2号页面),</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">1003(1003%4=3号页面),此时处于3号页面,若继续向右滑动,则到达1004(1004%4=0号页面),如此继续滑动下去,便可实现循环滑动的效果。向左滑动也是一样。</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">
</span>
使用方法:
</pre><pre name="code" class="java">1.在布局文件中嵌入<span style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; color: rgb(51, 51, 51); font-size: 14px; background-color: transparent;">InfiniteViewPager:</span>
<span style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; color: rgb(51, 51, 51); font-size: 14px; background-color: transparent;">
</span>
<span style="font-family:Consolas, Liberation Mono, Menlo, Courier, monospace;color:#333333;"><span style="font-size: 14px;"></span></span><pre name="code" class="html"><com.antonyt.infiniteviewpager.InfiniteViewPager
    android:id="@+id/pager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

2.给ViewPager设置适配器
<pre name="code" class="java">...
PagerAdapter wrappedAdapter = new InfinitePagerAdapter(adapter);
viewPager.setAdapter(wrappedAdapter);
...


 
 
项目github地址
 
 
 
 
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">



</span>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值