來自: 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一樣:地址 .