• android RecyclerView列表自动播放实现


    最近有个需求,类似于皮皮虾那种列表上有播放器,滑动停止时自动播放可视区域的第一项;

    查了一些资料,看了一些大神的博客,受益匪浅,这里也做一个记录,方便以后搬砖;

    要实现这个功能,主要需要四部分工作:

    1、监听列表的滚动事件,知道什么时候开始滚动和停止滚动;

    2、找出可视区域的item,再找出第一项中的播放器;

    3、监听移除屏幕的item,及时回收播放器资源;

    3、找一个适合的播放器,有些播放器就是不得劲。

    直接上代码,

    MainActivity.java

    1. package com.androidlmy.jzplayer;
    2. import androidx.appcompat.app.AppCompatActivity;
    3. import androidx.recyclerview.widget.LinearLayoutManager;
    4. import androidx.recyclerview.widget.RecyclerView;
    5. import android.content.Context;
    6. import android.graphics.Rect;
    7. import android.os.Bundle;
    8. import android.view.KeyEvent;
    9. import android.view.View;
    10. import cn.jzvd.Jzvd;
    11. import static cn.jzvd.Jzvd.STATE_PAUSE;
    12. import static cn.jzvd.Jzvd.STATE_PLAYING;
    13. public class MainActivity extends AppCompatActivity {
    14. private RecyclerView recyclerView;
    15. private PlayerAdapter adapter;
    16. @Override
    17. protected void onCreate(Bundle savedInstanceState) {
    18. super.onCreate(savedInstanceState);
    19. setContentView(R.layout.activity_main);
    20. recyclerView = findViewById(R.id.recycler_view);
    21. adapter = new PlayerAdapter(this);
    22. recyclerView.setLayoutManager(new LinearLayoutManager(this));
    23. recyclerView.setAdapter(adapter);
    24. recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
    25. @Override
    26. public void onChildViewAttachedToWindow(View view) {
    27. // item依附到recyclerview中时
    28. }
    29. @Override
    30. public void onChildViewDetachedFromWindow(View view) {
    31. // item从recyclerview中分离时
    32. Jzvd jzvd = view.findViewById(R.id.videoplayer);
    33. if (jzvd != null && Jzvd.CURRENT_JZVD != null &&
    34. jzvd.jzDataSource.containsTheUrl(Jzvd.CURRENT_JZVD.jzDataSource.getCurrentUrl())) {
    35. if (Jzvd.CURRENT_JZVD != null && Jzvd.CURRENT_JZVD.screen != Jzvd.SCREEN_FULLSCREEN) {
    36. // 释放播放器资源
    37. Jzvd.releaseAllVideos();
    38. }
    39. }
    40. }
    41. });
    42. recyclerView.addOnScrollListener(new AutoPlayScrollListener(this));
    43. }
    44. /**
    45. * 监听recycleView滑动状态,
    46. * 自动播放可见区域内的第一个视频
    47. */
    48. private static class AutoPlayScrollListener extends RecyclerView.OnScrollListener {
    49. private int firstVisibleItem = 0;
    50. private int lastVisibleItem = 0;
    51. private int visibleCount = 0;
    52. private Context context;
    53. public AutoPlayScrollListener(Context context) {
    54. this.context = context;
    55. }
    56. /**
    57. * 被处理的视频状态标签
    58. */
    59. private enum VideoTagEnum {
    60. /**
    61. * 自动播放视频
    62. */
    63. TAG_AUTO_PLAY_VIDEO,
    64. /**
    65. * 暂停视频
    66. */
    67. TAG_PAUSE_VIDEO
    68. }
    69. @Override
    70. public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    71. super.onScrollStateChanged(recyclerView, newState);
    72. switch (newState) {
    73. case RecyclerView.SCROLL_STATE_IDLE://停止滚动
    74. autoPlayVideo(recyclerView, VideoTagEnum.TAG_AUTO_PLAY_VIDEO);
    75. case RecyclerView.SCROLL_STATE_DRAGGING://开始滚动
    76. case RecyclerView.SCROLL_STATE_SETTLING://滑行中
    77. default:
    78. // autoPlayVideo(recyclerView, VideoTagEnum.TAG_PAUSE_VIDEO);//滑动时暂停视频 需要可以加上
    79. break;
    80. }
    81. }
    82. @Override
    83. public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    84. super.onScrolled(recyclerView, dx, dy);
    85. RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    86. if (layoutManager instanceof LinearLayoutManager) {
    87. LinearLayoutManager linearManager = (LinearLayoutManager) layoutManager;
    88. firstVisibleItem = linearManager.findFirstVisibleItemPosition();
    89. lastVisibleItem = linearManager.findLastVisibleItemPosition();
    90. visibleCount = lastVisibleItem - firstVisibleItem;
    91. }
    92. }
    93. /**
    94. * 循环遍历可见区域的播放器
    95. * 然后通过 getLocalVisibleRect(rect)方法计算出哪个播放器完全显示出来
    96. * @param recyclerView
    97. * @param handleVideoTag 视频需要进行状态
    98. */
    99. private void autoPlayVideo(RecyclerView recyclerView, VideoTagEnum handleVideoTag) {
    100. for (int i = 0; i < visibleCount; i++) {
    101. if (recyclerView != null && recyclerView.getChildAt(i) != null && recyclerView.getChildAt(i).findViewById(R.id.videoplayer) != null) {
    102. MyJzvdStd homeGSYVideoPlayer = (MyJzvdStd) recyclerView.getChildAt(i).findViewById(R.id.videoplayer);
    103. Rect rect = new Rect();
    104. homeGSYVideoPlayer.getLocalVisibleRect(rect);
    105. int videoheight = homeGSYVideoPlayer.getHeight();
    106. if (rect.top == 0 && rect.bottom == videoheight) {
    107. handleVideo(handleVideoTag, homeGSYVideoPlayer);
    108. // 跳出循环,只处理可见区域内的第一个播放器
    109. break;
    110. }
    111. }
    112. }
    113. }
    114. /**
    115. * 视频状态处理
    116. *
    117. * @param handleVideoTag 视频需要进行状态
    118. * @param homeGSYVideoPlayer JZVideoPlayer播放器
    119. */
    120. private void handleVideo(VideoTagEnum handleVideoTag, MyJzvdStd homeGSYVideoPlayer) {
    121. switch (handleVideoTag) {
    122. case TAG_AUTO_PLAY_VIDEO:
    123. if ((homeGSYVideoPlayer.state != STATE_PLAYING)) {
    124. // 进行播放
    125. homeGSYVideoPlayer.startVideo();
    126. }
    127. break;
    128. case TAG_PAUSE_VIDEO:
    129. if ((homeGSYVideoPlayer.state != STATE_PAUSE)) {
    130. // 模拟点击,暂停视频
    131. homeGSYVideoPlayer.startButton.performClick();
    132. }
    133. break;
    134. default:
    135. break;
    136. }
    137. }
    138. }
    139. /**
    140. * 拦截返回键 返回不退出程序
    141. */
    142. @Override
    143. public boolean onKeyDown(int keyCode, KeyEvent event) {
    144. if (Jzvd.backPress()) {
    145. return true;
    146. } else {
    147. if (keyCode == KeyEvent.KEYCODE_BACK) {
    148. moveTaskToBack(true);
    149. return true;
    150. }
    151. }
    152. return super.onKeyDown(keyCode, event);
    153. }
    154. }

    PlayerAdapter.java

    1. package com.androidlmy.jzplayer;
    2. import android.annotation.SuppressLint;
    3. import android.content.Context;
    4. import android.view.LayoutInflater;
    5. import android.view.View;
    6. import android.view.ViewGroup;
    7. import android.widget.ImageView;
    8. import androidx.recyclerview.widget.RecyclerView;
    9. import com.bumptech.glide.Glide;
    10. import cn.jzvd.JzvdStd;
    11. public class PlayerAdapter extends RecyclerView.Adapter {
    12. private Context context;
    13. public PlayerAdapter(Context context) {
    14. this.context = context;
    15. }
    16. @Override
    17. public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    18. ViewHolder holder = new ViewHolder(LayoutInflater.from(
    19. context).inflate(R.layout.item_recyclerview, parent,
    20. false));
    21. return holder;
    22. }
    23. @SuppressLint("LongLogTag")
    24. @Override
    25. public void onBindViewHolder(ViewHolder holder, int position) {
    26. // String url = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";
    27. String url = "https://pointshow.oss-cn-hangzhou.aliyuncs.com/McTk51586843620689.mp4";
    28. holder.videoplayer.setUp(url, "", JzvdStd.SCREEN_NORMAL);
    29. holder.videoplayer.setVideoImageDisplayType(1);
    30. holder.videoplayer.thumbImageView.setScaleType(ImageView.ScaleType.FIT_XY);
    31. // holder.videoplayer.startVideo();//自动播放 在recyclerview有bug
    32. Glide.with(holder.videoplayer.getContext()).load("http://jzvd-pic.nathen.cn/jzvd-pic/00b026e7-b830-4994-bc87-38f4033806a6.jpg").
    33. into(holder.videoplayer.thumbImageView);
    34. }
    35. @Override
    36. public int getItemCount() {
    37. return 10;
    38. }
    39. class ViewHolder extends RecyclerView.ViewHolder {
    40. private MyJzvdStd videoplayer;
    41. public ViewHolder(View itemView) {
    42. super(itemView);
    43. videoplayer = itemView.findViewById(R.id.videoplayer);
    44. }
    45. }
    46. }

    activity_main.xml

    1. "1.0" encoding="utf-8"?>
    2. "http://schemas.android.com/apk/res/android"
    3. xmlns:app="http://schemas.android.com/apk/res-auto"
    4. xmlns:tools="http://schemas.android.com/tools"
    5. android:layout_width="match_parent"
    6. android:layout_height="match_parent"
    7. tools:context=".MainActivity">
    8. android:id="@+id/recycler_view"
    9. android:layout_width="match_parent"
    10. android:layout_height="match_parent" />

    item_recyclerview.xml

    1. "1.0" encoding="utf-8"?>
    2. "http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="300dp">
    5. android:id="@+id/videoplayer"
    6. android:layout_width="match_parent"
    7. android:layout_height="match_parent" />

    build.gradle 中加上播放器和glide的依赖

    1. implementation 'cn.jzvd:jiaozivideoplayer:7.0.5'
    2. implementation 'com.github.bumptech.glide:glide:4.8.0'
    3. annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

    资源下载链接

    https://download.csdn.net/download/msn465780/87131990

    好了,又可以愉快玩耍了。

  • 相关阅读:
    Jmeter性能测试指南
    【完美世界】天仙书院偷食也就算了,竟然还偷院长的孙女,美滋滋
    Django5+React18前后端分离开发实战04 开发我们的后端服务
    zabbix配置95计费
    Java中的网络编程------基于Socket的TCP编程和基于UDP的网络编程,netstat指令
    数据挖掘案列分析---LightGBM实战贷款违约预测
    【云原生&微服务十】SpringCloud之OpenFeign实现服务间请求头数据传递(OpenFeign拦截器RequestInterceptor的使用)
    厉害了!有了它,发顶会顶刊拿赛事大奖轻松多了!
    Backbone 网络-ResNet v2 详解
    低代码开发平台的功能有哪些?低代码“功能清单”一览
  • 原文地址:https://blog.csdn.net/msn465780/article/details/128004957