2014-8-12阅读191
评论0来自:
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页,也就是加载前的第一页,这次移动不要用动画,第二次移动,用上动画,在当前这个第二页向前移动:
1 | //这儿是关键,要不没有跳转效果
这是一次加载两条数据的情况 |
2 | vp.setCurrentItem(index+loadDataCount, false ); |
3 | vp.setCurrentItem(index+loadDataCount
- 1 , true ); |
上面是向前加载的,麻烦一点,向后加载就简单了,直接在arrayList里加载一条数据,然后:
1 | //index
= vp.getCurrentItem(); |
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)
{ |
19 | //
Ignore received duration, use fixed one instead |
20 | super .startScroll(startX, startY,dx,dy,mDuration); |
24 | public void startScroll( int startX, int startY, int dx, int dy)
{ |
25 | //
Ignore received duration, use fixed one instead |
26 | super .startScroll(startX, startY,dx,dy,mDuration); |
29 | public void setmDuration( int time)
{ |
33 | public int getmDuration()
{ |
然后在你用到viewPager的地方用JAVA的反射来调整viewPager的滑动速度:
view
source
print?01 | //
use reflect change duration |
03 | Field
mField = ViewPager. class .getDeclaredField( "mScroller" ); |
04 | mField.setAccessible( true ); |
05 | FixedSpeedScroller
mScroller = |
06 | new FixedSpeedScroller(viewPager.getContext(), new AccelerateInterpolator()); |
07 | //
可以用setDuration的方式调整速率 |
08 | mScroller.setmDuration( 400 ); |
09 | mField.set(viewPager,
mScroller); |
主要是滑动效果的处理,折腾了好久,有还是不明白的同学可以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一下,就像下面这样:
view
source
print?1 | viewPager.setCurrentItem(intoPosition) |
我还做了个简单的DEMO放在Github上,超级简单,其实就是跟平常用adapter一样:
地址 .