ViewPager 使用

学习了 ViewPager,在此做个总结,主要包括一下几个方面:

  • ViewPager 简介
  • ViewPager 的使用
  • FragmentStatePagerAdapter 和 FragmentPagerAdapter
  • ViewPager 的工作原理
  • 恢复 CrimeFragment 的边距
  • 添加 Jump to First 按钮和 Jump to Last 按钮

ViewPager 简介

  1. ViewPager 是 android 扩展包 v4 包中的类,这个类可以让用户左右切换当前的 view
  2. ViewPager 直接继承了 ViewGroup,所有它是一个容器类,可以在其中添加其他的 view 类。
  3. ViewPager 需要一个 PagerAdapter 适配器类给它提供数据。
  4. ViewPager 经常和 Fragment 一起使用,并且提供了专门的 FragmentPagerAdapter 和 FragmentStatePagerAdapter 类供 Fragment 中的 ViewPager 使用。

ViewPager 的使用

因为之前的封装,CrimeFragment 类可以不做修改直接使用。接下来看我们需要完成的任务。

  1. 创建 CrimePagerActivity 类
  2. 在 CrimePagerActivity 类中关联使用 ViewPager 及其 Adapter
  3. 修改 CrimeHolder.onClick(...) 方法,转而启动 CrimePagerActivity

创建 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 类中关联使用 ViewPager 及其 Adapter

ViewPager 某种程度上与 RecyclerView 类似,都需要借助 Adapter 提供视图。因为 ViewPager 与 PagerAdapter 间的配合要复杂很多,这里先用 FragmentPagerAdapter,它能处理很多细节的问题

FragmentPagerAdapter 提供了两个有用的方法:getCount() 和 getItem(int)。调用 getItem(int) 方法,获取并显示 crime 数组中指定位置的 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 获取数据集,然后获取 FragmentManager 的实例。接下来,设置 adapter 为 FragmentStatePagerAdapter 的一个匿名实例。创建 FragmentStatePagerAdapter 需要 FragmentManager。如前所述,qiansuoshuFragmentStatePagerAdapter 是我们的代理,代理首先将 getItem(int) 方法返回的 fragment 添加给 activity,然后才能使用 fragment 完成自己的工作。

代理究竟做了哪些工作呢?简单来说就是将返回的 fragment 添加给托管 activity,并帮助 ViewPager 找到 fragment 的视图一一对应。getItem(int) 方法首先获取指定位置的 Crime 实例,然后利用该 Crime 实例的 ID 创建并返回一个经过有效配置发 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 维护,因此,FragmentPagerAdapter 创建的 fragment 永远不会被销毁。

所以当数据量大时,可以选择 FragmentStatePagerAdapter,用户界面只有少量固定的 fragment 时,可以选择 FragmentPagerAdapter。

ViewPager 的工作原理

首先明确一点,要实现自己的 PagerAdapter 接口时,就需要了解它的工作原理,那么什么时候需要实现 PagerAdapter 接口呢?当需要托管非 fragment 视图时(如图片),就需要实现原生的 PagerAdapter。

为什么使用 ViewPager而不是 RecyclerView?

Adapter 需要我们及时提供 View。然而,觉得 fragment 创建的是 FragmentManager。因此,当 RecyclerView 要求 Adapter 提供 fragment 视图时。我们无法立即创建 fragment 并提供其视图。这就是 PagerView 存在的理由。

下面看它的内部实现。

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 创建指定位置的列表项视图,但并不要求立即创建视图,pager adpter 可以自己决定何时创建视图。然后将其添加给 ViewGroup,而 destroyItem(ViewGroup,int,Object) 方法则是告诉 pager adapter 视图已经销毁(FragmentStatePagerAdapter 和 FragmentPagerAdapter 的不同主要是在这里)。

详情可以参考此链接:
ViewPager 全面剖析及使用详解

视图创建后,ViewPager 会在某个时间点看到它,为了确定该视图所属的对象,ViewPager 会调用 isViewFromObject(View,Object),这里的 object 是 instantiateItem(ViewGroup,int) 方法返回的对象。

恢复 CrimeFragment 的边距

把 fragment_crime.xml 下的 LinearLayout 的 android:layout_margin="16dp" 改成 android:padding="16dp"。

GitHub地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容