學習了 ViewPager俊柔,在此做個總結,主要包括一下幾個方面:
- ViewPager 簡介
- ViewPager 的使用
- FragmentStatePagerAdapter 和 FragmentPagerAdapter
- ViewPager 的工作原理
- 恢復 CrimeFragment 的邊距
- 添加 Jump to First 按鈕和 Jump to Last 按鈕
ViewPager 簡介
- ViewPager 是 android 擴展包 v4 包中的類,這個類可以讓用戶左右切換當前的 view
- ViewPager 直接繼承了 ViewGroup降传,所有它是一個容器類,可以在其中添加其他的 view 類。
- ViewPager 需要一個 PagerAdapter 適配器類給它提供數(shù)據(jù)搀暑。
- ViewPager 經(jīng)常和 Fragment 一起使用,并且提供了專門的 FragmentPagerAdapter 和 FragmentStatePagerAdapter 類供 Fragment 中的 ViewPager 使用跨琳。
ViewPager 的使用
因為之前的封裝自点,CrimeFragment 類可以不做修改直接使用。接下來看我們需要完成的任務脉让。
- 創(chuàng)建 CrimePagerActivity 類
- 在 CrimePagerActivity 類中關聯(lián)使用 ViewPager 及其 Adapter
- 修改 CrimeHolder.onClick(...) 方法桂敛,轉而啟動 CrimePagerActivity
創(chuàng)建 CrimePagerActivity 類
CrimePagerActivity.java
public class CrimePagerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime_pager);
}
布局文件
activity_crime_pager.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/crime_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CrimePagerActivity">
</android.support.v4.view.ViewPager>
在 CrimePagerActivity 類中關聯(lián)使用 ViewPager 及其 Adapter
ViewPager 某種程度上與 RecyclerView 類似,都需要借助 Adapter 提供視圖溅潜。因為 ViewPager 與 PagerAdapter 間的配合要復雜很多术唬,這里先用 FragmentPagerAdapter,它能處理很多細節(jié)的問題
FragmentPagerAdapter 提供了兩個有用的方法:getCount() 和 getItem(int)滚澜。調用 getItem(int) 方法粗仓,獲取并顯示 crime 數(shù)組中指定位置的 Crime 時,它會返回配置過的 CrimeFragment 來顯示指定的 Crime设捐。
CrimePagerActivity.java
public class CrimePagerActivity extends AppCompatActivity {
private ViewPager mViewPager;
private List<Crime> mCrimes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime_pager);
mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
mCrimes =CrimeLab.get(this).getCrimes();
FragmentManager fragmentManager = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int position) {
Crime crime = mCrimes.get(position);
return CrimeFragment.newInstance(crime.getId());
}
@Override
public int getCount() {
return mCrimes.size();
}
});
解釋一下上面的代碼借浊,在 activity 視圖中找到 ViewPager 后,我們從 CrimeLab 獲取數(shù)據(jù)集萝招,然后獲取 FragmentManager 的實例蚂斤。接下來,設置 adapter 為 FragmentStatePagerAdapter 的一個匿名實例槐沼。創(chuàng)建 FragmentStatePagerAdapter 需要 FragmentManager曙蒸。如前所述,qiansuoshuFragmentStatePagerAdapter 是我們的代理岗钩,代理首先將 getItem(int) 方法返回的 fragment 添加給 activity纽窟,然后才能使用 fragment 完成自己的工作。
代理究竟做了哪些工作呢兼吓?簡單來說就是將返回的 fragment 添加給托管 activity师倔,并幫助 ViewPager 找到 fragment 的視圖一一對應。getItem(int) 方法首先獲取指定位置的 Crime 實例周蹭,然后利用該 Crime 實例的 ID 創(chuàng)建并返回一個經(jīng)過有效配置發(fā) CrimeFragment趋艘。
修改 CrimeHolder.onClick(...) 方法,轉而啟動 CrimePagerActivity
CrimePagerActivity.java
private static final String EXTRA_CRIME_ID = "com.bignerdranch.android.criminalintent.crime_id";
private ViewPager mViewPager;
private List<Crime> mCrimes;
public static Intent newIntent(Context packageContext, UUID crimeId){
Intent intent = new Intent(packageContext,CrimePagerActivity.class);
intent.putExtra(EXTRA_CRIME_ID,crimeId);
return intent;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime_pager);
UUID crimeId = (UUID)getIntent().getSerializableExtra(EXTRA_CRIME_ID) ;
...
}
修改 CrimeHolder.onClick(...) 方法凶朗,轉而啟動 CrimePagerActivity
CrimePagerActivity.java
@Override
public void onClick(View view) {
// Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId());
Intent intent = CrimePagerActivity.newIntent(getActivity(),mCrime.getId());
mPosition = this.getAdapterPosition();
startActivity(intent);
}
最后在 AndroidManifest.xml中刪除 CrimeActivity 的代碼瓷胧。
注意,目前 ViewPager 還不完美棚愤,ViewPager 默認只顯示 PagerAdapter 中的第一個列表項搓萧。要顯示選中的列表項需要在 CrimePagerActivity.onCreate(Bundle) 末尾添加以下代碼杂数。
CrimePagerActivity.java
for (int i = 0; i < mCrimes.size(); i++){
if (mCrimes.get(i).getId().equals(crimeId)){
mViewPager.setCurrentItem(i);
break;
}
}
FragmentStatePagerAdapter 和 FragmentPagerAdapter
FragmentPagerAdapter 是另一種可用的 PagerAdapter,其用法和 FragmentStatePagerAdapter 基本一致瘸洛,只是在卸載不需要的 fragment 時揍移,各自采用的處理方法不同。
FragmentStatePagerAdapter 會銷毀不需要的 fragment反肋,而 FragmentPagerAdapter 是調用 detach(Fragment) 方法來處理它那伐,只是銷毀了 fragment 的視圖,而 fragment 的實例由 FragmentManager 維護石蔗,因此罕邀,F(xiàn)ragmentPagerAdapter 創(chuàng)建的 fragment 永遠不會被銷毀。
所以當數(shù)據(jù)量大時养距,可以選擇 FragmentStatePagerAdapter诉探,用戶界面只有少量固定的 fragment 時,可以選擇 FragmentPagerAdapter棍厌。
ViewPager 的工作原理
首先明確一點肾胯,要實現(xiàn)自己的 PagerAdapter 接口時,就需要了解它的工作原理耘纱,那么什么時候需要實現(xiàn) PagerAdapter 接口呢敬肚?當需要托管非 fragment 視圖時(如圖片),就需要實現(xiàn)原生的 PagerAdapter揣炕。
為什么使用 ViewPager而不是 RecyclerView?
Adapter 需要我們及時提供 View东跪。然而畸陡,覺得 fragment 創(chuàng)建的是 FragmentManager。因此虽填,當 RecyclerView 要求 Adapter 提供 fragment 視圖時丁恭。我們無法立即創(chuàng)建 fragment 并提供其視圖。這就是 PagerView 存在的理由斋日。
下面看它的內部實現(xiàn)牲览。
PagerAdapter 不使用可返回視圖的 onBindViewHolder(...) 方法,而是使用以下方法:
public Object instantiateItem(ViewGroup container,int position)
public void destroyItem(ViewGroup container,int position,Object object)
public abstract boolean isViewFromObject(View view,Object object)
instantiateItem(ViewGroup,int) 方法是告訴 pager adapter 創(chuàng)建指定位置的列表項視圖恶守,但并不要求立即創(chuàng)建視圖第献,pager adpter 可以自己決定何時創(chuàng)建視圖。然后將其添加給 ViewGroup兔港,而 destroyItem(ViewGroup,int,Object) 方法則是告訴 pager adapter 視圖已經(jīng)銷毀(FragmentStatePagerAdapter 和 FragmentPagerAdapter 的不同主要是在這里)庸毫。
詳情可以參考此鏈接:
ViewPager 全面剖析及使用詳解
視圖創(chuàng)建后,ViewPager 會在某個時間點看到它衫樊,為了確定該視圖所屬的對象飒赃,ViewPager 會調用 isViewFromObject(View,Object)利花,這里的 object 是 instantiateItem(ViewGroup,int) 方法返回的對象。
恢復 CrimeFragment 的邊距
把 fragment_crime.xml 下的 LinearLayout 的 android:layout_margin="16dp" 改成 android:padding="16dp"载佳。