一. 继承
1 2 3 4
| java.lang.Object ↳android.view.View ↳android.view.ViewGroup ↳androidx.viewpager.widget.ViewPager
|
ViewPager 可以让用户左右切换当前的 View。它之前是android.support.v4
里面的类,现在归为到了androidx.viewpager.widget
里面。
ViewPager 需要一个PagerAdapter适配器来提供数据。
ViewPager 经常和 Fragment 一起使用,并且提供了专门的 FragmentAdapter 和 FragmentStateAdapter 适配器。
1. 方法讲解
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
| viewPager.setAdapter(new PagerAdapter() { @Override public int getCount() { return 0; } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return false; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { return super.instantiateItem(container, position); } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { super.destroyItem(container, position, object); } });
|
① 对 boolean isViewFromObject(View view, Object object) 方法的理解
首先明确,ViewPager里面对每个页面的管理是key-value形式的,也就是说每个page都有个对应的id(id是object类型),需要对page操作的时候都是通过id来完成的。
Object instantiateItem(ViewGroup container, int position) 方法就是用来往PageView里添加自己需要的page。同时注意它还有个返回值object,这就是那个id。
最后,boolean isViewFromObject(View view, Object object) 就是告诉框架,这个view的id是不是这个object。所以,该方法一般返回 view == object
。
② 对 Object instantiateItem(@NonNull ViewGroup container, int position) 方法的理解
- ViewPager会预加载子页面,在预加载时会自动调用这个方法。虽然一共有几个子页面已经知道了,但这些页面并未创建出来。
我们要在这个方法中创建子页面 并且将要展示的内容添加到子页面中
当ViewPage第一次加载时会执行这个函数并且执行两次,第一次执行加载第0页面, 第二次执行加载第1页。
当从第0页滑动到第1页后 会触发这个函数 预加载第2页,当从第1页滑到第2页后 会触发这个函数 预加载第3页,以类此推。同理,反向滑动也是一样的。
③ 对 void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) 方法的理解
这个方法用来销毁某个子页面释放资源。
当刚进入 ViewPager 时 这时已经加载了 0号页与1号页,此时用户处于0号页 用户可能还要滑到1号页 此时哪个页面都不会销毁。
当用户从0号页滑到1号页 这时已加载的页面时 0、1、2 页,此时用户即可能滑到 0号页 也可能滑到 2号页 所以也不会触发这个函数。
当用户从 1号页 滑到 2号页 此时已加载的有 0、1、2、3 页,用户处于 2号页 不可能会直接滑到0号页 所以,此时会触发这个函数来销毁 0号页。
④ ViewPager 默认加载两个页面,能不能自己设置加载的页面数量?
设置预加载的页面数量,默认是1。但是就算设置 0,源码里面也会把你重新设为1。这里面说的数量是屏幕之外加载的数量。
1
| public void setOffscreenPageLimit(int limit);
|
⑤ 设置当前显示哪一个页面
1 2 3
| public void setCurrentItem(int item);
public void setCurrentItem(int item, boolean smoothScroll)
|
item:控制显示哪一个页面。
smoothScroll:true 换页速度慢一点,平滑一点。false相反。
2. 简单的使用
① 布局文件
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 42 43 44 45 46 47
|
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" />
<TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignBottom="@+id/imageView" android:background="#33000000" android:gravity="center" android:paddingTop="5dp" android:paddingBottom="5dp" android:textStyle="bold" android:textColor="#ffffff" />
</RelativeLayout>
|
② 主要的代码
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
final ViewPager viewPager = findViewById(R.id.view_pager);
int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3}; String[] texts = {"桥边吹笛","城市风景","漩涡鸣人"};
final List<View> views = new ArrayList<>();
for (int i = 0; i < resources.length; i++) { RelativeLayout inflate = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.page_layout, null);
ImageView imageView = inflate.findViewById(R.id.imageView); TextView textView = inflate.findViewById(R.id.textView);
imageView.setBackgroundResource(resources[i]); textView.setText(texts[i]);
views.add(inflate); }
viewPager.setAdapter(new PagerAdapter() { @Override public int getCount() { return views.size(); }
@Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; }
@NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { container.addView(views.get(position)); return views.get(position); }
@Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView(views.get(position)); } }); } }
|
③ 运行效果

④ 补充:适配器中的方法还可以类似这样书写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content">
<ImageView android:id="@+id/image_view" android:layout_width="match_parent" android:layout_height="wrap_content" />
</LinearLayout>
|
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 42 43 44 45 46 47 48 49
| public class RotateActivity extends AppCompatActivity {
private int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3};
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rotate);
ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(new PagerAdapter() { @Override public int getCount() { return resources.length; }
@Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return object == view; }
@NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { View inflate = LayoutInflater.from(getApplicationContext()).inflate(R.layout.pager_layout, null); ImageView imageView = inflate.findViewById(R.id.image_view); imageView.setBackgroundResource(resources[position]);
container.addView(inflate);
return inflate; }
@Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View)object); } });
} }
|
3. 监听滑动的方法
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
| viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override public void onPageSelected(int position) {
}
@Override public void onPageScrollStateChanged(int state) {
} });
|
我们之前的例子是在加载视图的时候视图的图片和文本已经更新了,我们可以在 onPageSelected
中刷新数据。
1. 介绍
我们自己写一个类继承 FragmentPagerAdapter,体会一下其使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class MyAdapterFragment extends FragmentPagerAdapter {
public MyAdapterFragment(@NonNull FragmentManager fm, int behavior) { super(fm, behavior); }
@NonNull @Override public Fragment getItem(int position) { return null; }
@Override public int getCount() { return 0; } }
|
问题1:数据源在哪里?
我们可以修改构造方法,多传一个参数用来获取数据源。
问题2:FragmentManager是什么东西,怎么获取到?
FragmentActivity 或者 AppCompatActivity 中可以通过 getSupportFragmentManager() 方法 获取到 FragmentManager。
问题3:构造方法的第二个参数 behavior 是什么意思?
behavior 有两个值:
- BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
- BEHAVIOR_SET_USER_VISIBLE_HINT。 已经被弃用了,主要是为了兼容老的代码。
在懒加载中会继续说这个问题,现在只需要记住,传入第一个值就可。
2. 单独使用
① 适配器
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
| public class MyAdapterFragment extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyAdapterFragment(@NonNull FragmentManager fm, int behavior, List<Fragment> fragments) { super(fm, behavior);
this.fragments = fragments; }
@NonNull @Override public Fragment getItem(int position) { return fragments.get(position); }
@Override public int getCount() { return fragments.size(); } }
|
② 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
| public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
ViewPager pager = findViewById(R.id.pager);
List<Fragment> fragments = new ArrayList<>(); fragments.add(new MyFragment1()); fragments.add(new MyFragment2());
pager.setAdapter( new MyAdapterFragment( getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, fragments ) ); } }
|
③ Fragment
布局文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textStyle="bold" android:textSize="24sp" />
</RelativeLayout>
|
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 27 28 29
| public class MyFragment1 extends Fragment {
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View inflate = inflater.inflate(R.layout.fragment_layout, null); TextView textView = inflate.findViewById(R.id.textView); textView.setText("界面:小猫"); return inflate; } }
public class MyFragment2 extends Fragment {
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View inflate = inflater.inflate(R.layout.fragment_layout, null); TextView textView = inflate.findViewById(R.id.textView); textView.setText("界面:小狗"); return inflate; } }
|
④ 运行效果

3. 配和 Tablayout
我们只需要简单的修改一下代码
① 适配器
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 42 43
| public class MyAdapterFragment extends FragmentPagerAdapter {
private List<Fragment> fragments;
private String[] titles;
public MyAdapterFragment(@NonNull FragmentManager fm, int behavior, List<Fragment> fragments,String[] titles) { super(fm, behavior);
this.fragments = fragments;
this.titles = titles; }
@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 titles[position]; } }
|
② 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 30 31 32 33
| public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
ViewPager pager = findViewById(R.id.pager); TabLayout tab = findViewById(R.id.tab);
List<Fragment> fragments = new ArrayList<>(); fragments.add(new MyFragment1()); fragments.add(new MyFragment2());
String[] titles = getResources().getStringArray(R.array.title);
pager.setAdapter( new MyAdapterFragment( getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, fragments, titles ) );
tab.setupWithViewPager(pager); } }
|
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
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical">
<com.google.android.material.tabs.TabLayout android:id="@+id/tab" android:layout_width="match_parent" android:layout_height="wrap_content"
app:tabSelectedTextColor="#ff8000" app:tabIndicatorColor="#ff8000" android:background="#fbf4f2" app:tabRippleColor="@null"
app:tabTextAppearance="@style/MyTextStyle" />
<androidx.viewpager.widget.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
</LinearLayout>
|
③ fragment (无改动)
布局文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textStyle="bold" android:textSize="24sp" />
</RelativeLayout>
|
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 27 28 29
| public class MyFragment1 extends Fragment {
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View inflate = inflater.inflate(R.layout.fragment_layout, null); TextView textView = inflate.findViewById(R.id.textView); textView.setText("界面:小猫"); return inflate; } }
public class MyFragment2 extends Fragment {
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View inflate = inflater.inflate(R.layout.fragment_layout, null); TextView textView = inflate.findViewById(R.id.textView); textView.setText("界面:小狗"); return inflate; } }
|
④ 运行效果

⑤ 总结
我们只是在刚才的基础上,向适配器传递了标题的数据,通过代理方法 getPageTitles 设置对应位置的标题。
我们是使用 setupWithViewPager 绑定 TabLayout 和 ViewPager的数据。
慎用 setupWithViewPager ,有可能会出现问题。
注意:这种设置TabLayout的Item的方式实际上也是通过 addTab 的方式,可以参考:如何手动修改TabPageIndicator的title的解决办法
1.FragmentStatePagerAdapter的实现和FragmentPagerAdapter的实现一样。
2.FragmentPagerAdapter适用于页面比较少的情况,FragmentStatePagerAdapter适用于页面比较多的情况。
3.FragmentStatePagerAdapter中fragment实例在destroyItem的时候被真正释放,所以FragmentStatePagerAdapter省内存。FragmentPagerAdapter中的fragment实例在destroyItem的时候并没有真正释放fragment对象只是detach,所以FragmentPagerAdapter消耗更多的内存,带来的好处就是效率更高一些。
4.关于 FragmentPagerAdapter 和 FragmentStatePagerAdapter 的区别参考:
FragmentPagerAdapter和FragmentStatePagerAdapter区别
参考:ViewPager的翻页动画
参考:ViewPager的无限循环
参考:ViewPager的预加载和懒加载
参考文章
【Android进阶】关于PagerAdapter的使用方法的总结
ViewPager适配器PagerAdapter重写方法解释
安卓PagerAdapter中的isViewFromObject()方法有什么用?
Viewpager OnPageChangeListener 滑动事件讲解
getSupportFragmentManager()方法不可用解决办法