前 言

目前抖音 App 比较火,大家在玩 Android 版的抖音的过程中有没有发现,当你在刷抖音视频时手指上滑拉取下一视频,大家有没有觉得拉取视频很顺畅,而且视频能立即播放,当手指下滑拉取上一视频,效果也一样。其实这背后都是 ViewPager2 控件的功劳,当用户启动到抖音视频页面时,ViewPager2 除了加载当前的视频界面外还自动加载下一个视频界面的操作,所以大家有没有感觉首次进入到视频页面加载视频时等待的时间很长,这其实是 ViewPager2 同时加载当前视频和下一个视频的原因。当用户上滑拉取下一视频界面时, ViewPager2 不会立即销毁掉上一个视频界面,而是销毁上上个视频界面,下滑也是如此, ViewPager2 这样做的原因就是让用户感觉在滑动的过程中比较顺畅。

ViewPager2 的特性

ViewPager2 是用来替换 ViewPager,解决 ViewPager 的多数痛点,包括 RTL(从右到左) 布局支持,垂直方向滚动,可修改的 Fragment 集合等等。

编码实现
  • 首先在 AS 的内层 build.gradle 里添加 ViewPager2 控件的依赖
dependencies {
    ........
    implementation 'com.google.android.material:material:1.3.0-alpha04'
}

或者

dependencies {
    ........
    implementation "androidx.viewpager2:viewpager2:1.0.0"
}
  • 添加 ViewPager2 Layout 布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

从上面的 xml 代码可以看出,我们在这里定义 androidx.viewpager2.widget.ViewPager2 视图控件用来加载渲染数据列表集视图,它的类似 RecyclerView 控件的作用。

  • 设置 ViewPager2 加载视频视图的 Adapter
public class ViewPagerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private final static String TAG = "ViewPagerAdapter";

    private List<ShortVideoInfo> itemDataList = null;
    private Context context = null;

    public ViewPagerAdapter(Context context, List<ShortVideoInfo> itemDataList) {
        this.itemDataList = itemDataList;
        this.context = context;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
                                                      int viewType) {
        View v = LayoutInflater.from(context).inflate(R.layout.layout_viewpager2_item, parent, false);
        final RecyclerView.ViewHolder holder = new RecyclerItemNormalHolder(context, v);
        return holder;

    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        RecyclerItemNormalHolder recyclerItemViewHolder = (RecyclerItemNormalHolder) holder;
        recyclerItemViewHolder.setRecyclerBaseAdapter(this);
        recyclerItemViewHolder.onBind(position, itemDataList.get(position));
    }

    @Override
    public int getItemCount() {
        return itemDataList.size();
    }


    @Override
    public int getItemViewType(int position) {
        return 1;
    }

}

代码中自定义一个 ViewPagerAdapter() 的构造方法用来接收从 Activity 或者 Fragment 传递过来的上下文和视频List数据集;在 onCreateViewHolder() 方法中加载 item 条目的视图。除此之外,我们还在 onBindViewHolder() 方法中绑定自定义的 RecyclerItemNormalHolder 视图 Holder。

  • 设置视频视图的 Holder
public class RecyclerItemNormalHolder extends RecyclerItemBaseHolder {

    public final static String TAG = "RecyclerItemNormalHolder";

    protected Context context = null;

    @BindView(R.id.video_item_player)
    SampleCoverVideo gsyVideoPlayer;
    @BindView(R.id.tv_content)
    TextView tvContent;

    ImageView imageView;

    GSYVideoOptionBuilder gsyVideoOptionBuilder;

    public RecyclerItemNormalHolder(Context context, View v) {
        super(v);
        this.context = context;
        ButterKnife.bind(this, v);
        imageView = new ImageView(context);
        gsyVideoOptionBuilder = new GSYVideoOptionBuilder();
    }

    public void onBind(final int position, ShortVideoInfo videoModel) {

        String videoUrl = videoModel.getVideoUrl();
        String textContent = videoModel.getTextContent();

        Glide.with(context).load(videoModel.getVideoCover()).into(imageView);

        tvContent.setText(" " + textContent);

        Map<String, String> header = new HashMap<>();
        header.put("ee", "33");

        // 防止错位,离开释放
        //gsyVideoPlayer.initUIState();
        gsyVideoOptionBuilder
                .setIsTouchWiget(false)
                .setThumbImageView(imageView)
                .setUrl(videoUrl)
                .setVideoTitle(textContent)
                .setCacheWithPlay(true)
                .setRotateViewAuto(true)
                .setLockLand(true)
                .setPlayTag(TAG)
                .setMapHeadData(header)
                .setShowFullAnimation(true)
                .setNeedLockFull(true)
                .setPlayPosition(position)
                .setVideoAllCallBack(new GSYSampleCallBack() {
                    @Override
                    public void onPrepared(String url, Object... objects) {
                        super.onPrepared(url, objects);
                        GSYVideoManager.instance().setNeedMute(false);
                    }

                    @Override
                    public void onQuitFullscreen(String url, Object... objects) {
                        super.onQuitFullscreen(url, objects);
                        GSYVideoManager.instance().setNeedMute(false);
                    }

                    @Override
                    public void onEnterFullscreen(String url, Object... objects) {
                        super.onEnterFullscreen(url, objects);
                        GSYVideoManager.instance().setNeedMute(false);
                        gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]);
                    }
                }).build(gsyVideoPlayer);


        //增加title
        gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE);

        //设置返回键
        gsyVideoPlayer.getBackButton().setVisibility(View.GONE);

        //设置全屏按键功能
        gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                resolveFullBtn(gsyVideoPlayer);
            }
        });
    }

    /**
     * 全屏幕按键处理
     */
    private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) {
        standardGSYVideoPlayer.startWindowFullscreen(context, true, true);
    }

    public SampleCoverVideo getPlayer() {
        return gsyVideoPlayer;
    }

}

该 Holder 的作用就是设置视频播放的控制和 item 视图上的文本内容,然后在适配器 ViewPagerAdapter 的 onBindViewHolder() 方法中绑定该 Holder 进行数据关联。

  • 在 Activity 或者 Fragment 设置加载视图内容
    private void getData() {

        setData();
        viewPagerAdapter = new ViewPagerAdapter(this, mList);
        viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
        viewPager2.setAdapter(viewPagerAdapter);

        viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                // 大于0说明有播放
                int playPosition = GSYVideoManager.instance().getPlayPosition();
                if (playPosition >= 0) {
                    // 对应的播放列表TAG
                    if (GSYVideoManager.instance().getPlayTag().equals(RecyclerItemNormalHolder.TAG)
                            && (position != playPosition)) {
                        playPosition(position);
                    }
                }
            }
        });
        viewPager2.post(new Runnable() {
            @Override
            public void run() {
                playPosition(0);
            }
        });

    }

从代码中我们可以看到,我们首选实例化 ViewPagerAdapter 对象,然后通过 viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL) 设置为垂直方向滑动,然后把实例 ViewPagerAdapter 对象通过 viewPager2.setAdapter(viewPagerAdapter) 添加到 ViewPager2 对象里。其中的 viewPager2.registerOnPageChangeCallback() 是监听页面更改回调函数,该函数下的 onPageSelected() 方法是在所选页面上,可以在方法里设置当前界面的视频播放、暂时以及界面上的按钮点击事件操作等。

界面运行效果图如下:

在这里插入图片描述

———————— The end ————————

码字不易,如果您觉得这篇博客写的比较好的话,可以赞赏一杯咖啡吧~~
在这里插入图片描述


Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(码云)

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐