欢迎光临
我们一直在努力

ViewPager2高级实战:仿抖音上下滑动切换效果,完美实现竖屏视频流

想要实现抖音那样的丝滑上下滑动切换效果?本文手把手教你使用ViewPager2打造高性能竖屏视频流,涵盖播放器联动、手势控制、预加载优化等核心功能,性能媲美原生!


一、效果预览与核心特性

📱 最终效果

  • 垂直滑动切换视频页面
  • 滑动过程中自动播放/暂停
  • 滑动阻尼效果与速度控制
  • 视频预加载与缓存管理
  • 双击点赞、滑动交互等手势

🎯 技术架构

┌─────────────────────────────────────┐
│ UI Layer │
│ ┌─────────┐ ┌─────────┐ │
│ │VideoFrag│ │VideoFrag│ … │
│ └─────────┘ └─────────┘ │
└───────────────┬────────────────────┘
│ ViewPager2 (Vertical)
┌───────────────┴────────────────────┐
│ Logic Layer │
│ ┌────────────────────────────┐ │
│ │ VideoPlayerManager │ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ GestureDetector │ │
│ └────────────────────────────┘ │
└────────────────────────────────────┘


二、核心实现步骤

1. 垂直方向ViewPager2配置

class TiktokViewPager2 @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager2(context, attrs) {

init {


orientation = ORIENTATION_VERTICAL
overScrollMode = View.OVER_SCROLL_NEVER

// 优化滑动体验
(getChildAt(0) as? RecyclerView)?.apply {


overScrollMode = View.OVER_SCROLL_NEVER
layoutManager = LinearLayoutManager(context).apply {


orientation = RecyclerView.VERTICAL
}
}
}

// 自定义滑动阻尼
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {


val result = super.onInterceptTouchEvent(ev)
(getChildAt(0) as? RecyclerView)?.let {

recyclerView ->
when (ev.action) {


MotionEvent.ACTION_MOVE -> {


// 顶部下拉阻尼
if (!recyclerView.canScrollVertically(–1) && ev.y – startY > 0) {


return false
}
// 底部上拉阻尼
if (!recyclerView.canScrollVertically(1) && ev.y – startY < 0) {


return false
}
}
}
}
return result
}
}

2. 视频Fragment实现(ExoPlayer)

class VideoFragment : Fragment() {

private lateinit var binding: FragmentVideoBinding
private var player: ExoPlayer? = null
private var videoUrl: String? = null

// 播放状态
var isPlaying = false
private set

companion object {


private const val ARG_VIDEO_URL = \”video_url\”

fun newInstance(videoUrl: String): VideoFragment {


return VideoFragment().apply {


arguments = Bundle().apply {


putString(ARG_VIDEO_URL, videoUrl)
}
}
}
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {


binding = FragmentVideoBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {


super.onViewCreated(view, savedInstanceState)
videoUrl = arguments?.getString(ARG_VIDEO_URL)

initPlayer()
initGesture()
}

private fun initPlayer() {


player = ExoPlayer.Builder(requireContext()).build().apply {


setMediaItem(MediaItem.fromUri(videoUrl ?: return))
repeatMode = Player.REPEAT_MODE_ONE
addListener(playerListener)

binding.playerView.player = this
prepare()
}
}

private val playerListener = object : Player.Listener {


override fun onPlaybackStateChanged(playbackState: Int) {


when (playbackState) {


Player.STATE_READY -> {


binding.progressBar.visibility = View.GONE
if (isResumed && isVisible) {


startPlay()
}
}
Player.STATE_BUFFERING -> {


binding.progressBar.visibility = View.VISIBLE
}
}
}
}

fun startPlay() {


player?.playWhenReady = true
isPlaying = true
}

fun pausePlay() {


player?.playWhenReady = false
isPlaying = false
}

fun releasePlayer() {


player?.release()
player = null
}

override fun onResume() {


super.onResume()
if (isVisibleToUser) {


startPlay()
}
}

override fun onPause() {


super.onPause()
pausePlay()
}

override fun onDestroy() {


super.onDestroy()
releasePlayer()
}

// 手势控制
private fun initGesture() {


val gestureDetector = GestureDetector(requireContext(),
object : GestureDetector.SimpleOnGestureListener() {


override fun onDoubleTap(e: MotionEvent): Boolean {


// 双击点赞
showLikeAnimation(e.x, e.y)
return true
}

override fun onSingleTapConfirmed(e: MotionEvent): Boolean {


// 单击控制播放/暂停
if (isPlaying) pausePlay() else startPlay()
return true
}
})

binding.root.setOnTouchListener {

v, event ->
gestureDetector.onTouchEvent(event)
true
}
}

private fun showLikeAnimation(x: Float, y: Float) {


// 实现点赞动画
val likeView = ImageView(requireContext()).apply {


setImageResource(R.drawable.ic_like)
layoutParams = ViewGroup.LayoutParams(100, 100)
x = x – 50
y = y – 50
}

binding.root.addView(likeView)

likeView.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.alpha(0f)
.translationY(–100f)
.setDuration(800)
.withEndAction {

binding.root.removeView(likeView) }
.start()
}
}

3. 智能适配器(带预加载)

class VideoPagerAdapter(
fragment: Fragment,
private val videoList: List<VideoItem>
) : FragmentStateAdapter(fragment) {

// 预加载管理
private val preloadManager = VideoPreloadManager()

override fun getItemCount()

赞(0)
未经允许不得转载:171主机测评 » ViewPager2高级实战:仿抖音上下滑动切换效果,完美实现竖屏视频流
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址