ViewPager的简介与使用
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="viewPager" type="id"></item>
</resources>
package com.huangfei.criminalintent;
import java.util.ArrayList;
import java.util.UUID;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
public class CrimePagerActivity extends FragmentActivity {
private ViewPager mViewPager;
private ArrayList<Crime> mCrimes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 以代码的方式创建视图并不奇怪,但我们还无法完全弃用XML文件,因为某些构件块依然需要资源ID。
* FragmentManager要求任何用作fragment容器的视图都必须具有资源ID。
* ViewPager是一个fragment容器,因此,必须赋予其资源ID。
*
* 以代码的方式创建视图,应完成以下任务项:
* 1、为ViewPager创建资源ID;
* 2、创建ViewPager实例并赋值给mViewPager
* 3、赋值资源ID给ViewPager,并对其进行配置;
* 4、设置ViewPager为activity的内容视图
*
* 定义独立资源ID与定义字符串资源ID并没有什么不同:在res/values目录下的XML文件中创建一个名为ids.xml的Android XML资源文件,
* 用以存储资源ID,并在其中新增一个名为viewPager的ID。
*/
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
mCrimes = CrimeLab.get(this).getCrimes();
FragmentManager fm = getSupportFragmentManager();
/**
* FragmentStatePagerAdapter与FragmentPagerAdapter的区别:
* FragmentStatePagerAdapter:使用FragmentStatePagerAdapter会销毁掉不需要的fragment。事务提交后。可将fragment从activity
* 的FragmentManager中彻底移除。因此FragmentStatePagerAdapter更节省内存。
*
* FragmentPagerAdapter:对于不需要的fragment,FragmentPagerAdapter则选择调用事务的detach(Fragment)方法,
* 而非remove(Fragment)方法。FragmentPagerAdapter只销毁了fragment的视图,但仍将fragment实例保留在FragmentManager中。
* 因此,FragmentPagerAdapter创建的fragment永远不会被销毁。
*
* 当有大量fragment实例时,可选择FragmentStatePagerAdapter;而当用户只需少量且固定的fragment时,则选择FragmentPagerAdapter。
*/
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
@Override
public int getCount() {
return mCrimes.size();
}
@Override
public Fragment getItem(int position) {
Crime crime = mCrimes.get(position);
return CrimeFragment.newInstance(crime.getId());
}
});
/**
* onPageScrolled(...)方法可告知我们页面将滑向哪里。
* onPageScrollStateChanged(...)方法可告知我们当前页面所处的行为状态,如正在被用户滑动、页面滑动入位到完全静止以及页面切换完成后的闲置状态。
* onPageSelected(...)方法可告知我们当前哪一个页面被选中。
*/
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Crime crime = mCrimes.get(position);
setTitle(crime.getTitle());
}
@Override
public void onPageScrolled(int position, float posOffset, int posOffsetPixels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
UUID crimeId = (UUID) getIntent().getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);
for (int i = 0; i < mCrimes.size(); i++) {
if(mCrimes.get(i).getId().equals(crimeId)){
mViewPager.setCurrentItem(i);
break;
}
}
}
}
ViewPager的工作原理
为什么选择使用ViewPager而不是AdapterView呢?AdapterView有一个用起来和ViewPager差不多的Gallery子类,为什么不用它呢?
由于无法使用现有的Fragment,在应用中,如使用AdapterView,则需要处理大量的内部实现工作。Adapter需要我们及时地提供View。然而,决定Fragment视图何时创建的是FragmentManager,而不是我们。所以,当Gallery要求提供Fragment视图时,我们无法立即创建Fragment视并提供其视图。
ViewPager使用的是PagerAdapter,而非原来的Adapter,PagerAdapter要比Adapter复杂的多,因为要处理更多的视图管理相关工作。PagerAdapter中代替可返回视图的getView(…)方法,采用了如下方法:
public Object instantiateItem(ViewGroup container, int position)
public void destroyItem(ViewGroup container, int position,Object object)
public boolean isViewFromObject(View view, Object object)
instantiateItem(ViewGroup, int)方法告诉PagerAdapter创建指定位置的列表项视图,然后将其添加ViewGroup给视图容器,而destroyItem(ViewGroup, int ,Object)方法则告诉PagerAdapter销毁已建视图。注意,instantiateItem(ViewGroup, int)方法对何时创建视图并无要求。因此,PagerAdapter可自行决定何时创建视图。
视图创建完成后,ViewPager会在某个时点注意它。为确定该视图所属的对象,ViewPager会调用isViewFromObject(View, Object)方法。这里,Object参数是instantiateItem(ViewGroup, int)方法返回的对象。因此,假设ViewPager调用instantiateItem(ViewGroup, 5)方法返回一个A对象,那么,只要传入参数是第5个对象的视图,isViewFromObject(View, Object)方法则应返回true,否则会返回false。
对ViewPager来说,这是一个复杂的过程,但对于PagerAdapter来说,这算不上什么,因为PagerAdapter只要能够创建、销毁视图以及识别视图来自那个对象即可。这样宽松的要求使得PagerAdapter能够比较自由地通过instantiateItem(ViewGroup, int)方法创建并添加新的Fragment,然后返回可以跟踪管理的FragmentObject。以下是isViewFromObject(View, Object)方法的具体实现。
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}