来自: http://hiqianjin.com/blog/474
update 8/22 这个文章过时了,关于viewPager动态加载的请看我另一篇文章,这个文章中调整viewpager的滑动速度的代码还是可行的,关于动态加载,请看:http://hiqianjin.com/blog/516
最近用到一个效果,先有一个数据列表,然后,从这个列表中的任意一个位置进去,然后,点击“上一条”,“下一条”进行数据加载,我用了viewPager进行加载,为了使前后加载都有动画效果,且viewPager加载到一定数量的数据后就删除一些前后的数据(向前加载的时候就不断删后面的数据,向后反之)。
在viewPagerAdapter中,数据用了arrayList来封装,当向前的时候,我在arrayList的0位置,也就是最前面加载一条数据,然后再让adapter.notifyDataSetChange(),这样数据就加载到里面去了,关键是,当你处于viewPager index=0的时候,向前加载一条数据,然后,viewPager.setCurrentIndex(currenIndex – 1);目前,CurrenIndex = 0 ,-1后=-1,这样是可以移动到向前加载的那一页的,但是没有滑动的效果,为了让它用效果,用了以下方法,先把viewPager的页面移到加载后的第2页,也就是加载前的第一页,这次移动不要用动画,第二次移动,用上动画,在当前这个第二页向前移动:
2 | vp.setCurrentItem(index+loadDataCount, false ); |
3 | vp.setCurrentItem(index+loadDataCount - 1 , true ); |
上面是向前加载的,麻烦一点,向后加载就简单了,直接在arrayList里加载一条数据,然后:
2 | vp.setCurrentItem(index+loadDataCount, false ); |
主要的思路就是:当viewPager向前加载到快要到index=0的时候就去加载几条数据,要么到index=0的时候再去加载也行,向后一样的道理,关键是移动的动画处理。
—————————————————————————————————————————————–
DEMO完成后,一次加载多页又不适用于我现有的项目,我又做成了viewPager中只会存在一条数据,当每次加载完,滑动到下一条数据后,就马上把上一条删掉,这下代码就简单了,大概如下:
向前加载:
02 | list.add( 0 ,yourDataBean) |
04 | viewPagerAdapter.notifyDataSetChanged(); |
05 | viewPager.setCurrentItem(index+ 1 , false ); |
06 | viewPager.setCurrentItem(index, true ); |
07 | new Handler().postDelayed( new Runnable() { |
10 | pagerViews.remove(pagerViews.size()- 1 ); |
11 | viewPagerAdapter.notifyDataSetChanged(); |
向后加载:
02 | list.add(yourDataBean); |
03 | viewPagerAdapter.notifyDataSetChanged(); |
04 | viewPager.setCurrentItem(index+ 1 , true ); |
05 | new Handler().postDelayed( new Runnable() { |
09 | viewPagerAdapter.notifyDataSetChanged(); |
这儿延迟500毫秒是因为我的viewPager的滑动时间我自定义为了400毫秒,如何自定义viewPager的滑动速度如下:
新建一个滑动处理类
01 | import android.content.Context; |
02 | import android.view.animation.Interpolator; |
03 | import android.widget.Scroller; |
05 | public class FixedSpeedScroller extends Scroller { |
07 | private int mDuration = 2000 ; |
09 | public FixedSpeedScroller(Context context) { |
13 | public FixedSpeedScroller(Context context, Interpolator interpolator) { |
14 | super (context, interpolator); |
18 | public void startScroll( int startX, int startY, int dx, int dy, int duration) { |
20 | super .startScroll(startX, startY, dx, dy, mDuration); |
24 | public void startScroll( int startX, int startY, int dx, int dy) { |
26 | super .startScroll(startX, startY, dx, dy, mDuration); |
29 | public void setmDuration( int time) { |
33 | public int getmDuration() { |
然后在你用到viewPager的地方用JAVA的反射来调整viewPager的滑动速度:
03 | Field mField = ViewPager. class .getDeclaredField( "mScroller" ); |
04 | mField.setAccessible( true ); |
05 | FixedSpeedScroller mScroller = |
06 | new FixedSpeedScroller(viewPager.getContext(), new AccelerateInterpolator()); |
08 | mScroller.setmDuration( 400 ); |
09 | mField.set(viewPager, mScroller); |
10 | } catch (Exception e) { |
主要是滑动效果的处理,折腾了好久,有还是不明白的同学可以email我。
以前写过一次这个文章,在这儿,直到上周末,做一个图片应用的时候,发现以前出现了方向性错误,导致可能误导了一些人,大家看到以前那篇文章的时候,估计没有几个人能看明白是怎么回事儿,在动态向前向后加载的时候,有不少的小逻辑判断,极容易出错,而且不断的设置setCurrentItem(),在临界的时候,其实用了一个先把viewPager设置为无动画,跳转一次位置,再设置viewPager有动画,再次跳转位置,模拟有动画的效果,其实在这个操作过程中仔细看,能看出来page的变化的,效果不好。
好了,扯了这么大一通,从上面一段就可以看出来以前写得不简单了,下面是新的思路,效果和逻辑都很简单:
再来回顾一下这个功能的应用场景:有一个装有很多条数据的一个list,这个list在listView中显示出来了,现在滑动listView随便到一个位置,点击一个item进入数据的详情页,在这个详情页,我们用到了viewPager,让它实现左右滑动的效果。
以前我也是在网上找的DEMO,大家通常的做法是,一进入详情页,就把每个页面的view初始化出来,然后把这些view放在list里面,传给自己定义的viewPagerAdapter,这样就出现滑动效果了。但是这样就会出现一个问题,就是我的list太大,初始化出来的view页面就会很多,比如说1000条数据,不可能全初始化出view来放list里再传给adapter吧,这样八成OOM的,于是我就照着这个思路,先初始化一部分view,再不断的滑动过程中,再初始化一部分,但是如果你是从数据列表的中间进入,向前滑动时不断的初始化view添加进list,这样viewPagerAdapter的大小就会发生变化,你的currentIndex就在不断的变化。超级不好控制。
再回想一下我们平时做一些常见的adapter的做法,没有在activity中把所有的item初始化出来,再传给adapter,让adapter做显示吧,常用的做法是,在adapter里再进行数据的封装什么的,在adapter中的getView()中进行操作,无论是自定义界面,还是填充数据。其实,viewPager的PagerAdapter也是同样的思路。回想一下,其实就是基本的adapter的操作了,以前刚学习这个控件的时候被网上别人的demo误导了。
01 | public class DemoAdapter extends PagerAdapter { |
03 | private Context context; |
04 | private ArrayList list; |
05 | private LayoutInflater inflater; |
07 | public DemoAdapter (Context context, ArrayList list) { |
09 | this .context = context; |
10 | inflater = LayoutInflater.from(context); |
15 | public void destroyItem(ViewGroup container, int position, Object object) { |
16 | ((ViewPager) container).removeView((View) object); |
20 | public void finishUpdate(View container) {} |
23 | public int getCount() { |
24 | return list != null ? list.size() : 0 ; |
28 | public Object instantiateItem(ViewGroup view, int position) { |
30 | UserBean bean = list.get(position); |
33 | View userLayout = inflater.inflate(R.layout.show_user_detail, view, false ); |
34 | ImageView ivPhoto = (ImageView) userLayout.findViewById(R.id.user_photo); |
35 | TextView tvName = (TextView) userLayout.findViewById(R.id.user_name); |
38 | tvName.setText(bean.getUserName); |
40 | ((ViewPager) view).addView(userLayout, 0 ); |
45 | public boolean isViewFromObject(View view, Object object) { |
46 | return view.equals(object); |
50 | public void restoreState(Parcelable state, ClassLoader loader) {} |
53 | public Parcelable saveState() { |
58 | public void startUpdate(View container) {} |
看完上面这段代码是不是很简单,还要注意一点就是,你在外面的listView从哪儿跳进viewPager的时候,要在viewPager设置完adapter的时候setCurretntIndex一下,就像下面这样:
1 | viewPager.setCurrentItem(intoPosition) |
我还做了个简单的DEMO放在Github上,超级简单,其实就是跟平常用adapter一样:地址 .