gihub地址:
https://github.com/Doikki/DKVideoPlayer
列表播放如图所示:
一、依赖
//添加RecyclerView的依赖包 implementation 'androidx.recyclerview:recyclerview:1.2.1' // 异步加载图片依赖 implementation 'com.squareup.picasso:picasso:2.5.2' // 上拉刷新、下来加载依赖 implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.3' // -- Android:视频播放器dkplayer //# 必选,内部默认使用系统mediaplayer进行解码 implementation 'com.github.dueeeke.dkplayer:dkplayer-java:3.2.6' //# 可选,包含StandardVideoController的实现 implementation 'com.github.dueeeke.dkplayer:dkplayer-ui:3.2.6' //# 可选,使用exoplayer进行解码 implementation 'com.github.dueeeke.dkplayer:player-exo:3.2.6' //# 可选,使用ijkplayer进行解码 implementation 'com.github.dueeeke.dkplayer:player-ijk:3.2.6' //# 可选,如需要缓存或者抖音预加载功能请引入此库 implementation 'com.github.dueeeke.dkplayer:videocache:3.2.6'
二、工具类
Tag.java
package com.chy.permission;public final class Tag { //列表播放 public static final String LIST = "list"; //无缝播放 public static final String SEAMLESS = "seamless"; //画中画 public static final String PIP = "pip";}
Utils.java
package com.chy.permission;import android.view.View;import android.view.ViewParent;import android.widget.FrameLayout;import com.dueeeke.videoplayer.player.VideoView;import com.dueeeke.videoplayer.player.VideoViewConfig;import com.dueeeke.videoplayer.player.VideoViewManager;import java.lang.reflect.Field;public final class Utils { private Utils() { } public static Object getCurrentPlayerFactory() { VideoViewConfig config = VideoViewManager.getConfig(); Object playerFactory = null; try { Field mPlayerFactoryField = config.getClass().getDeclaredField("mPlayerFactory"); mPlayerFactoryField.setAccessible(true); playerFactory = mPlayerFactoryField.get(config); } catch (Exception e) { e.printStackTrace(); } return playerFactory; } public static void removeViewFormParent(View v) { if (v == null) return; ViewParent parent = v.getParent(); if (parent instanceof FrameLayout) { ((FrameLayout) parent).removeView(v); } } public static String playState2str(int state) { String playStateString; switch (state) { default: case VideoView.STATE_IDLE: playStateString = "idle"; break; case VideoView.STATE_PREPARING: playStateString = "preparing"; break; case VideoView.STATE_PREPARED: playStateString = "prepared"; break; case VideoView.STATE_PLAYING: playStateString = "playing"; break; case VideoView.STATE_PAUSED: playStateString = "pause"; break; case VideoView.STATE_BUFFERING: playStateString = "buffering"; break; case VideoView.STATE_BUFFERED: playStateString = "buffered"; break; case VideoView.STATE_PLAYBACK_COMPLETED: playStateString = "playback completed"; break; case VideoView.STATE_ERROR: playStateString = "error"; break; } return String.format("playState: %s", playStateString); } public static String playerState2str(int state) { String playerStateString; switch (state) { default: case VideoView.PLAYER_NORMAL: playerStateString = "normal"; break; case VideoView.PLAYER_FULL_SCREEN: playerStateString = "full screen"; break; case VideoView.PLAYER_TINY_SCREEN: playerStateString = "tiny screen"; break; } return String.format("playerState: %s", playerStateString); }}
VideoAdapter.java
package com.chy.demoprj.adapter;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.TextView;import androidx.annotation.NonNull;import androidx.recyclerview.widget.RecyclerView;import com.chy.demoprj.R;import com.dueeeke.videocontroller.component.PrepareView;import com.squareup.picasso.Picasso;import java.util.HashMap;import java.util.List;public class VideoAdapter extends RecyclerView.Adapter { private Context context; private List> datas; private OnItemChildClickListener mOnItemChildClickListener;// 播放控件点击事件 public VideoAdapter(Context context,List> datas){ this.context = context; this.datas = datas; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item_video_layout,parent,false); ViewHolder viewHolder = new ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position) { ViewHolder vh = (ViewHolder) holder; HashMap entity = datas.get(position); // 设置播放占位图(不设置默认黑色) Picasso.with(context) .load(entity.get("thumbUrl")) .into(vh.mThumb); vh.mPosition = position; } @Override public int getItemCount() { return datas.size(); } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ public FrameLayout mPlayerContainer;// 容器 public PrepareView mPrepareView;// 播放视图 public ImageView mThumb;// 缩略图 public int mPosition;// 当前视图index public ViewHolder(@NonNull View itemView) { super(itemView); mPlayerContainer = itemView.findViewById(R.id.player_container); mPrepareView = itemView.findViewById(R.id.prepare_view); mThumb = mPrepareView.findViewById(R.id.thumb); if (mOnItemChildClickListener != null) { mPlayerContainer.setOnClickListener(this); } //通过tag将ViewHolder和itemView绑定 itemView.setTag(this); } @Override public void onClick(View v) { if (v.getId() == R.id.player_container){ if (mOnItemChildClickListener != null){ mOnItemChildClickListener.onItemChildClick(mPosition); } } } } public interface OnItemChildClickListener{ void onItemChildClick(int position); } public void setOnItemChildClickListener(OnItemChildClickListener onItemChildClickListener) { mOnItemChildClickListener = onItemChildClickListener; }}
三、布局
activity_main.xml
item_video_layout.xml
四、实现
package com.chy.demoprj;import androidx.annotation.NonNull;import androidx.appcompat.app.AppCompatActivity;import androidx.recyclerview.widget.LinearLayoutManager;import androidx.recyclerview.widget.RecyclerView;import android.Manifest;import android.content.pm.ActivityInfo;import android.os.Bundle;import android.view.View;import android.widget.FrameLayout;import android.widget.Toast;import com.chy.demoprj.adapter.VideoAdapter;import com.chy.permission.PermissionUtils;import com.chy.permission.Tag;import com.chy.permission.Utils;import com.dueeeke.videocontroller.StandardVideoController;import com.dueeeke.videocontroller.component.CompleteView;import com.dueeeke.videocontroller.component.ErrorView;import com.dueeeke.videocontroller.component.GestureView;import com.dueeeke.videocontroller.component.TitleView;import com.dueeeke.videocontroller.component.VodControlView;import com.dueeeke.videoplayer.player.VideoView;import com.dueeeke.videoplayer.player.VideoViewManager;import com.scwang.smartrefresh.layout.api.RefreshLayout;import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;import com.scwang.smartrefresh.layout.listener.OnRefreshListener;import java.util.ArrayList;import java.util.HashMap;import java.util.List;public class MainActivity extends AppCompatActivity { private static final int REQUEST_PERMISSION_CODE = 0;// 权限所用 // 动态申请权限 private String[] permissions = { Manifest.permission.INTERNET,// 网络权限 Manifest.permission.WRITE_EXTERNAL_STORAGE,// 写入数据权限 Manifest.permission.READ_EXTERNAL_STORAGE,// 读取数据权限 Manifest.permission.ACCESS_FINE_LOCATION,// 定位权限 Manifest.permission.ACCESS_COARSE_LOCATION // 获取基站的服务信号权限,以便获取位置信息 }; private RefreshLayout refreshLayout; private RecyclerView recyclerView;// 列表 private LinearLayoutManager layoutManager; private List> datas = new ArrayList<>(); // 视频播放-控件 protected VideoView mVideoView; protected StandardVideoController mController; protected ErrorView mErrorView; protected CompleteView mCompleteView; protected TitleView mTitleView; protected int mCurPos = -1; protected int mLastPos = mCurPos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getPermission(); initData(); initControls(); initVideoView(); } private void getPermission(){ boolean flage = PermissionUtils.hasPermissions(MainActivity.this,permissions); if (flage) { System.out.println("权限获取成功!"); } else { PermissionUtils.requestPermissions(MainActivity.this, REQUEST_PERMISSION_CODE, permissions); } } private void initData(){ HashMap hashMap = null; // 占位图 String thumbUrl = "https://p1-xg.byteimg.com/img/tos-cn-p-0000/527b08d0f31d4705a4d8f4a72120948c~tplv-crop-center:1041:582.jpg"; // 视频播放地址 String playerUrl = "https://vd4.bdstatic.com/mda-pdhb52ikamv3bdb7/sc/cae_h264/1681819063478400576/mda-pdhb52ikamv3bdb7.mp4"; // 循环添加数据 for (int i=0,len=8;i<=len;i++){ hashMap = new HashMap<>(); hashMap.put("thumbUrl",thumbUrl); hashMap.put("playerUrl",playerUrl); hashMap.put("vtitle","播放"+i); datas.add(hashMap); } } private void initControls(){ refreshLayout = findViewById(R.id.refreshLayout); // 下拉刷新事件 refreshLayout.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh(@NonNull RefreshLayout refreshLayout) { refreshLayout.finishRefresh(2000);// 传入false表示刷新失败 } }); // 上拉加载事件 refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() { @Override public void onLoadMore(@NonNull RefreshLayout refreshLayout) { refreshLayout.finishLoadMore(2000);// 传入false表示加载失败 runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplication(),"没有更多数据",Toast.LENGTH_SHORT).show(); } }); } }); recyclerView = findViewById(R.id.recyclerView); // 设置布局 layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); // 配置器 VideoAdapter videoAdapter = new VideoAdapter(this,datas); // 设置点击事件 videoAdapter.setOnItemChildClickListener(new VideoAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(int position) { startPlay(position); } }); recyclerView.setAdapter(videoAdapter); // RecyclerView监听事件 recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() { @Override public void onChildViewAttachedToWindow(@NonNull View view) { } @Override public void onChildViewDetachedFromWindow(@NonNull View view) { FrameLayout playerContainer = view.findViewById(R.id.player_container); View v = playerContainer.getChildAt(0); if (v != null && v == mVideoView && !mVideoView.isFullScreen()){ releaseVideoView(); } } }); } protected void initVideoView() { mVideoView = new VideoView(this); mVideoView.setOnStateChangeListener(new VideoView.SimpleOnStateChangeListener() { @Override public void onPlayStateChanged(int playState) { //监听VideoViewManager释放,重置状态 if (playState == com.dueeeke.videoplayer.player.VideoView.STATE_IDLE) { Utils.removeViewFormParent(mVideoView); mLastPos = mCurPos; mCurPos = -1; } } }); mController = new StandardVideoController(this); mErrorView = new ErrorView(this); mController.addControlComponent(mErrorView); mCompleteView = new CompleteView(this); mController.addControlComponent(mCompleteView); mTitleView = new TitleView(this); mController.addControlComponent(mTitleView); mController.addControlComponent(new VodControlView(this)); mController.addControlComponent(new GestureView(this)); mController.setEnableOrientation(true); mVideoView.setVideoController(mController); } @Override public void onPause() { super.onPause(); pause(); } protected void pause() { releaseVideoView(); } @Override public void onResume() { super.onResume(); resume(); } protected void resume() { if (mLastPos == -1) return; //恢复上次播放的位置 startPlay(mLastPos); } protected void startPlay(int position) { if (mCurPos == position) return; if (mCurPos != -1) { releaseVideoView(); } HashMap entity = datas.get(position); //边播边存// String proxyUrl = ProxyVideoCacheManager.getProxy(getActivity()).getProxyUrl(videoBean.getUrl());// mVideoView.setUrl(proxyUrl); String playurl = entity.get("playerUrl"); mVideoView.setUrl(playurl); mTitleView.setTitle(entity.get("vtitle")); View itemView = layoutManager.findViewByPosition(position); if (itemView == null) return; VideoAdapter.ViewHolder viewHolder = (VideoAdapter.ViewHolder) itemView.getTag(); //把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。 mController.addControlComponent(viewHolder.mPrepareView, true); Utils.removeViewFormParent(mVideoView); viewHolder.mPlayerContainer.addView(mVideoView, 0); //播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它 getVideoViewManager().add(mVideoView, Tag.LIST); mVideoView.start(); mCurPos = position; } private void releaseVideoView() { mVideoView.release(); if (mVideoView.isFullScreen()) { mVideoView.stopFullScreen(); } if (this.getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } mCurPos = -1; } protected VideoViewManager getVideoViewManager(){ return VideoViewManager.instance(); }}
来源地址:https://blog.csdn.net/qq_19688207/article/details/132186213