视频页面:ViewPager
ViewPager 是一种可以让用户通过左右滑动来切换页面的控件,通过它我们可以展示超过屏幕尺寸大小的内容,在某种程度上它可以说是实现多页面的最佳方式,同时 ViewPager 还支持任意动态的添加/删除页面。比如我们可以将不同的类别的内容分别放在不同页面当中,然后通过滑动切换不同的类别从而给用户展示不同的页面,这个在类似百度App等新闻类App中非常适用。在 ViewPager 中插入“娱乐”、“国际”、“体育”、“星座”等等新闻类别,然后在不同的 View 中展示不同的新闻内容,还可以根据用户的喜好动态增加/删除某些页面,接下来就一起来看看如何完成多页视图。
1. ViewPager 的特性
大家在使用 Android 手机的时候一定都见过下图的效果:
没错,这个就是今天的主角——ViewPager 了。在实际开发过程中,我们大多数时候会采用 Fragment 来展示一个页面而不会直接采用 View,在后面的章节学完 Fragment 之后就会知道,Fragment 可以封装 UI 和逻辑,并且会维护自己的生命周期,所以通过 Fragment 我们可以实现更丰富生动的效果,当然对于 ViewPager 的使用而言其实二者几乎没什么差别,我们现在还是把重点放在 ViewPager 上,在后面学完 Fragment 之后只需要做一些简单的改动即可将 View 替换成 Fragment。ViewPager 和前面所学的 ListView/GridView 类似,也需要一个适配器来完成数据的适配,不同的是 ViewPager 有一个专门的适配器——PagerAdapter,所以我们很多的工作也是围绕着 PagerAdapter 展开。
2. PagerAdapter 的使用方法
类似前面所讲的 BaseAdapter 的四个回调接口(不记得的同学可以返回前面章节回顾一下),PagerAdapter 同样也有类似的回调接口,如下:
- public Object instantiateItem(ViewGroup container, int position):
根据传入的 position 创建一个 Page,适配器需要在这个回调里网 container 里面添加 View,如下:
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.view_pager, container, false);
TextView textView = itemView.findViewById(R.id.TextView);
textView.setText("Imooc Android");
container.addView(itemView);
return itemView;
}
- public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object):
根据传入的 position 移除一个 Page
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
- gpublic int getCount():
返回当前 ViewPager 中可用 View 的数量
public int getCount() {
return mList.length;
}
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object):
通过instantiateItem()
返回的对象可以看做是一个 key,这个方法用来判断传入的 View 是否是之前创建的 key,如下:
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
常用的一般就是以上四种回调方法,理解起来都比较简单,其中要注意的是getCount
和isViewFromObject
这两个是必须实现的,而instantiateItem()
和destroyItem()
是可选的,不过大多数场景还是推荐大家实现 4 个回调方法。
3. ViewPager 完整示例
本节将通过一个简单的例子学习 ViewPager 的使用,每一个 Page 将会通过一个 View 来实现(在学习了 Fragment 之后,可以尝试将 View 替换成 Fragment)。例子中的每一个 Page 表示一种类别,在切换过程中我们会接收切换的状态回调,同步更新类别标题。
3.1 整体布局
由于每个页面都在 ViewPager 中,所以整体的布局非常简单,我们只需要放置一个 ViewPager 以及一个 TextView 用来显示当前 Page 的标题即可。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout\_width="match\_parent"
android:layout\_height="match\_parent"
tools:showIn="@layout/activity\_main">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view\_pager"
android:layout\_width="match\_parent"
android:layout\_height="match\_parent"/>
<TextView
android:text="Num"
android:textSize="100sp"
android:id="@+id/text"
android:layout\_marginTop="50dp"
android:layout\_centerHorizontal="true"
android:layout\_width="wrap\_content"
android:layout\_height="wrap\_content"/>
</RelativeLayout>
3.2 Page 页面的布局编写
对于整体布局而言,主要的页面都在 ViewPager 当中,所以我们需要为不同结构的 Page 编写不同的页面,由于本例中每个 Page 的页面结构都一样,所以可以直接复用一套,直接在里面放置一个 ImageView 用于展示类别图片。
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout\_width="match\_parent"
android:layout\_height="match\_parent" />
3.3 适配器的编写
适配这一块主要就是对四个回调接口的实现,其实在第 2 小节的描述中已经展示了各个方法的实现方式。
package com.emercy.myapplication;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import java.util.List;
public class MyAdapter extends PagerAdapter {
private final List<View> mView;
public MyAdapter(List<View> view) {
mView = view;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
container.addView(mView.get(position));
return mView.get(position);
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(mView.get(position));
}
@Override
public int getCount() {
return mView.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
}
3.4 MainActivity 主逻辑编写
主逻辑主要的任务就是将前面的布局都用上,并通过 Adapter 将数据和布局串联起来,所以我们需要获取到 ViewPager 中每个 View 的实例,设置类别之后传递给 Adpater,绑定的任务就交由Adpater完成。接下来再监听 ViewPager 的滑动状态从而判断当前切换的位置,从而实现同步更新类别标题,整体代码如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
import androidx.viewpager.widget.ViewPager;
import org.w3c.dom.Text;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
private ViewPager mViewPager;
private String[] mTitle = new String[]{"苹果", "香蕉", "荔枝"};
private List<View> mView = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = findViewById(R.id.text);
mViewPager = findViewById(R.id.view_pager);
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
tv.setText(mTitle[position]);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
ImageView view1 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view1.setBackgroundColor(Color.RED);
view1.setImageResource(R.drawable.apple);
mView.add(view1);
ImageView view2 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view2.setBackgroundColor(Color.GREEN);
view2.setImageResource(R.drawable.banana);
mView.add(view2);
ImageView view3 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view3.setBackgroundColor(Color.BLUE);
view3.setImageResource(R.drawable.lychee);
mView.add(view3);
mViewPager.setAdapter(new MyAdapter(mView));
tv.setText(mTitle[0]);
}
}
编译之后效果如下:
通过左右滑动可以切换不同的页面,每个页面对应的一种水果类别,这样就通过 ViewPager 实现了一个简单的页面切换效果。
4. 小节
本节介绍了 ViewPager 的特点及使用场景,并讲解了 ViewPager 的专属适配器——PagerAdapter 的几个回调函数的使用方法,最后采用 ViewPager 实现了一个简单的例子用于切换不同的页面从而展示不同的类别。这一节中是直接采用 View 来承载每一个 Page,而在实际开发中大多数场景会采用 Fragemnt 来承载 Page,不过对于 ViewPager 的使用到同小异,针对 Fragment 系统提供了两种 Adapter:FragmentPageAdapter
和FragmentStatePagerAdapter
,在学完 Fragement之后大家可以自行修改本节的例子,通过 Fragment 来实现本例的效果。