国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于GSYVideoPlayer自定義布局結合RecyclerView高仿抖音實現上下滑動雙擊屏幕點贊/單擊暫停,拖動進度條實時改變時間以及進度條放大

這篇具有很好參考價值的文章主要介紹了基于GSYVideoPlayer自定義布局結合RecyclerView高仿抖音實現上下滑動雙擊屏幕點贊/單擊暫停,拖動進度條實時改變時間以及進度條放大。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

注意代碼量有點多,但是你不要就此放棄,看效果圖決定你需不需該需求??并且代碼好理解基本上都是基于GSYVideoPlayer的方法進行重寫改造出來的,請放心食用

GSYVideoPlayer是一款開源并且強大的Android視頻播放器方便你們閱讀了GSYVideoPlayer更快速的上手GSYVideoPlayer框架地址

效果圖預覽

基于GSYVideoPlayer自定義布局高仿抖音

1.添加 GSYVideoPlayer依賴

    //GSYVideoPlayer播放器
    implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer:v8.3.5-release-jitpack'
    //GSYVideoPlayer播放器是否需要AliPlayer模式
    implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-aliplay:v8.3.5-release-jitpack'

2.自定義GSYVideoPlayer布局?

package com.zyy.inpaint

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.app.Activity
import android.app.Service
import android.content.Context
import android.graphics.Matrix
import android.graphics.Point
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Message
import android.os.Vibrator
import android.text.TextUtils
import android.util.AttributeSet
import android.view.*
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.SeekBar
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.shuyu.gsyvideoplayer.utils.CommonUtil
import com.shuyu.gsyvideoplayer.utils.Debuger
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer
import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.abs


open class SampleCoverVideo : StandardGSYVideoPlayer {

    var onLikeListener: () -> Unit = {}//屏幕點贊后,點贊按鈕需同步點贊

    private var mCoverImage: ImageView? = null
    private var mCoverOriginUrl: String? = null
    private var mCoverOriginId: Int = 0
    private var mDefaultRes: Int? = null
    private var videoStart: ImageView? = null
    private var startVessel: LinearLayout? = null
    private var layoutTime: LinearLayout? = null

    private var icon: Drawable = resources.getDrawable(R.drawable.like_not,null)
    private var mClickCount = 0 //點擊一次是暫停,多次是點贊
    private var mHandler = LikeLayoutHandler(this)

    private var startTime = System.currentTimeMillis()

    constructor(context: Context, fullFlag: Boolean) : super(context,fullFlag)
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    init {
        //GSYVideoManager.instance().optionModelList = GSYVideoPlayerOptimize.videoOptionModelOptimize()

        mCoverImage = findViewById(R.id.thumb_image)
        startVessel = findViewById(R.id.startVessel)
        videoStart = findViewById(R.id.videoStart)
        layoutTime = findViewById(R.id.layout_time)

        mProgressBar.apply {
            layoutParams.height = Util.dip2px(context, 18f) //初始化進度條高度
            isClickable = false
            isFocusable = false
        }

        if(mThumbImageViewLayout != null && (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)) {
            mThumbImageViewLayout.visibility = VISIBLE
        }

        clipChildren = false // 避免旋轉時點贊被遮擋
    }
    //監(jiān)聽觸摸,實際上是重寫GSYVideoPlayer中的監(jiān)聽觸摸
    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        val id = v!!.id
        val x = event!!.x
        val y = event.y

        if (mIfCurrentIsFullscreen && mLockCurScreen && mNeedLockFull) {
            onClickUiToggle(event)
            startDismissControlViewTimer()
            return true
        }

        if (id == R.id.fullscreen) {
            return false
        }
        if (id == R.id.surface_container) {
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    touchSurfaceDown(x, y)
                    startTime = System.currentTimeMillis()
                }
                MotionEvent.ACTION_MOVE -> {
                    val deltaX = x - mDownX
                    val deltaY = y - mDownY
                    val absDeltaX = abs(deltaX)
                    val absDeltaY = abs(deltaY)
                    if (mIfCurrentIsFullscreen && mIsTouchWigetFull
                        || mIsTouchWiget && !mIfCurrentIsFullscreen
                    ) {
                        if (!mChangePosition && !mChangeVolume && !mBrightness) {
                            touchSurfaceMoveFullLogic(absDeltaX, absDeltaY)
                        }
                    }
                    touchSurfaceMove(deltaX, deltaY, y)
                }
                MotionEvent.ACTION_UP -> {
                    startDismissControlViewTimer()
                    touchSurfaceUp()
                    Debuger.printfLog(this.hashCode()
                        .toString() + "------------------------------ surface_container ACTION_UP")
                    startProgressTimer()

                    //不要和隱藏虛擬按鍵后,滑出虛擬按鍵沖突
                    if (mHideKey && mShowVKey) {
                        return true
                    }
                    handler.removeCallbacksAndMessages(null)
                    //判斷當前時間戳 減去 ACTION_DOWN按住時獲取的時間戳 是否大于500
                    if (System.currentTimeMillis() - startTime >= 500) {
                        //vibrate(context as Activity, longArrayOf(800, 1000, 800, 1000, 800, 1000),false)
                    } else {
                        // 點擊事件處理
                        mClickCount++
                        mHandler.removeCallbacksAndMessages(null)
                        if(mClickCount >= 2){
                            addHeartView(x, y)
                            onLikeListener()
                            mHandler.sendEmptyMessageDelayed(1, 500)
                        }else{
                            mHandler.sendEmptyMessageDelayed(0, 500)
                        }
                    }
                }
            }
            gestureDetector.onTouchEvent(event)
        } else if (id == R.id.progress) {
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    cancelDismissControlViewTimer()
                    cancelProgressTimer()
                    var vpdown = parent
                    while (vpdown != null) {
                        vpdown.requestDisallowInterceptTouchEvent(true)
                        vpdown = vpdown.parent
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    cancelProgressTimer()
                    var vpdown = parent
                    while (vpdown != null) {
                        vpdown.requestDisallowInterceptTouchEvent(true)
                        vpdown = vpdown.parent
                    }
                }
                MotionEvent.ACTION_UP -> {
                    startDismissControlViewTimer()
                    Debuger.printfLog(this.hashCode()
                        .toString() + "------------------------------ progress ACTION_UP")
                    startProgressTimer()
                    var vpup = parent
                    while (vpup != null) {
                        vpup.requestDisallowInterceptTouchEvent(false)
                        vpup = vpup.parent
                    }
                    mBrightnessData = -1f
                }

            }
        }

        return false
    }

    /**
     * 在Layout中添加紅心并,播放消失動畫
     */
    private fun addHeartView(x: Float, y: Float){
        val lp = LayoutParams(icon.intrinsicWidth, icon.intrinsicHeight) //計算點擊的點位紅心的下部中間
        lp.leftMargin = (x - icon.intrinsicWidth / 2).toInt()
        lp.topMargin = (y - icon.intrinsicHeight).toInt()
        val img = ImageView(context)
        img.scaleType = ImageView.ScaleType.MATRIX
        val matrix = Matrix()
        matrix.postRotate(getRandomRotate()) //設置紅心的微量偏移
        img.imageMatrix = matrix
        img.setImageDrawable(icon)
        img.layoutParams = lp
        addView(img)
        val animSet = getShowAnimSet(img)
        val hideSet = getHideAnimSet(img)
        animSet.start()
        animSet.addListener(object : AnimatorListenerAdapter(){
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                hideSet.start()
            }
        })
        hideSet.addListener(object : AnimatorListenerAdapter(){
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                removeView(img)//動畫結束移除紅心
            }
        })
    }

    /**
     * 生成一個隨機的左右偏移量
     */
    private fun getRandomRotate(): Float = (Random().nextInt(20) - 10).toFloat()

    /**
     * 剛點擊的時候的一個縮放效果
     */
    private fun getShowAnimSet(view: ImageView): AnimatorSet{
        // 縮放動畫
        val scaleX = ObjectAnimator.ofFloat(view, "scaleX",1.2f,1f)
        val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.2f,1f)
        val animSet = AnimatorSet()
        animSet.playTogether(scaleX, scaleY)
        animSet.duration = 100
        return animSet
    }

    /**
     * 縮放結束后到紅心消失的效果
     */
    private fun getHideAnimSet(view: ImageView): AnimatorSet{
        // 1.alpha動畫
        val alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0.1f)
        // 2.縮放動畫
        val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f)
        val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 2f)
        // 3.translation動畫
        val translation = ObjectAnimator.ofFloat(view, "translationY", 0f, -150f)
        val animSet = AnimatorSet()
        animSet.playTogether(alpha, scaleX, scaleY, translation)
        animSet.duration = 500
        return animSet
    }

    private fun pauseClick(){
        if(mClickCount == 1){
            clickStartIcon() //播放視頻或暫停
            showHiddenButtonStart() //顯示或隱藏播放按鈕
        }
        mClickCount = 0
    }

    fun onPause(){
        mClickCount = 0
        mHandler.removeCallbacksAndMessages(null)
    }

    companion object{
        @Suppress("DEPRECATION")
        private class LikeLayoutHandler(view: SampleCoverVideo): Handler(){
            private val mView = WeakReference(view)
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                when(msg.what){
                    0 -> mView.get()?.pauseClick()
                    1 -> mView.get()?.onPause()
                }
            }
        }
        //真機震動
        private fun vibrate(activity: Activity, pattern: LongArray, isRepeat: Boolean) {
            val vib =  activity.getSystemService(Service.VIBRATOR_SERVICE) as Vibrator
            vib.vibrate(pattern, if(isRepeat) 1 else -1)
        }

    }

    override fun getLayoutId(): Int {
        return R.layout.layout_video_cover
    }

    fun loadCoverImage(url: String, res: Int){
        mCoverOriginUrl = url
        mDefaultRes = res
        Glide.with(context.applicationContext)
            .setDefaultRequestOptions(RequestOptions()
                .frame(100000)
                .centerCrop()
                .error(res)
                .placeholder(res))
            .load(url)
            .into(mCoverImage!!)
    }

    fun loadCoverImageBy(id: Int, res: Int){
        mCoverOriginId = id
        mDefaultRes = res
        mCoverImage?.setImageResource(id)
    }

    override fun startWindowFullscreen(
        context: Context?,
        actionBar: Boolean,
        statusBar: Boolean,
    ): GSYBaseVideoPlayer {
        val gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar)
        val sampleCoverVideo = gsyBaseVideoPlayer as SampleCoverVideo
        if(mCoverOriginUrl != null) {
            mDefaultRes?.let { sampleCoverVideo.loadCoverImage(mCoverOriginUrl!!, it) }
        } else  if(mCoverOriginId != 0) {
            mDefaultRes?.let { sampleCoverVideo.loadCoverImageBy(mCoverOriginId, it) }
        }
        return gsyBaseVideoPlayer
    }

    override fun showSmallVideo(
        size: Point?,
        actionBar: Boolean,
        statusBar: Boolean,
    ): GSYBaseVideoPlayer {
        //下面這里替換成你自己的強制轉化
        val sampleCoverVideo =  super.showSmallVideo(size, actionBar, statusBar) as SampleCoverVideo
        sampleCoverVideo.startVessel?.visibility = GONE
        sampleCoverVideo.startVessel = null
        return sampleCoverVideo
    }

    override fun cloneParams(from: GSYBaseVideoPlayer?, to: GSYBaseVideoPlayer?) {
        super.cloneParams(from, to)
        val sf = from as SampleCoverVideo
        val st = to as SampleCoverVideo
        st.mShowFullAnimation = sf.mShowFullAnimation
    }
    /**
     * 退出window層播放全屏效果
     */
    @SuppressWarnings("ResourceType")
    override fun clearFullscreenLayout() {
        if(!mFullAnimEnd){
            return
        }
        var delay = 0
        mIfCurrentIsFullscreen = false
        // ------- ?。?!如果不需要旋轉屏幕,可以不調用?。。?------
        // 不需要屏幕旋轉,還需要設置 setNeedOrientationUtils(false)
        if(mOrientationUtils != null){
            delay = mOrientationUtils.backToProtVideo()
            mOrientationUtils.isEnable = false
            if (mOrientationUtils != null) {
                mOrientationUtils.releaseListener()
                mOrientationUtils = null
            }
        }
        if (!mShowFullAnimation) {
            delay = 0
        }
        val vp = (CommonUtil.scanForActivity(context)).findViewById<View>(Window.ID_ANDROID_CONTENT)
        val oldF = vp.findViewById<View>(fullId)
        if (oldF != null) {
            //此處fix bug#265,推出全屏的時候,虛擬按鍵問題
            val gsyVideoPlayer = oldF as SampleCoverVideo
            gsyVideoPlayer.mIfCurrentIsFullscreen = false
        }

        if (delay == 0) {
            backToNormal()
        } else {
            postDelayed({
                run {
                    backToNormal()
                }
            }, delay.toLong())
        }
    }
    /******************* 下方兩個重載方法,在播放開始前不屏蔽封面,不需要可屏蔽 ********************/
    override fun onSurfaceUpdated(surface: Surface?) {
        super.onSurfaceUpdated(surface)
        if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == VISIBLE) {
            mThumbImageViewLayout.visibility = INVISIBLE
        }
    }

    override fun setViewShowState(view: View?, visibility: Int) {
        if (view == mThumbImageViewLayout && visibility != VISIBLE) {
            return
        }
        super.setViewShowState(view, visibility)
    }

    override fun onSurfaceAvailable(surface: Surface?) {
        super.onSurfaceAvailable(surface)
        if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) {
            if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == VISIBLE) {
                mThumbImageViewLayout.visibility = INVISIBLE
            }
        }
    }
    /******************* 下方重載方法,在播放開始不顯示底部進度和按鍵,不需要可屏蔽 ********************/
    private var byStartedClick: Boolean? = null
    override fun onClickUiToggle(e: MotionEvent?) {
        if (mIfCurrentIsFullscreen && mLockCurScreen && mNeedLockFull) {
            setViewShowState(mLockScreen, VISIBLE)
            return
        }

        byStartedClick = true
        super.onClickUiToggle(e)
    }

    override fun changeUiToNormal() {
        super.changeUiToNormal()
        byStartedClick = false
    }

    override fun changeUiToPreparingShow() {
        super.changeUiToPreparingShow()
        Debuger.printfLog("Sample changeUiToPreparingShow")
        setViewShowState(layoutTime, INVISIBLE)
        setViewShowState(startVessel, INVISIBLE)
    }

    override fun changeUiToPlayingBufferingShow() {
        super.changeUiToPlayingBufferingShow()
        Debuger.printfLog("Sample changeUiToPlayingBufferingShow")
        if (!byStartedClick!!) {
            setViewShowState(layoutTime, INVISIBLE)
            setViewShowState(startVessel, INVISIBLE)
        }
    }

    override fun changeUiToPlayingShow() {
        super.changeUiToPlayingShow()
        Debuger.printfLog("Sample changeUiToPlayingShow")
        if (!byStartedClick!!) {
            setViewShowState(layoutTime, INVISIBLE)
            setViewShowState(startVessel, INVISIBLE)
        }
    }

    override fun startAfterPrepared() {
        super.startAfterPrepared()
        Debuger.printfLog("Sample startAfterPrepared")
        setViewShowState(layoutTime, INVISIBLE)
        setViewShowState(startVessel, INVISIBLE)
        setViewShowState(mBottomProgressBar, VISIBLE)
    }

    /**
     * 重載雙擊方法,不允許繼承父級,就可以關閉雙擊暫停和播放
     */
    override fun touchDoubleUp(e: MotionEvent?) {
    }

    override fun onClick(v: View?) {
        val i = v!!.id
        if (mHideKey && mIfCurrentIsFullscreen) {
            CommonUtil.hideNavKey(mContext)
        }
        if (i == R.id.surface_container && mCurrentState == CURRENT_STATE_ERROR) {
            if (!mSurfaceErrorPlay) {
                onClickUiToggle(null)
                return
            }
            if (mVideoAllCallBack != null) {
                Debuger.printfLog("onClickStartError")
                mVideoAllCallBack.onClickStartError(mOriginUrl, mTitle, this)
            }
            prepareVideo()
        } else if (i == R.id.thumb) {
            if (!mThumbPlay) {
                return
            }
            if (TextUtils.isEmpty(mUrl)) {
                Debuger.printfError("********" + resources.getString(com.shuyu.gsyvideoplayer.R.string.no_net))
                return
            }
            if (mCurrentState == CURRENT_STATE_NORMAL) {
                if (isShowNetConfirm) {
                    showWifiDialog()
                    return
                }
                startPlayLogic()
            } else if (mCurrentState == CURRENT_STATE_AUTO_COMPLETE) {
                onClickUiToggle(null)
            }
        }
    }

    /**
     * 在Android應用中播放一個名為"heartbeat"的動畫,
     * 并將該動畫應用于startVessel視圖。動畫播放結束后,startVessel視圖會被設置為不可見狀態(tài)。其中,pulseAnimation是動畫的實例,
     * context是應用上下文,R.anim.heartbeat是動畫資源的ID,startVessel是要應用動畫的視圖,setViewShowState()是自定義的一個方法,
     * 用于設置視圖的可見性狀態(tài)。
     */
    fun showHiddenButtonStart(){
        if(mCurrentState == CURRENT_STATE_PLAYING){
            //控件淡出動畫效果
            val pulseAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_out)
            startVessel?.startAnimation(pulseAnimation)
            setViewShowState(startVessel, INVISIBLE)
        }else if(mCurrentState == CURRENT_STATE_PAUSE){
            //控件淡入動畫效果
            val pulseAnimation = AnimationUtils.loadAnimation(context, R.anim.heartbeat)
            startVessel?.startAnimation(pulseAnimation)
            setViewShowState(startVessel, VISIBLE)
        }
    }

    // 重寫進度條實時監(jiān)聽的操作
    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        //判斷是否是用戶滑動
        if(fromUser){
            if (gsyVideoManager != null && mHadPlay) {
                setViewShowState(layoutTime, VISIBLE)
                try {
                    val time = seekBar!!.progress * duration / 100
                    mCurrentTimeTextView.text = CommonUtil.stringForTime(time)
                } catch (e: Exception) {
                    Debuger.printfWarning(e.toString())
                }
            }
        }
        mHadSeekTouch = false
    }
    // 重寫用戶停止拖動進度條時的操作
    override fun onStopTrackingTouch(seekBar: SeekBar?) {
        seekBar?.isClickable = true
        setViewShowState(layoutTime, INVISIBLE)
        if(seekBar != null){
            seekBar.layoutParams.height = Util.dip2px(context, 18f)
            //動態(tài)修改SeekBar的高度
        }
        if (mVideoAllCallBack != null && isCurrentMediaListener) {
            if (isIfCurrentIsFullscreen) {
                Debuger.printfLog("onClickSeekbarFullscreen")
                mVideoAllCallBack.onClickSeekbarFullscreen(mOriginUrl, mTitle, this)
            } else {
                Debuger.printfLog("onClickSeekbar")
                mVideoAllCallBack.onClickSeekbar(mOriginUrl, mTitle, this)
            }
        }
        if (gsyVideoManager != null && mHadPlay) {
            try {
                val time = seekBar!!.progress * duration / 100
                gsyVideoManager.seekTo(time)
            } catch (e: java.lang.Exception) {
                Debuger.printfWarning(e.toString())
            }
        }
        mHadSeekTouch = false
    }
    //用戶開始觸碰進度條觸發(fā)
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        seekBar?.isClickable = false
        if (seekBar != null){
           seekBar.layoutParams.height = Util.dip2px(context, 25f)
        }
        super.onStartTrackingTouch(seekBar)
    }

    override fun onVideoResume() {
        super.onVideoResume()
        setViewShowState(startVessel, INVISIBLE)
    }
    //Android SeekBar禁止點擊允許滑動


}

3.自定義布局控件

? ? ? ? 3.1 layout_video_cover.xm

<?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">
    <FrameLayout
        android:id="@+id/surface_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">

    </FrameLayout>

    <RelativeLayout
        android:id="@+id/thumb"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:gravity="center">
        <ImageView
            android:id="@+id/thumb_image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:adjustViewBounds="true" />
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/layout_time"
        android:layout_width="match_parent"
        android:layout_marginBottom="20dp"
        android:layout_height="40dp"
        android:gravity="center"
        android:orientation="horizontal"
        android:layout_above="@+id/layout_bottom_lzy"
        android:visibility="visible">

        <TextView
            android:id="@+id/current"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"
            android:textSize="20sp"
            android:textColor="#ffffff"
            android:layout_marginEnd="10dp"/>

        <TextView
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="/"
            android:textSize="20sp"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/total"
            android:layout_marginLeft="10sp"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"
            android:textSize="20sp"
            android:textColor="#ffffff" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout_bottom_lzy"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:layout_alignParentBottom="true"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        >

        <SeekBar
            android:id="@+id/progress"
            android:layout_width="0dp"
            android:paddingBottom="8dp"
            android:paddingTop="8dp"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:layout_weight="1.0"
            android:background="@null"
            android:max="100"
            android:progressDrawable="@drawable/mx_bottom_seek_progress"
            android:thumb="@drawable/tiktok_seek_thumb" />

        <ImageView
            android:id="@+id/fullscreen"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:paddingRight="16dp"
            android:scaleType="center"
            android:src="@drawable/video_enlarge"
            android:visibility="gone" />
    </LinearLayout>


    <ImageView
        android:id="@+id/back_tiny"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginLeft="6dp"
        android:layout_marginTop="6dp"
        android:visibility="gone" />

    <LinearLayout
        android:id="@+id/layout_top"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="@drawable/video_title_bg"
        android:gravity="center_vertical">

        <ImageView
            android:id="@+id/back"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:paddingLeft="10dp"
            android:scaleType="centerInside"
            android:src="@drawable/video_back" />

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:textColor="@android:color/white"
            android:textSize="18sp" />
    </LinearLayout>

    <moe.codeest.enviews.ENDownloadView
        android:id="@+id/loading"
        android:layout_width="28dp"
        android:layout_height="28dp"
        android:layout_centerInParent="true"
        android:visibility="invisible" />

    <LinearLayout
        android:id="@+id/startVessel"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_gravity="center"
        android:gravity="center"
        android:visibility="invisible"
        android:background="@drawable/shaper_button_play">

        <ImageView
            android:id="@+id/videoStart"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@drawable/play"
            android:layout_marginLeft="3dp" />

    </LinearLayout>

    <ImageView
        android:id="@+id/small_close"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:paddingLeft="10dp"
        android:paddingTop="10dp"
        android:scaleType="centerInside"
        android:src="@drawable/video_small_close"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/lock_screen"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="50dp"
        android:scaleType="centerInside"
        android:src="@drawable/unlock"
        android:visibility="gone" />

</RelativeLayout>

? ? ? ? 3.2 設置SeekBar進度條的樣式(顏色我就不貼了自己嘗試)設置播放按鈕暫停和播放時控件顯示的動畫

? ? ? ? ????????3.2.1 設置SeekBar進度條樣式mx_bottom_seek_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="@color/lucency_gray" />
            <size android:height="4dp" />
            <corners android:radius="3dp" />
        </shape>
    </item>
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <solid android:color="@color/lucency_gray_lode" />
                <size android:height="4dp" />
                <corners android:radius="3dp" />
            </shape>
        </clip>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="@color/mx_player_color_progress" />
                <size android:height="4dp" />
                <corners android:radius="3dp" />
            </shape>
        </clip>
    </item>
</layer-list>

? ? ? ? ????????3.2.2??設置播放按鈕暫停和播放時控件顯示的動畫(在res目錄下創(chuàng)建anim包)

android gsyvideoplayer,Android,Java,Kotlin,android,kotlin,java

? ? ? ? ? ? ? ? fade_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/accelerate_interpolator"
        android:duration="200"
        android:fillAfter="true"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0"
        android:toYScale="0" />
    <!--同時配置淡出功能-->
    <alpha
        android:duration="100"
        android:fillAfter="true"
        android:fromAlpha="1"
        android:toAlpha="0" />
    <translate
        android:duration="500"
        android:fromYDelta="0"
        android:toYDelta="-100%"
        />
</set>

? ? ? ? ? ? ? ? heartbeat.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="200"
        android:fillAfter="true"
        android:fillEnabled="true"
        android:fromXScale="0"
        android:fromYScale="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <!--同時配置淡入功能-->
    <alpha
        android:duration="100"
        android:fillAfter="true"
        android:fromAlpha="0"
        android:toAlpha="1" />
    <translate
        android:duration="500"
        android:fromYDelta="0"
        android:toYDelta="0" />
</set>

? ? ? ?3.3 設置SeekBar進度條點擊和沒被點擊時thumb(拇指)的顏色

? ? ? ? ????????3.3.1 tiktok_seek_thumb.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/video_seek_thumb_select" android:state_pressed="true" />
    <item android:drawable="@drawable/video_seek_thumb_unselected" />
</selector>

? ? ? ? ? ? ? ? 3.3.2 video_seek_thumb_select.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#ffffffff" />
    <size
        android:width="14.0dip"
        android:height="14.0dip" />
</shape>

? ? ? ? ? ? ? ? 3.3.3 video_seek_thumb_unselected.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/seek_thumb_red" />
    <size
        android:width="5.0dip"
        android:height="5.0dip" />
</shape>

? ? ? ? 3.4 RecyclerView的子布局item 就是添加自定義布局控件,需要其他控件請自行添

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black">
        <com.zyy.inpaint.SampleCoverVideo
            android:id="@+id/video_item_player"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4. 創(chuàng)建工具類 ScrollCalculatorHelper 用于操作播放邏輯

package com.zyy.inpaint

import android.app.AlertDialog
import android.content.Context
import android.graphics.Rect
import android.os.Handler
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.shuyu.gsyvideoplayer.utils.NetworkUtils
import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer
import kotlinx.coroutines.Runnable

class ScrollCalculatorHelper() {
    private var firstVisible = 0
    private var lastVisible = 0
    private var visibleCount = 0
    private var playId: Int? = null
    var rangeTop: Int? = null
    var rangeBottom: Int? = null
    private var runnable: PlayRunnable? = null

    private val playHandler: Handler = Handler()

    constructor(playId: Int, rangeTop: Int, rangeBottom: Int) : this() {
        this.playId = playId
        this.rangeTop = rangeTop
        this.rangeBottom= rangeBottom
    }

    fun onScrollStateChanged(view: RecyclerView, scrollState: Int){
        when(scrollState){
            RecyclerView.SCROLL_STATE_IDLE -> playVideo(view)
        }
    }

    fun onScroll(view: RecyclerView, firstVisibleItem: Int, lastVisibleItem: Int, visibleItemCount: Int){
        if (firstVisible == firstVisibleItem) {
            return
        }
        firstVisible = firstVisibleItem
        lastVisible = lastVisibleItem
        visibleCount = visibleItemCount
    }

    private fun playVideo(view: RecyclerView){
        val layoutManager: RecyclerView.LayoutManager? = view.layoutManager

        var gsyBaseVideoPlayer: GSYBaseVideoPlayer? = null

        var needPlay = false
        for (i in 0..visibleCount){
            if (layoutManager?.getChildAt(i) != null && layoutManager.getChildAt(i)!!.findViewById<View>(
                    playId!!) != null) {
                val player = layoutManager.getChildAt(i)!!.findViewById<View>(playId!!) as GSYBaseVideoPlayer
                val rect = Rect()
                player.getLocalVisibleRect(rect)
                val height = player.height
                //說明第一個完全可視
                if(rect.top == 0 && rect.bottom == height){
                    gsyBaseVideoPlayer = player
                    if ((player.currentPlayer.currentState == GSYBaseVideoPlayer.CURRENT_STATE_NORMAL
                                || player.currentPlayer.currentState == GSYBaseVideoPlayer.CURRENT_STATE_ERROR)) {
                        needPlay = true
                    }
                    break
                }
            }
        }
        if (gsyBaseVideoPlayer != null && needPlay) {
            if (runnable != null) {
                val tmpPlayer = runnable?.gsyBaseVideoPlayer
                playHandler.removeCallbacks(runnable!!)
                runnable = null
                if (tmpPlayer == gsyBaseVideoPlayer) {
                    return
                }
            }
            runnable = PlayRunnable(gsyBaseVideoPlayer, rangeTop!!, rangeBottom!!)
            //降低頻率
            playHandler.postDelayed(runnable!!, 400)
        }
    }

    class PlayRunnable(gsyBaseVideoPlayer: GSYBaseVideoPlayer, rangeTop: Int, rangeBottom: Int) : Runnable{
        var gsyBaseVideoPlayer: GSYBaseVideoPlayer? = gsyBaseVideoPlayer
        val rangeTop1 = rangeTop
        val rangeBottom1 = rangeBottom
        override fun run() {
            var inPosition = false
            //如果未播放, 需要播放
            if(gsyBaseVideoPlayer != null){
                val screenPosition = IntArray(2)
                gsyBaseVideoPlayer?.getLocationOnScreen(screenPosition)
                val halfHeight = gsyBaseVideoPlayer?.height!! / 2
                val rangePosition = screenPosition[1] + halfHeight
                if(rangePosition in (rangeTop1 + 1)..rangeBottom1){
                    inPosition = true
                }
                if(inPosition){
                    gsyBaseVideoPlayer?.context?.let {
                        ScrollCalculatorHelper().startPlayLogic(gsyBaseVideoPlayer!!,
                            it)
                    }
                    //gsyBaseVideoPlayer.startPlayLogic();
                }
            }
        }
    }
    /***************************************自動播放的點擊播放確認******************************************/
    private fun startPlayLogic(gsyBaseVideoPlayer: GSYBaseVideoPlayer, context: Context){
        if (!com.shuyu.gsyvideoplayer.utils.CommonUtil.isWifiConnected(context)) {
            //這里判斷是否wifi
            showWifiDialog(gsyBaseVideoPlayer, context)
            return
        }
        gsyBaseVideoPlayer.startPlayLogic()
    }

    private fun showWifiDialog(gsyBaseVideoPlayer: GSYBaseVideoPlayer, context: Context){
        if (!NetworkUtils.isAvailable(context)) {
            Toast.makeText(context, context.resources.getString(com.shuyu.gsyvideoplayer.R.string.no_net), Toast.LENGTH_LONG).show()
            return
        }
        Toast.makeText(context, context.resources.getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi),Toast.LENGTH_LONG).show()
        gsyBaseVideoPlayer.startPlayLogic()
       /* val builder = AlertDialog.Builder(context)
        builder.setMessage(context.resources.getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi))
        builder.setPositiveButton(context.resources.getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_confirm)
        ) { dialog, _ ->
            dialog?.dismiss()
            gsyBaseVideoPlayer.startPlayLogic()
        }
        builder.setNegativeButton(context.resources.getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_cancel)
        ) { dialog, _ -> dialog?.dismiss() }
        builder.create().show()*/
    }
}

5. 主Activity MainActivity 如果你們是Fragment一樣的代碼不需要改動 activity中的布局只有一個RecyclerView (videos是網絡視頻路徑,我這里做的時假數據,如果網絡請求也是同理,Paging3在RecyclerView中獲取網絡視頻路徑,基本上按照我下面的代碼,你用不用Paging3都是這樣寫)

package com.zyy.inpaint

import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.transition.Explode
import android.view.Window
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.utils.CommonUtil
import com.zyy.inpaint.databinding.ActivityMainBinding
import com.zyy.inpaint.view_model.ActivityViewModel


class MainActivity : AppCompatActivity() {
    private val TAG = MainActivity::class.simpleName
    private lateinit var atViewModel: ActivityViewModel
    private var pagerSnapHelper: PagerSnapHelper? = null
    private var shrotVideoAdapter: RecyclerNormalAdapter? = null
    private lateinit var scrollCalculatorHelper: ScrollCalculatorHelper
    private val binding: ActivityMainBinding by lazy{
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
            window.enterTransition = Explode()
            window.exitTransition = Explode()
        }
        setContentView(binding.root)
        //限定范圍為屏幕一半的上下偏移180
        val playTop = CommonUtil.getScreenHeight(this) / 2 - CommonUtil.dip2px(this, 180f)
        val playBottom = CommonUtil.getScreenHeight(this) / 2 + CommonUtil.dip2px(this, 180f)
        scrollCalculatorHelper = ScrollCalculatorHelper(R.id.video_item_player, playTop, playBottom)

        val videos = listOf(
            "https://vd2.bdstatic.com/mda-pa8gdgeje11q0juv/sc/cae_h264/1673265652790205165/mda-pa8gdgeje11q0juv.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1673794885-0-0-3ed5f7ef458037d258774634a28c24bc&bcevod_channel=searchbox_feed&pd=1&cd=0&pt=3&logid=1885824382&vid=3653968767863324420&abtest=106846_2-106991_1&klogid=1885824382",
            "https://vd2.bdstatic.com/mda-paebp8x9m1f23277/sc/cae_h264/1673771465094233171/mda-paebp8x9m1f23277.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1673835039-0-0-82fe31f4dceacc147007fc4a04b86294&bcevod_channel=searchbox_feed&pd=1&cd=0&pt=3&logid=2439604427&vid=5532715800795306313&abtest=106846_2-106991_1&klogid=2439604427",
            "https://vd2.bdstatic.com/mda-nar659w01c98k85d/sc/cae_h264_nowatermark_delogo/1643171162969466355/mda-nar659w01c98k85d.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1673835494-0-0-b89e8c9cc6acd00019738c807647eba9&bcevod_channel=searchbox_feed&pd=1&cd=0&pt=3&logid=2894791446&vid=11604659103229506139&abtest=106846_2-106991_1&klogid=2894791446",
            "https://vd2.bdstatic.com/mda-mc2h3k988k3b1mbh/v1-cae/sc/mda-mc2h3k988k3b1mbh.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1673794689-0-0-85852c703a917d14a6e07cf48c96dbad&bcevod_channel=searchbox_feed&pd=1&cd=0&pt=3&logid=1689187715&vid=2424761345650013440&abtest=106846_2-106991_1&klogid=1689187715",
            "https://vd2.bdstatic.com/mda-nh99crj71cen9809/sc/cae_h264/1660116949250702198/mda-nh99crj71cen9809.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1673835855-0-0-8fa1bf15105827d15e3774705945b144&bcevod_channel=searchbox_feed&pd=1&cd=0&pt=3&logid=3254897660&vid=6897246374997195249&abtest=106846_2-106991_1&klogid=3254897660",
        )

        atViewModel = ViewModelProvider(this)[ActivityViewModel::class.java]

        binding.videoRecyclerView.apply {
            shrotVideoAdapter = RecyclerNormalAdapter(videos, this@MainActivity)
            //關閉recycleView動畫
            (this.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
            //抖音都在用的recycleView滑動模板
            pagerSnapHelper = PagerSnapHelper()
            pagerSnapHelper?.attachToRecyclerView(this)
            adapter = shrotVideoAdapter
            //recycleView適配
            layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
        }
        binding.videoRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener(){
            var firstVisibleItem: Int? = null
            var lastVisibleItem: Int? = null
            /**
             * @param recyclerView 當前RecyclerView
             * @param dx 水平滾動距離
             * @param dy 垂直滾動距離
             * dx > 0 時為手指向左滑動,列表滾動顯示右面的內容
             * dx < 0 時為手指向右滑動,列表滾動顯示左面的內容
             * dy > 0 時為手指向上滑動,列表滾動顯示下面的內容
             * dy < 0 時為手指向下滑動,列表滾動顯示上面的內容
             */
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                val layoutManager = recyclerView.layoutManager as LinearLayoutManager
                firstVisibleItem = layoutManager.findFirstVisibleItemPosition()// 在屏幕可見區(qū)域的第一項位置 : 通過 findFirstVisibleItemPosition() 方法獲取
                lastVisibleItem = layoutManager.findLastVisibleItemPosition() //在屏幕可見區(qū)域的最后一項位置 : 通過 findLastVisibleItemPosition() 方法獲取
                /**
                 * 為什么需要以上值,這里說下整體思路:
                 *(1)獲取當前處于屏幕可見的列表
                 *(2)滑出屏幕的視頻我們需要回收掉
                 *(3)當屏幕處于靜止狀態(tài)時我們才開始播放視頻
                 */
                //一屏幕顯示一個item 所以固定1
                //實時獲取設置 當前顯示的GSYBaseVideoPlayer的下標
                scrollCalculatorHelper.onScroll(recyclerView, firstVisibleItem!!, lastVisibleItem!!,  1)
            }

            /**
             * 滾動狀態(tài)
             * @param newState 給我們返回三種狀態(tài) 方法體內的三中滾動
             * 屏幕處于靜止時才開始播放,只要播放的邏輯寫在 onScrollStateChanged 的 SCROLL_STATE_IDLE 狀態(tài)下即可;
             */
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                scrollCalculatorHelper.onScrollStateChanged(recyclerView, newState)
            }
        })
    }

    @Deprecated("Deprecated in Java")
    override fun onBackPressed() {
        if(GSYVideoManager.backFromWindowFull(this)){
            return
        }
        super.onBackPressed()
    }

    override fun onPause() {
        super.onPause()
        GSYVideoManager.onPause()
    }

    override fun onResume() {
        super.onResume()
        GSYVideoManager.onResume()
    }

    override fun onDestroy() {
        super.onDestroy()
        GSYVideoManager.releaseAllVideos()
    }


}

6. RecyclerView適配器(RecyclerViewNormalAdapter)

package com.zyy.inpaint

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.zyy.inpaint.databinding.ItemShortVideoBinding

class RecyclerNormalAdapter(var itemDataList: List<String>, var context: Context):
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        var TAG : String = "RecyclerBaseAdapter"
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val view: ItemShortVideoBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context),
            R.layout.item_short_video, parent, false
        )
        return RecyclerItemNormalHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val recyclerItemNormalHolder = holder as RecyclerItemNormalHolder
        recyclerItemNormalHolder.recyclerBaseAdapter = this@RecyclerNormalAdapter
        holder.binding?.videoItemPlayer?.let {
            recyclerItemNormalHolder.onBind(position, itemDataList[position],
                it)
        }
    }

    override fun getItemCount() = itemDataList.size

}

7. ViewHolder (RecyclerItemNormalHodler / RecyclerItemBaseHolder)

? ? ? ?7.1 RecyclerItemNormalHodler .java

package com.zyy.inpaint

import android.content.Context
import android.telecom.VideoProfile
import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.model.VideoOptionModel
import com.zyy.inpaint.databinding.ItemShortVideoBinding
import kotlinx.android.synthetic.main.layout_video_cover.view.*
import tv.danmaku.ijk.media.player.IjkMediaPlayer

class RecyclerItemNormalHolder(view : View): RecyclerItemBaseHolder(view){
    val TAG: String = "RecyclerView2List"
    var imageView: ImageView? = null
    var gsyVideoOptionBuilder: GSYVideoOptionBuilder? = null

    var binding: ItemShortVideoBinding? = null
    var context: Context? = null
    constructor(binding: ItemShortVideoBinding): this(binding.root){
        this.binding = binding
        this.context = app.getContext()
        imageView = ImageView(context)
        gsyVideoOptionBuilder = GSYVideoOptionBuilder()
    }

    fun onBind(position: Int, videoModel: String, gsyVideoPlayer: SampleCoverVideo){
       /* gsyVideoPlayer.onPauseListener = {
            if(gsyVideoPlayer.gsyVideoManager.isPlaying){
                gsyVideoPlayer.onVideoPause()
            } else {
                gsyVideoPlayer.onVideoResume(false)
            }
            gsyVideoPlayer.showHiddenButtonStart() //顯示或隱藏播放按鈕

        }*/

        val url: String = videoModel
        val title: String = "6664565"
        //防止錯位,離開釋放
        //gsyVideoPlayer.initUIState();
        val header: MutableMap<String, String> = HashMap()
        header["ee"] = "33"
        gsyVideoOptionBuilder?.setIsTouchWiget(false)?.setUrl(url)?.setVideoTitle(title)?.setStartAfterPrepared(true)
            ?.setCacheWithPlay(false)?.setRotateViewAuto(false)?.setLockLand(true)?.setPlayTag(TAG)
            ?.setShowFullAnimation(true)?.setNeedLockFull(true)?.setLooping(true)
            ?.setPlayPosition(position)?.setMapHeadData(header)
            ?.setVideoAllCallBack(object : GSYSampleCallBack(){
                override fun onPrepared(url: String?, vararg objects: Any?) {
                    super.onPrepared(url, *objects)
                    if (!gsyVideoPlayer.isIfCurrentIsFullscreen) {
                        //靜音
                        GSYVideoManager.instance().isNeedMute = false
                    }
                }
                override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
                    super.onQuitFullscreen(url, *objects)
                    //全屏不靜音
                    GSYVideoManager.instance().isNeedMute = false
                }
                override fun onEnterFullscreen(url: String?, vararg objects: Any?) {
                    super.onEnterFullscreen(url, *objects)
                    GSYVideoManager.instance().isNeedMute = false
                    gsyVideoPlayer.currentPlayer?.titleTextView?.text = objects[0] as String
                }
            })?.build(gsyVideoPlayer)

        //隱藏title
        gsyVideoPlayer.titleTextView?.visibility = View.GONE
        //設置返回鍵
        gsyVideoPlayer.backButton?.visibility = View.GONE
        //設置全屏按鍵功能
        gsyVideoPlayer.fullscreenButton?.setOnClickListener { resolveFullBtn(gsyVideoPlayer) }
        gsyVideoPlayer.loadCoverImage("https://ts4.cn.mm.bing.net/th?id=OIP-C.D68LlaxVgTAyk2bXd0fLTQHaEK&w=333&h=187&c=8&rs=1&qlt=90&o=6&dpr=1.1&pid=3.1&rm=2", R.mipmap.ic_launcher)
        //實現第一個item視頻自動播放 由于自動播放是監(jiān)聽RecyclerView滑動時觸發(fā),所以第一個item不會自動播放
        if(position == 0){
            gsyVideoPlayer.startPlayLogic()
        }

    }

    /**
     * 全屏幕按鍵處理
     */
    private fun resolveFullBtn(standardGSYVideoPlayer: SampleCoverVideo) {
        standardGSYVideoPlayer.startWindowFullscreen(context, actionBar = false, statusBar = true)
    }



}

? ? ? ?7.2?RecyclerItemBaseHolder

package com.zyy.inpaint

import android.view.View
import androidx.recyclerview.widget.RecyclerView

open class RecyclerItemBaseHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
    //該adapter主要用于操作數據源刷新等等操作,方便Holder直接調用
    lateinit var recyclerBaseAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>
}

8. 工具類用于根據手機分辨率從(dp單位轉化為px(像素) / px像素轉化為dp)

package com.zyy.inpaint

import android.content.Context

class Util {
    companion object{
        /**
         * 根據手機的分辨率從 dp 的單位 轉成為 px(像素)
         */
        fun dip2px(context: Context, dpValue: Float): Int{
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f).toInt()
        }

        /**
         * 根據手機的分辨率從 px(像素) 的單位 轉成為 dp
         */
        fun px2dip(context: Context, pxValue: Float): Int{
            val scale = context.resources.displayMetrics.density
            return (pxValue / scale + 0.5f).toInt()
        }
    }
}

好了到這里就結束了,如有疑問請在評論區(qū)留言

如果對你有幫助請一鍵三連哦!!以防丟失,后續(xù)還會出各種實戰(zhàn)需要使用到的場景(也可以在評論區(qū)或私信留言需要什么功能需求,我盡量滿足大家)

注:如有侵權請私信聯系我刪除文章來源地址http://www.zghlxwxcb.cn/news/detail-776347.html

到了這里,關于基于GSYVideoPlayer自定義布局結合RecyclerView高仿抖音實現上下滑動雙擊屏幕點贊/單擊暫停,拖動進度條實時改變時間以及進度條放大的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • HarmonyOS鴻蒙基于Java開發(fā): Java UI 自定義布局

    當Java UI框架提供的布局無法滿足需求時,可以創(chuàng)建自定義布局,根據需求自定義布局規(guī)則。 Component類相關接口? 表1? Component類相關接口 接口名稱 作用 setEstimateSizeListener 設置測量組件的偵聽器 setEstimatedSize 設置測量的寬度和高度 onEstimateSize 測量組件的大小以確定寬度和高度

    2024年02月19日
    瀏覽(27)
  • [Android]自定義RecyclerView中View的動畫

    官方有一個默認Item動畫類DafaultItemAnimator,其中 DefaultItemAnimator 繼承了SimpleItemAnimator 繼承了 RecyclerView.ItemAnimator SimpleItemAnimator 它是一個包裝類,用來判斷當前的ViewHolder到底是執(zhí)行移動、移除、添加或者改變等行為。 DefaultItemAnimator 是執(zhí)行具體動畫類,它負責將viewHolder初始化

    2024年02月11日
    瀏覽(18)
  • 元宇宙NFG系統,結合社交電商,是虛實結合交易的重要布局

    互聯網時代已經發(fā)展了近幾十年,它的市場已經越來越成熟,越來越多的行業(yè)被互聯網“改造”,互聯網企業(yè)甚至喊出了“不擁抱互聯網,就要被淘汰”的口號。在這樣的背景下,如何借助互聯網的東風,實現企業(yè)乃至產業(yè)鏈的轉型升級,是業(yè)界共同面臨的新課題、新挑戰(zhàn)、

    2023年04月09日
    瀏覽(25)
  • android pdf框架-3,基于recyclerview修改

    基于recyclerview的實現版本 解析使用的是pdifum.這個庫缺點是縮放功能不行.點擊鏈接功能沒有.只有渲染. GitHub - danjdt/android-pdfviewer: A Android PDF Viewer that render pdf using PdfRenderer and displays it in a RecyclerView. recyclerview的滑動并不是像ios那樣,有很好的慣性,針對此,從ebookdroid中拿了flinge

    2024年02月19日
    瀏覽(17)
  • MaterialSkin與系統Panel容器結合使用,實現自適應舒適布局

    MaterialSkin與系統Panel容器結合使用,實現自適應舒適布局

    你是否也有這樣的疑惑,當窗口拖拽之后,要如何才能填補右側和下方的空缺? 有些人直接固定窗體大小,不允許用戶拖拽。。。 也有萌新直接問我,MaterialSkin里面沒有什么辦法能讓拖拽的時候,控件也跟著動嗎? 其實這些功能在 系統的Panel 里面早就有了,只是大家不知道

    2024年02月06日
    瀏覽(19)
  • element-UI Pagination 分頁 布局,自定義布局

    element-UI Pagination 分頁 布局,自定義布局

    分頁左右布局,自定義布局 elm 分頁默認布局是 左對齊的 我們這節(jié)要實現的效果是這樣 (主要是拆分 分頁每個一項) 兩端對齊用的比較多 或者這樣 直接上代碼 主要通過 loyout 屬性 如果你想要圖2上的布局如上代碼 你想要左中右布局圖三效果 你需要用三個 el-pagination 只需要指

    2024年02月16日
    瀏覽(19)
  • Android的 AlertDialog自定義布局與常用布局用法(彈窗)

    Android的 AlertDialog自定義布局與常用布局用法(彈窗)

    1.直接上效果圖,看看是不是你們想要的效果圖 2.主活動MainActivity2的代碼如下

    2024年02月12日
    瀏覽(15)
  • Pycharm保存自定義布局

    Pycharm保存自定義布局

    在View-Tool Windows下可以啟用特定窗口,窗口標簽會出現在左邊(圖中紅框處),下邊或右邊,可以拖動擺放位置 在windows-layout下可以選擇保存布局 1.圖中第一個選項:選擇或創(chuàng)建布局 2.圖中第二個選項:恢復現有布局(將你現在看到的界面恢復為你選擇的布局) 3.圖中第三個

    2024年02月10日
    瀏覽(12)
  • 開源播放器GSYVideoPlayer的簡單介紹及播放rtsp流的優(yōu)化

    開源播放器GSYVideoPlayer的簡單介紹及播放rtsp流的優(yōu)化

    本文介紹,開源播放器GSYVideoPlayer的簡單介紹及播放rtsp流的優(yōu)化 github地址: https://github.com/CarGuo/GSYVideoPlayer 讓我們看看介紹: 視頻播放器(IJKplayer、ExoPlayer、MediaPlayer),HTTPS支持,支持彈幕,支持濾鏡、水印、gif截圖,片頭廣告、中間廣告,多個同時播放,支持基本的拖動

    2024年02月06日
    瀏覽(25)
  • G6繪制樹形圖(自定義節(jié)點、自定義邊、自定義布局)

    G6繪制樹形圖(自定義節(jié)點、自定義邊、自定義布局)

    在 registerNode 中定義所有的節(jié)點 為了使用內置的布局方式,選擇參數為 ‘tree-node’ 樹節(jié)點類型,數據格式可以存在children子節(jié)點,效果自動生成子樹 cfg 可以拿到數據,如cfg.id、cfg.name 使用 group.addShape(‘rect’, {}) 定義節(jié)點 rect 配置參數:https://antv-g6.gitee.io/zh/docs/api/shapeProp

    2024年02月05日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包