一、实现效果

1、可见的第一个正在播放的视频item滑向上如果超顶部时就停止,并且下一个视频会自动播放,是仿西瓜视频的。2、点击某一个正在播放的视频item右下角的全屏(横屏没有上下滑动)

二、引入依赖

appbuild.gradle在添加以下代码
1、implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6',这个里面带的适配器,直接调用就即可

BaseRecyclerViewAdapterHelper简称BRVAH

Android SDK是否支持BaseRecyclerViewAdapterHelper:3.0.6
android compileSdkVersion 29
android compileSdkVersion 30
android compileSdkVersion 31
android compileSdkVersion 32
android compileSdkVersion 33

这依赖包还需要得到要添加,在Projectbuild.gradle在添加以下代码,不添加就不行

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }//加上
    }
}

2、视频:implementation 'cn.jzvd:jiaozivideoplayer:7.0.5'
3、封面图:implementation 'com.github.bumptech.glide:glide:4.8.0'

三、AndroidManifest.xml

1、增加网络权限

<uses-permission android:name="android.permission.INTERNET" />

2、增加配置

		<activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:exported="true">
            ...
        </activity>

其中android:configChanges="orientation|screenSize|keyboardHidden"是列表item的视频横竖屏切换时,禁止activity重新创建

四、实现源码

1、实体类

MyPlayer.java

package com.example.myapplication3;

public class MyPlayer {

    private String videoUrl;
    private String coverImageUrl;

    public MyPlayer(String videoUrl, String coverImageUrl) {
        this.videoUrl = videoUrl;
        this.coverImageUrl = coverImageUrl;
    }

    public String getVideoUrl() {
        return videoUrl;
    }

    public void setVideoUrl(String videoUrl) {
        this.videoUrl = videoUrl;
    }

    public String getCoverImageUrl() {
        return coverImageUrl;
    }

    public void setCoverImageUrl(String coverImageUrl) {
        this.coverImageUrl = coverImageUrl;
    }
}

2、视频自定义View

MyJzvdStd.kt

package com.example.myapplication3

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.widget.SeekBar
import cn.jzvd.JzvdStd

/**
 * 这里可以监听到视频播放的生命周期和播放状态
 * 所有关于视频的逻辑都应该写在这里
 * Created by Nathen on 2017/7/2.
 */
class MyJzvdStd : JzvdStd {
    constructor(context: Context?) : super(context) {}
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}

    override fun init(context: Context) {
        super.init(context)
    }

    override fun onClick(v: View) {
        super.onClick(v)
        val i = v.id
        if (i == cn.jzvd.R.id.fullscreen) {
            Log.i(TAG, "onClick: fullscreen button")
        } else if (i == cn.jzvd.R.id.start) {
            Log.i(TAG, "onClick: start button")
        }
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        super.onTouch(v, event)
        val id = v.id
        if (id == cn.jzvd.R.id.surface_container) {
            when (event.action) {
                MotionEvent.ACTION_UP -> {
                    if (mChangePosition) {
                        Log.i(TAG, "Touch screen seek position")
                    }
                    if (mChangeVolume) {
                        Log.i(TAG, "Touch screen change volume")
                    }
                }
            }
        }
        return false
    }

    override fun getLayoutId(): Int {
        return cn.jzvd.R.layout.jz_layout_std
    }

    override fun startVideo() {
        super.startVideo()
        Log.i(TAG, "startVideo")
    }

    override fun onStopTrackingTouch(seekBar: SeekBar) {
        super.onStopTrackingTouch(seekBar)
        Log.i(TAG, "Seek position ")
    }

    override fun gotoScreenFullscreen() {
        super.gotoScreenFullscreen()
        Log.i(TAG, "goto Fullscreen")
    }

    override fun gotoScreenNormal() {
        super.gotoScreenNormal()
        Log.i(TAG, "quit Fullscreen")
    }

    override fun autoFullscreen(x: Float) {
        super.autoFullscreen(x)
        Log.i(TAG, "auto Fullscreen")
    }

    override fun onClickUiToggle() {
        super.onClickUiToggle()
        Log.i(TAG, "click blank")
    }

    //onState 代表了播放器引擎的回调,播放视频各个过程的状态的回调
    override fun onStateNormal() {
        super.onStateNormal()
    }

    override fun onStatePreparing() {
        super.onStatePreparing()
    }

    override fun onStatePlaying() {
        super.onStatePlaying()
    }

    override fun onStatePause() {
        super.onStatePause()
    }

    override fun onStateError() {
        super.onStateError()
    }

    override fun onStateAutoComplete() {
        super.onStateAutoComplete()
        Log.i(TAG, "Auto complete")
    }

    //changeUiTo 真能能修改ui的方法
    override fun changeUiToNormal() {
        super.changeUiToNormal()
    }

    override fun changeUiToPreparing() {
        super.changeUiToPreparing()
    }

    override fun changeUiToPlayingShow() {
        super.changeUiToPlayingShow()
    }

    override fun changeUiToPlayingClear() {
        super.changeUiToPlayingClear()
    }

    override fun changeUiToPauseShow() {
        super.changeUiToPauseShow()
    }

    override fun changeUiToPauseClear() {
        super.changeUiToPauseClear()
    }

    override fun changeUiToComplete() {
        super.changeUiToComplete()
    }

    override fun changeUiToError() {
        super.changeUiToError()
    }

    override fun onInfo(what: Int, extra: Int) {
        super.onInfo(what, extra)
    }

    override fun onError(what: Int, extra: Int) {
        super.onError(what, extra)
    }
}

3、适配器

播放器适配器PlayerAdapter.kt,这里第一个视频开始播放,其他的未播放

package com.example.myapplication3.adapter

import android.widget.ImageView
import cn.jzvd.JzvdStd
import com.bumptech.glide.Glide
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication3.MyPlayer
import com.example.myapplication3.R
import kotlinx.android.synthetic.main.item_recyclerview.view.*

class PlayerAdapter(layoutResId: Int = R.layout.item_recyclerview) :
    BaseQuickAdapter<MyPlayer, BaseViewHolder>(layoutResId) {

    override fun convert(holder: BaseViewHolder, item_url: MyPlayer) {

        holder.itemView.run {
            videoplayer.setUp(item_url.videoUrl,"",JzvdStd.SCREEN_NORMAL)
            videoplayer.thumbImageView.scaleType = ImageView.ScaleType.FIT_XY
//            videoplayer.thumbImageView.scaleType = ImageView.ScaleType.FIT_CENTER

			//第一个视频开始播放,其他的未播放
            if ( 0 == holder.adapterPosition){
                videoplayer.startVideo() 
            }

            Glide.with(videoplayer.context).load(item_url.coverImageUrl).into(videoplayer.thumbImageView)
        }
    }
}

item布局item_recyclerview.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="300dp">

    <com.example.myapplication3.MyJzvdStd
        android:id="@+id/videoplayer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

在这里插入图片描述

代码解析

1、视频地址

videoplayer.setUp("视频地址".videoUrl,"",JzvdStd.SCREEN_NORMAL)

2、封面图地址

Glide.with(videoplayer.context).load("封面图地址".coverImageUrl).into(videoplayer.thumbImageView)

3、如何使用jiaozivideoplayer播放本地视频
注意:jiaozivideoplayer支持file:///开头的文件播放,但是不支持assets或者raw目录下的视频,assets下的视频要先复制到本地路经才能播放

4、实现视图

MainActivity.kt,在我们滑动的时候,jiaozivideoplayer会自动停止正在播放的视频,这里面我还添加了一个功能,就是监听当滑动停止的时候自动播放下一个视频

package com.example.myapplication3

import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.view.KeyEvent
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import cn.jzvd.Jzvd
import com.example.myapplication3.adapter.PlayerAdapter
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val mAdapter by lazy {
        PlayerAdapter().apply {

        }
    }

    //视频
    private val videoUrl = "http://gslb.miaopai.com/stream/ed5HCfnhovu3tyIQAiv60Q__.mp4"
//    private val videoUrl = "https://vd4.bdstatic.com/mda-jbppbefbbztvws50/sc/mda-jbppbefbbztvws50.mp4"
    //封面图
    private val coverImageUrl =
        "https://img1.baidu.com/it/u=265818744,2982786856&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800"

    private val mList: MutableList<MyPlayer> = ArrayList()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }

    private fun init() {

        for (i in 0..19) {
            val video = MyPlayer(videoUrl, coverImageUrl)
            mList.add(video);
        }
        val layoutManager = LinearLayoutManager(this@MainActivity)
        layoutManager.orientation = LinearLayoutManager.VERTICAL
        recyclerview.layoutManager = layoutManager
        recyclerview.adapter = mAdapter
        mAdapter.setList(mList)

        recyclerview.addOnScrollListener(AutoPlayScrollListener(this@MainActivity))

    }

    /**
     * 监听recycleView滑动状态,
     * 自动播放可见区域内的第一个视频
     */
    private class AutoPlayScrollListener(private val context: Context) : RecyclerView.OnScrollListener() {
        private var firstVisibleItem = 0
        private var lastVisibleItem = 0
        private var visibleCount = 0

        /**
         * 被处理的视频状态标签
         */
        private enum class VideoTagEnum {
            /**
             * 自动播放视频
             */
            TAG_AUTO_PLAY_VIDEO,

            /**
             * 暂停视频
             */
            TAG_PAUSE_VIDEO
        }

        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)
            when (newState) {
                RecyclerView.SCROLL_STATE_IDLE -> autoPlayVideo(
                    recyclerView,
                    VideoTagEnum.TAG_AUTO_PLAY_VIDEO
                )
                RecyclerView.SCROLL_STATE_DRAGGING, RecyclerView.SCROLL_STATE_SETTLING -> {}
                else -> {}
            }
        }

        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val layoutManager = recyclerView.layoutManager
            if (layoutManager is LinearLayoutManager) {
                val linearManager = layoutManager
                firstVisibleItem = linearManager.findFirstVisibleItemPosition()
                lastVisibleItem = linearManager.findLastVisibleItemPosition()
                visibleCount = lastVisibleItem - firstVisibleItem
            }
        }

        /**
         * 循环遍历可见区域的播放器
         * 然后通过 getLocalVisibleRect(rect)方法计算出哪个播放器完全显示出来
         * @param recyclerView
         * @param handleVideoTag 视频需要进行状态
         */
        private fun autoPlayVideo(recyclerView: RecyclerView?, handleVideoTag: VideoTagEnum) {
            for (i in 0 until visibleCount) {
                if (recyclerView != null && recyclerView.getChildAt(i) != null && recyclerView.getChildAt(i).findViewById<MyJzvdStd>(R.id.videoplayer) != null) {
                    val homeGSYVideoPlayer = recyclerView.getChildAt(i).findViewById<MyJzvdStd>(R.id.videoplayer) as MyJzvdStd
                    val rect = Rect()
                    homeGSYVideoPlayer.getLocalVisibleRect(rect)
                    val videoheight = homeGSYVideoPlayer.height
                    if (rect.top == 0 && rect.bottom == videoheight) {
                        handleVideo(handleVideoTag, homeGSYVideoPlayer)
                        // 跳出循环,只处理可见区域内的第一个播放器
                        break
                    }
                }
            }
        }

        /**
         * 视频状态处理
         *
         * @param handleVideoTag     视频需要进行状态
         * @param homeGSYVideoPlayer JZVideoPlayer播放器
         */
        private fun handleVideo(handleVideoTag: VideoTagEnum, homeGSYVideoPlayer: MyJzvdStd) {
            when (handleVideoTag) {
                VideoTagEnum.TAG_AUTO_PLAY_VIDEO -> if (homeGSYVideoPlayer.state !== Jzvd.STATE_PLAYING) {
                    // 进行播放
                    homeGSYVideoPlayer.startVideo()
                }
                VideoTagEnum.TAG_PAUSE_VIDEO -> if (homeGSYVideoPlayer.state !== Jzvd.STATE_PAUSE) {
                    // 模拟点击,暂停视频
                    homeGSYVideoPlayer.startButton.performClick()
                }
                else -> {}
            }
        }
    }

    /**
     * 拦截返回键 返回不退出程序
     */
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (Jzvd.backPress()) {
            return true
        } else {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                moveTaskToBack(true)
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" />
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐