简介
文章发布于我的个人博客: 手动实现轮播图(一):ViewPager 上手
想看效果直接拉到最后~
Viewpager是 Android 提供的布局管理器,常被用来实现左右滑动的页面、视图。
在实际工程中,有许多都是用来实现轮播图功能的。
今天,我们从零开始造一个简易轮播图组件。
本系列文章面向的读者,是刚学完 Android 教材的初学者,旨在:
- 简单介绍ViewPager原理并如何快速上手
- 使用简单的代码结构,完成一个初级的轮播图组件
文章作者毕竟经验不多,水平有限,所以缺漏在所难免,希望路过读到本文的前辈们不吝赐教,谢谢~
接下来,我们就从Viewpager是什么开始,慢慢来了解他。
1. Viewpager 上手
官方开发文档:android.support.v4.view.ViewPager
- ViewPager是一个布局管理器,可以作为根布局
- 因为他继承于ViewGroup,常见的布局管理器还有FrameLayout, LinearLayout等
- 当他作为根布局时,每一个页面都将占据整个布局
- ViewPager该怎么使用
- 在布局文件中添加一个<ViewPager>标签,此位置作为ViewPager容器主体所在
- 创建一个新的布局文件,作为内嵌页面的布局
- 如果使用fragment的话,我们只需要创建一个模板,之后所有内嵌页面都使用这个模板来生成即可
- 如果单单使用布局文件,那么我们每一个页面项都要创建一个布局文件,之后手动添加ViewPager容器
- 所以本文章均使用fragment来实现
- 在activity中,实例化ViewPager
- 为ViewPager设置Adapter
- 类似于RecyclerView,我们也是使用Adapter来和ViewPager进行通信
- 这样大大方便了我们使用
上述步骤中,前几步几乎是组件/布局实例化的常规操作,所以我们真正要做的其实非常少。
接下来我们开始动手来使用ViewPager。
创建 ViewPager 容器和子页面布局文件
我们新建一个项目之后,打开默认创建的activity_main.xml布局文件中,将内容改为以下代码:
<?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.support.v4.view.ViewPager
android:id="@+id/view_pager_inside"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="@android:color/darker_gray"
android:layout_centerInParent="true">
</android.support.v4.view.ViewPager>
</RelativeLayout>复制代码
可以看到,布局文件中仅有一个根布局RelativeLayout和一个ViewPager。
这里的ViewPager就是容器主体所在。
接着我们创建嵌入的页面布局文件:
新建一个view_pager_fragment.xml文件,内容如下:
<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
android:layout_width="300dp"
android:layout_height="300dp"
app:cardCornerRadius="10dp"
android:elevation="5dp">
<TextView
android:id="@+id/text_view_fragment"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.v7.widget.CardView>
</LinearLayout>复制代码
这里面是常规布局,有一个卡片CardView和内藏一个的TextView。
到时候,滑动的每一个页面的布局模板都来自这个文件,我们只需要在代码里稍微修改,就可以生成特定的页面了。
现在,我们回到MainActivity.java文件中,实例化我们刚刚的ViewPager。
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager; // 定义一个 Viewpager 变量
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewPager = findViewById(R.id.view_pager_inside); // 实例化 ViewPager
}
}复制代码
接下来我们该干什么呢?当然是为ViewPager添加页面了。
那么页面从哪里来呢?当然是我们之前创建的那个布局view_page_fragment.xml了。
- 我们的ViewPager主体位于activity_main.xml布局中
- 我们在MainActivity.java中使用setContentView(R.layout.activity_main); 设置两者关联
- 然后我们可以在MainActivity.java里面实例化ViewPager并使用它
- 同理,我们要创建一个Fragment,将它和view_page_fragment.xml关联起来,并在它里面实例化页面的布局
不理解Fragment的同学,可以看一下文档里的 片段 哦。
创建一个PageFragment.java类,继承于android.support.v4.app.Fragment,这里特别注意要使用v4包里的Fragment。
现在这个类里空荡荡,让我们来填充一些有意思的内容。
- 关联PageFragment.java和view_page_fragment.xml
使用Alt + Insert,选择Override Methods,然后重写onCreateView如下:
private TextView mTextView;
private CardView mCardView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view_pager_fragment, container, false);
mTextView = view.findViewById(R.id.text_view_fragment);
mCardView = view.findViewById(R.id.card_view);
return view;
}复制代码
我们使用了LayoutInflater来将view_page_fragment.xml加载为代码里的View对象,然后再从view对象里,找到我们放置的两个组件:CardView和TextView。
如果不理解LayoutInflater可以看看如下两位的文章:
- Android LayoutInflater原理分析,带你一步步深入了解View(一)
- 理解Android中的LayoutInflater
这样就算是关联起来了,系统在创建PageFrament.java对象的时候,就会实例化view_page_fragment.xml布局了。
接着我们为PageFragment.java创建一个静态生成器(方法)。为什么要静态生产类呢?
因为我们每生成一个页面,其实就是创建一个PageFragment.java的对象,然后我们还要向这个对象传递数据。
为了不做重复的工作,我们写一个静态生成器,这样每次外部类只要调用这个静态生成器,就可以很简单地创建对象了。
看看代码:
public class PageFragment extends Fragment {
public static Fragment newInstance(){
return new PageFragment();
}
public View onCreateView ...
}复制代码
如果你写过静态Intent生成方法,相信这个类生成器也很容易理解了。
上面代码就是在返回时,先创建一个PageFragment对象再返回去。就这一句代码,有必要写一个静态方法吗?
当然有,因为我们还没有把他真正的用处挖掘出来呢!
前面说到的,我们之所以只需要创建一个布局模板文件,而不需要每一个页面就定制一个,就是我们要在代码里动态定制页面。
我们这里子页面模板里,只有一个TextView可以写东西,所以我们用它来作为区分页面的标志,比如T1、T2这样。
那问题就是,我们如何动态定制页面呢?
我们来看看现在的情况吧:
可以看到,这是典型的 MVC 结构,在这里面呢,PageFragment唯一地通过MainActivity.java来创建,虽然我们还没有实现这一步。
也即是说,我们要在这一步里,向PageFragment传递定制化的数据,比如页面一传递T1,页面二传递T2这样子。
接着在PageFragment只需要使用同一套代码就可以生成不同的页面了。
问题的难点在于如何向一个Fragment传递数据。当然,这样的文章已经写了很多了,相信你稍微搜索一下,就知道我们即将使用的是Fragment Arguement的方法。其实就是在fragment对象上附加一个参数。
这种方法是不是很像Intent的附加参数呢?
下面是实现代码:
public class PageFragment extends Fragment {
private static final String ARGS_TITLE = "argsTitle";
private CardView mCardView;
private TextView mTextView;
public static Fragment newInstance(String title){
Bundle args = new Bundle();
args.putString(ARGS_TITLE, title);
PageFragment pageFragment = new PageFragment();
pageFragment.setArguments(args);
return pageFragment;
}
public View onCreateView ...
}复制代码
在这里面,我们使用了Bundle对象来存储要传递的数据,然后使用setArguement()方法来把参数附加到新建的pageFragment对象里面。
最后返回这个对象即可。
接下来我们就可以在MainActivity.java里面使用这个静态生成器。(我只列出了新增的代码哦)
public class MainActivity extends AppCompatActivity {
...
private String[] mStringList = {
"T1", "T2", "T3", "T4", "T5"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
...
FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentPagerAdapter(fm) {
@Override
public Fragment getItem(int position) {
String title = mStringList[position];
return PageFragment.newInstance(title);
}
@Override
public int getCount() {
return mStringList.length;
}
});
}
}复制代码
这里我们先定义了一个字符串数组,来存储文字字符串。也就是之前图片的【模型】区域。
FragmentManager fm = getSupportFragmentManager(); 复制代码
这句代码是获取一个FragmentManager,也就是fragment的管理器。接下来在ViewPager中需要用这个管理器来管理fragment。(别担心,这里系统已经帮你做好了,你只要传入一个管理器就行。
mViewPager.setAdapter(new FragmentPagerAdapter(fm) {
...
});复制代码
这一句也好理解,前面说了,ViewPager也需要一个对应的Adapter来和他通信,幸运的是系统已经为我们提供了两个非常好用的Fragment的Adapter。
- FragmentPagerAdapter
- 会提前自动创建:前中后,三个页面
- 适合页面布局简单的情况
- FragmentStatePagerAdapter
- 只会创建一个页面
- 适合页面布局复杂的情况
所以我们这里使用了FragmentPagerAdapter咯。
使用这个FragmentPagerAdapter,最少只需要重写两个方法:
- getItem()
- 通过position参数,返回一个创建好的页面
- 我们就要在这里面做页面的创建工作哦
- getCount()
- 要创建的页面的数量
理解到这里,我们只需要在这段代码后面,轻轻加上一句:
mViewPager.setCurrentItem(0);复制代码
然后构建、运行,这个 Demo 就做好啦!
快试试效果吧~
试完了吗?是不是感觉哪里不对劲?
TextView 呢?
对啊,因为你虽然在newInstance里存放了数据,但是你并没有取出来呀~
来到PageFragment里取出来吧。
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view_pager_fragment, container, false);
mTextView = view.findViewById(R.id.text_view_fragment);
mCardView = view.findViewById(R.id.card_view);
String title = getArguments().getString(ARGS_TITLE);
mTextView.setText(title);
return view;
}复制代码
现在不就可以了嘛~
什么?你嫌一个TextView太单调?...
那你干嘛不加一个ImageView进去啊,然后传入一些令人心旷神怡的图片还不是美滋滋?
- 本项目地址ViewPagerDemo
- 感谢下列参考文章
- Android ViewPager 无限循环左右滑动(可自动) 实现
- Android ViewPager实现循环滚动