androidx ViewPager2 实现横向、纵向滑动播放短视频(一)

需求描述

最近的开发需求,实现类似某音的功能,大致就是界面横向滑动,加载不同分类的视频列表,纵向滑动加载某分类下的视频列表,然后进行短视频的播放,具体短视频内的 点赞、关注、评论等,暂且不提。目前已上线几个版本,还算稳定,做个总结,希望能帮到有这方面需求的朋友。

视图选择

app 基于androidx,使用ViewPager2,具体实现思路:
一、界面的横向滑动,使用ViewPager2+TabLayout 达到界面效果,布局文件大致简单如下:

 <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
      <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/videoViewPager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:overScrollMode="never" />
      <com.google.android.material.tabs.TabLayout
          android:id="@+id/videoTabLayout"
          android:layout_width="wrap_content"
          android:layout_height="@dimen/dp_30"
          android:layout_marginTop="@dimen/dp_30"
          android:layout_marginLeft="@dimen/dp_5"
          android:background="@color/translucent"
          app:tabIndicatorColor="@color/white"
          app:tabIndicatorFullWidth="false"
          app:tabIndicatorHeight="@dimen/dp_2"
          app:tabRippleColor="@color/translucent"
          app:tabSelectedTextColor="@color/white"
          app:tabTextAppearance="@style/video_tabLayout_text"
          app:tabTextColor="@color/video_tab_unselect" />
 </FrameLayout>

布局文件是从项目里简单抽出来的,videoViewPager控制主界面,tablayout横向滑动标签,数据源使用FragmentStateAdapter,控制横向滑动的视图,代码大致简单如下:

// 横向滑动时的Tab页
videoTabLayout!!.addTab(videoTabLayout!!.newTab().setText("关注"));
videoTabLayout!!.addTab(videoTabLayout!!.newTab().setText("推荐"), true);
//横向滑动的Fragment视图
 videoTypeFragmentAdapter = ShortVideoTypeFragmentAdapter(this, videoTabLayout!!.tabCount)
 videoViewPager!!.adapter = videoTypeFragmentAdapter
//创建横向展现的视图Fragment
override fun createFragment(position: Int): Fragment {
        when (position) {
            Int_ZREO -> {
                return  ShortVideoTypeFragment1(Int_ZREO)
            }
            Int_ONE -> {
                return  ShortVideoTypeFragment2(Int_ONE )
            }
            else -> {
                return ShortVideoTypeFragment1(Int_ZREO)
            }
        }
    }

这里使用TabLayout的addOnTabSelectedListener方法控制 选中tab时,设置videoViewPager对应的Item,使用ViewPager2的registerOnPageChangeCallback方法控制选中某Page时同时选中对应的Tab;最后设置videoViewPager的当前Item,这里设置显示第二个Fragment视图(index=1),注意setCurrentItem的第二个参数smoothScroll,如果给true,尽管ViewPager2默认不预加载,他也默认会创建第一个Fragment视图(index=0),当设置flase时,如果你的offscreenPageLimit参数不设置,他默认不会创建第一个Fragment视图。

// 选中Tab页时,选中当前Pager
videoTabLayout!!.addOnTabSelectedListener(object : OnTabSelectedListener {
     override fun onTabSelected(tab: TabLayout.Tab) {
         videoViewPager!!.currentItem = tab.position
     }
     override fun onTabUnselected(tab: TabLayout.Tab) {}
     override fun onTabReselected(tab: TabLayout.Tab) {}
 })
 // 选中当前页时,选中对应的Tab页
 videoViewPager!!.registerOnPageChangeCallback(object : OnPageChangeCallback() {
     override fun onPageSelected(position: Int) {
         super.onPageSelected(position)
         videoTabLayout!!.selectTab(videoTabLayout!!.getTabAt(position))
         videoTabLayout!!.setScrollPosition(position, 0f, false)
     }
 })
 videoViewPager!!.setCurrentItem(1, false)

二、界面的纵向滑动,FragmentStateAdapter创建的Fragment的内容视图直接使用ViewPager2,设置orientation属性即可,布局大概简单如下:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootFrameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/videoSwipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/videoPlayPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>

ViewPager2 支持纵向布局,直接设置RecyclerView.Adapter即可,adapter布局文件大概简单如下:

<FrameLayout 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:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:clickable="true">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/playerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="@color/translucent"
        app:use_controller="false"
        android:keepScreenOn="true" />
    <ImageView
        android:id="@+id/pauseIv"
        android:layout_width="@dimen/dp_34"
        android:layout_height="@dimen/dp_34"
        android:layout_gravity="center"
        android:src="@mipmap/icon_video_play"
        android:visibility="gone" />
    <ImageView
        android:id="@+id/shortVideoCoverView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:scaleType="centerCrop" />  
    <fr.castorflex.android.smoothprogressbar.SmoothProgressBar
        android:id="@+id/shortVideoLoadingView"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_gravity="bottom"
        android:background="@color/color_000000"
        android:indeterminate="true"
        android:indeterminateOnly="false" />
</FrameLayout>

Adapter大概简单代码如下:

class ShortVideoExoPlayAdapter(val context: Context) : RecyclerView.Adapter<ShortVideoExoPlayAdapter.RecyclerHolder>(){

    var shortVideoListBean = ArrayList<ShortVideoListBean.ShortVideoBean>()
    var mContext = context

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerHolder {
        val view: View = LayoutInflater.from(mContext).inflate(R.layout.item_video_exoplay, parent, false)
        return RecyclerHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerHolder, position: Int) {
       
        Glide.with(this.mContext).load(itemBean.cover_path)
                .placeholder(R.mipmap.goods_null_img)
                .error(R.mipmap.goods_null_img).into(holder.shortVideoCoverView!!)     holder.shortVideoLoadingView?.setSmoothProgressDrawableInterpolator(DecelerateInterpolator(1.0f))     holder.shortVideoLoadingView?.setSmoothProgressDrawableColors(mContext.resources.getIntArray(R.array.video_progress_bar_colors))
        holder.shortVideoLoadingView?.setSmoothProgressDrawableMirrorMode(true)
        holder.shortVideoLoadingView?.setSmoothProgressDrawableReversed(true)
        holder.shortVideoLoadingView?.setSmoothProgressDrawableSpeed(2.0f)
        holder.shortVideoLoadingView?.setSmoothProgressDrawableProgressiveStartSpeed(2.0f)
        holder.shortVideoLoadingView?.setSmoothProgressDrawableProgressiveStopSpeed(2.0f)
        holder.shortVideoLoadingView?.setSmoothProgressDrawableSectionsCount(1)
       
        var shortVideoUri:String = ""
        shortVideoUri = if (!TextUtils.isEmpty(itemBean.video_m3u8_path)) {
            itemBean.video_m3u8_path!!
        } else {
            itemBean.video_path
        }
        holder.mediaItem = MediaItem.fromUri(shortVideoUri)
    }

    override fun onBindViewHolder(holder: RecyclerHolder, position: Int, payloads: List<Any>) {
        if (payloads == null || payloads.isEmpty()) {
            onBindViewHolder(holder, position)
        } else { 
        }
    }
    override fun onViewAttachedToWindow(holder: ShortVideoExoPlayAdapter.RecyclerHolder) {
    }
    override fun onViewDetachedFromWindow(holder: ShortVideoExoPlayAdapter.RecyclerHolder) {
    }
    override fun getItemCount(): Int {
        return shortVideoListBean.size
    }
    class RecyclerHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
       
        var shortVideoLoadingView: SmoothProgressBar? = null
        var shortVideoCoverView: ImageView? = null
        var playerView: PlayerView? = null
        var mediaItem: MediaItem?=null

        init {
            shortVideoLoadingView = itemView.findViewById<SmoothProgressBar>(R.id.shortVideoLoadingView)
            shortVideoCoverView = itemView.findViewById<View>(R.id.shortVideoCoverView) as ImageView
            playerView = itemView.findViewById<View>(R.id.playerView) as PlayerView
        }
    }
}

然后就是设置ViewPager2相关属性,纵向滑动展现数据,这里说一下ViewPager2的RecyclerHolder,我Log跟踪的结果,纵向滑动会创建5个Holder,然后滑动时Holder 视图复用;offscreenPageLimit 属性即使不设置,滑动到第二页时,第三页也会创建,就是说一旦滑动效果产生,当前页的上一页下一页都会存在的。

videoPlayPager!!.orientation = ViewPager2.ORIENTATION_VERTICAL
videoPlayPager!!.offscreenPageLimit = 1
videoPlayPager!!.adapter = shortVideoPlayListAdapter

到这里视图效果基本实现完毕,实现需求的横向、纵向滑动 展现数据,接下来只需要控制短视频的播放。

播放器的选择以及控制

最初使用的播放器有点弱,滑动时或者快速滑动时播放器出现的问题太多,包括播放器打开失败、无法暂停等,后来改用ExoPlayer,效果很满意。
实现横向、纵向滑动展现数据播放短视频, 控制播放器也是比较让人头大的,下一篇继续写ExoPalyer播放短视频的相关总结。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您可以使用ViewPager2来实现RecyclerView的左右滑动功能。以下是一些步骤可以帮助您完成这个实现: 1. 首先,在您的布局文件中,将ViewPager2添加为父容器,并设置其布局属性,以适应您的需求。例如: ```xml <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 在您的活动或碎片中,找到对应的ViewPager2视图,并获取其实例: ```java ViewPager2 viewPager = findViewById(R.id.viewPager); ``` 3. 创建一个适配器类来管理RecyclerView的内容。这里我们使用RecyclerViewAdapter作为示例: ```java public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> { // 实现适配器的其他方法 // ... } ``` 4. 在您的活动或碎片中,初始化适配器和RecyclerView实例,并将RecyclerView设置给ViewPager2: ```java RecyclerViewAdapter adapter = new RecyclerViewAdapter(); RecyclerView recyclerView = new RecyclerView(this); recyclerView.setAdapter(adapter); viewPager.setAdapter(new RecyclerViewAdapterWrapper(recyclerView)); ``` 5. 创建一个RecyclerViewAdapterWrapper类,继承自RecyclerView.Adapter,用于将RecyclerView适配给ViewPager2: ```java public class RecyclerViewAdapterWrapper extends RecyclerView.Adapter<RecyclerViewAdapterWrapper.ViewHolder> { private RecyclerView recyclerView; public RecyclerViewAdapterWrapper(RecyclerView recyclerView) { this.recyclerView = recyclerView; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(recyclerView); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // 不需要做任何操作 } @Override public int getItemCount() { return 1; } static class ViewHolder extends RecyclerView.ViewHolder { ViewHolder(@NonNull View itemView) { super(itemView); } } } ``` 6. 最后,您可以在RecyclerViewAdapter类中实现RecyclerView的内容和逻辑,根据您的需求进行自定义。 现在,您就可以在ViewPager2中左右滑动RecyclerView了。注意,ViewPager2还可以与其他类型的视图(如Fragment)结合使用,以实现更丰富的界面效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JavPer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值