Android开发学习教程(27)- ViewPager+Fragment+FragmentPagerAdapter+FragmentStatePagerAdapter

0 1,035

—— 我从未见过一个早起勤奋谨慎诚实的人抱怨命运不好,良好的品格,坚强的意志,是不会被所谓的命运击败的。

ViewPager是什么

ViewPager是一个布局管理器,用户通过左右移动来滑动页面,其中每个页面使用Fragment而不是使用Activity。它还可以用在用户首次启动应用程序时的引导页。

ViewPager的用法

实现viewpager的步骤:

1. 将 ViewPager 小部件添加到 XML 布局中。

2. 通过扩展 FragmentPagerAdapter 或 FragmentStatePagerAdapter 类来创建适配器。

适配器用来填充 Viewpager 内的页面。PagerAdapter 是由 FragmentPagerAdapter 和 FragmentStatePagerAdapter 扩展的基类,让我们看一下这两个类之间的区别。

FragmentPagerAdapter 和 FragmentStatePagerAdapter 的区别:

(1)fragments对象的处理:FragmentPagerAdapter范围外fragments会保存在内存中(detach),但是fragment对应的View会被销毁;FragmentStatePagerAdapter范围外fragments不会保存在内存中(remove),View也会被销毁。

(2)状态的处理:FragmentPagerAdapter范围外fragments对应的SavedState会保存;FragmentStatePagerAdapter只保存范围内fragments对应的SavedState。这个SavedState在Fragment的生命周期回调中供外部传参数,和Activity类似。

(3)适用场景:相同数量的fragments,FragmentPagerAdapter内存较大,但页面切换更友好;FragmentStatePagerAdapter内存占用少,页面切换稍差。因此FragmentPagerAdapter适用于Fragment数量少的情况,FragmentStatePagerAdapter适用于Fragment数量多的情况。

FragmentPagerAdapter的用法

第 1 步:将 ViewPager 小部件添加到 XML 布局中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="fill"
        app:tabMode="fixed" />
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

第 2 步:创建Fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class Page1Fragment extends Fragment {
    public Page1Fragment() {
        // required empty public constructor.
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_page1, container, false);
    }
}

fragment_page1.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0F9D58"
    tools:context=".Page1Fragment">
    
    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Page 1"
        android:textColor="@color/white"
        android:textSize="60sp"
        android:textStyle="bold" />
</FrameLayout>

和上面一样再分别新建Page2Fragment、fragment_page2.xml、Page3Fragment、fragment_page3.xml

第 3 步:创建 ViewPager 适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.fragment.app.Fragment;
  import androidx.fragment.app.FragmentManager;
  import androidx.fragment.app.FragmentPagerAdapter;
  import java.util.ArrayList;
  import java.util.List;
  public class ViewPagerAdapter extends FragmentPagerAdapter {
      private final List<fragment> fragments = new ArrayList<>();
      private final List<string> fragmentTitle = new ArrayList<>();
      public ViewPagerAdapter(@NonNull FragmentManager fm)
      {
          super(fm);
      }
      public void add(Fragment fragment, String title)
      {
          fragments.add(fragment);
          fragmentTitle.add(title);
      }
      @NonNull @Override public Fragment getItem(int position)
      {
          return fragments.get(position);
      }
      @Override public int getCount()
      {
          return fragments.size();
      }
      @Nullable
      @Override
      public CharSequence getPageTitle(int position)
      {
          return fragmentTitle.get(position);
      }
  }
</string></fragment>

方法说明:

1
2
3
4
5
6
getCount():此方法返回要显示的片段数。(需要覆盖)
getItem(int pos):返回 pos 索引处的片段。(需要覆盖)
ViewPagerAdapter(@NonNull FragmentManager FM):(必需)ViewPager Adapter 需要有一个接受 FragmentManager 实例的参数化构造函数。它负责管理片段。FragmentManager 管理 Android 中的 Fragment,具体来说,它处理 Fragment 之间的事务。事务是一种添加、替换或删除片段的方法。
getPageTitle(int pos):(可选)与 getItem() 类似,此方法返回索引 pos 处的页面标题。
add(Fragment fragment, String title):此方法负责填充片段和片段标题列表。分别持有片段和标题。

第 4 步:ViewPager设置适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
public class MainActivity extends AppCompatActivity {
    
    private ViewPagerAdapter viewPagerAdapter;
    private ViewPager viewPager;
    private TabLayout tabLayout;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager = findViewById(R.id.viewpager);
        // 实例化适配器
        viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPagerAdapter.add(new Page1Fragment(), "Page 1");
        viewPagerAdapter.add(new Page2Fragment(), "Page 2");
        viewPagerAdapter.add(new Page3Fragment(), "Page 3");
        // viewPager设置适配器
        viewPager.setAdapter(viewPagerAdapter);
        tabLayout = findViewById(R.id.tab_layout);
        // 使用setupWithiewPager方法将 TabLayout 链接到 Viewpager
        tabLayout.setupWithViewPager(viewPager);
    }
}

Android开发学习教程(27)- ViewPager+Fragment+FragmentPagerAdapter+FragmentStatePagerAdapter

Android开发学习教程(27)- ViewPager+Fragment+FragmentPagerAdapter+FragmentStatePagerAdapter

FragmentStatePagerAdapter的用法

其它步骤和上面一样,这里Fragment我们使用同一个,只是针对不同的tab设置不同的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class DynamicFragment extends Fragment {
    public static DynamicFragment newInstance(String arg) {
        Bundle bundle = new Bundle();
        bundle.putString("arg", arg);
        DynamicFragment fragment = new DynamicFragment();
        fragment.setArguments(bundle);
        return fragment;
    }
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_page, container, false);
    }
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ((TextView) getView().findViewById(R.id.tv)).setText("Page" + getArguments().getString("arg"));
    }
}

适配器部分和上面的ViewPagerAdapter内容一样,区别只是继承的是 FragmentStatePagerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  import java.util.ArrayList;
  import java.util.List;
  import androidx.annotation.Nullable;
  import androidx.fragment.app.Fragment;
  import androidx.fragment.app.FragmentManager;
  import androidx.fragment.app.FragmentStatePagerAdapter;
  public class DynamicFragmentAdapter extends FragmentStatePagerAdapter {
      private final List<fragment> fragments = new ArrayList<>();
      private final List<string> fragmentTitle = new ArrayList<>();
      public DynamicFragmentAdapter(FragmentManager fm) {
          super(fm);
      }
      public void add(Fragment fragment, String title) {
          fragments.add(fragment);
          fragmentTitle.add(title);
      }
      // get the current item with position number
      @Override
      public Fragment getItem(int position) {
          return fragments.get(position);
      }
      // get total number of tabs
      @Override
      public int getCount() {
          return fragmentTitle.size();
      }
      @Nullable
      @Override
      public CharSequence getPageTitle(int position) {
          return fragmentTitle.get(position);
      }
  }
</string></fragment>

MainActivity也只是实例化适配器时改变了下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MainActivity2 extends AppCompatActivity {
    private DynamicFragmentAdapter dynamicFragmentAdapter;
    private ViewPager viewPager;
    private TabLayout tabLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main1);
        viewPager = findViewById(R.id.viewpager);
        // 实例化适配器
        dynamicFragmentAdapter = new DynamicFragmentAdapter(getSupportFragmentManager());
        dynamicFragmentAdapter.add(DynamicFragment.newInstance("1"), "PAGE 1");
        dynamicFragmentAdapter.add(DynamicFragment.newInstance("2"), "PAGE 2");
        dynamicFragmentAdapter.add(DynamicFragment.newInstance("3"), "PAGE 3");
        // viewPager设置适配器
        viewPager.setAdapter(dynamicFragmentAdapter);
        tabLayout = findViewById(R.id.tab_layout);
        // 使用setupWithiewPager方法将 TabLayout 链接到 Viewpager
        tabLayout.setupWithViewPager(viewPager);
    }
}

FragmentPagerAdapter和FragmentStatePagerAdapter不同的根本原因

任何不同的根本原因都可以从源码中找到,我们来看下从源码的角度两者有何不同

Ctrl+鼠标点击跳转到源码内部,我们发现,两者都是继承自PagerAdapter,整体代码非常简短,只有200行,里面有两个方法instantiateItem和destroyItem

FragmentPagerAdapter instantiateItem方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    final long itemId = getItemId(position);
    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }
    return fragment;
}

此方法用来为每一个页面创建页面(这里也就是Fragment了),源码其实很简单,先获取FragmentManager事务对象,然后用findFragmentByTag查找Fragment,找到了就attach上去,没找到就调用getItem方法获取定义的Fragment然后add上去。

FragmentStatePagerAdapter instantiateItem方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
    // If we already have this item instantiated, there is nothing
    // to do.  This can happen when we are restoring the entire pager
    // from its saved state, where the fragment manager has already
    // taken care of restoring the fragments we previously had instantiated.
    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    Fragment fragment = getItem(position);
    if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
    if (mSavedState.size() > position) {
        Fragment.SavedState fss = mSavedState.get(position);
        if (fss != null) {
            fragment.setInitialSavedState(fss);
        }
    }
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
    mFragments.set(position, fragment);
    mCurTransaction.add(container.getId(), fragment);
    return fragment;
}

此方法一样用来为每一个页面创建页面,源码同样也很简单,先在mFragments集合中查找有无对应页面的Fragmet,刚开始mFragments肯定是0,继续就往下执行,获取FragmentManager事务对象,然后用调用getItem方法获取定义的Fragment然后add上去。

到这里,首先就有个很明显的区别,上面需要先经过findFragmentByTag而这里是直接从mFragments查找有无对应页面的Fragmet,效率肯定快多了吧,至于为什么这里可以直接从mFragments查找,继续往下看

FragmentPagerAdapter destroyItem方法:

1
2
3
4
5
6
7
8
9
10
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
            " v=" + ((Fragment)object).getView());
    mCurTransaction.detach((Fragment)object);
}

很简单,就是detach掉某个Fragment页面

FragmentStatePagerAdapter destroyItem方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    Fragment fragment = (Fragment) object;
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
            " v=" + ((Fragment)object).getView());
    while (mSavedState.size() <= position) {
        mSavedState.add(null);
    }
    mSavedState.set(position, fragment.isAdded()
            ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
    mFragments.set(position, null);
    mCurTransaction.remove(fragment);
}

这里是将mFragments对应位置设为NULL,而且从FragmentManager中remove掉Fragment。这里也解释了上面为什么可以直接从mFragments查找而不必再使用findFragmentByTag去查找Fragment了,因为这里将mFragments对应位置设为NULL了。另一方面,这里直接从FragmentManager中remove掉Fragment了,所以FragmentManager在当前内存中不会缓存3个以上的Fragment(假设读者使用默认的mOffscreenPageLimit),这也是为什么FragmentStatePagerAdapter适用于页面数量多的情形了。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章资源,如无特殊说明或标注,均为本站网友和创作者贡献分享。如若本站内容侵犯了原著者的合法权益,可联系网站客服QQ2743319061删除。

云炬星球 安卓教程 Android开发学习教程(27)- ViewPager+Fragment+FragmentPagerAdapter+FragmentStatePagerAdapter https://src.yunjunet.cn/876823.html

常见问题
  • 放心亲,我们不会为了几十块钱的东西坏了名声!
查看详情
  • 方法一:点击“立即下载.”按钮,付款后在下载弹窗的虚线框的隐藏信息里获取 方法二:在正文底部使用VIP查看隐藏的解压密码 方法三:联系【云炬网络】公众号客服获取
查看详情
  • 付款后会出现“立即下载”按钮(点击即可下载),如果下载失败也可以联系客服发订单截图补发。
查看详情
  • 登录购买会多端同步购买记录,永久可以查看反复下载;非登录购买仅将购买记录保存到本地浏览器中,浏览器cookie清除后无法再次下载。先右上角点登录,然后点击微信图标可以快速授权注册登录^_^
查看详情
  • 可以试看。点击”查看演示“或“试看预览”按钮可以试读从资料目录中节选的部分内容,也可以自己指定想试看的内容。
查看详情
  • 原因一:本站所有资源已开启有效性检测(服务器24h全自动监测),当监测到下载链接无法访问时会提示“该资源已失效,请勿购买”,遇到这种情况可以联系客服修复失效的下载链接,或直接联系客服在淘宝下单购买即可。(检测原理:购买前服务器程序会预访问下载链接,响应值为200说明资源有效允许购买,响应值为404或502等报错说明资源失效禁止购买)。原因二:上传者未启用“下载”选项。
查看详情
官方客服团队

为您解决烦忧 - 24小时在线 专业服务