前言
用户在下载剪辑完毕的视频后,系统将会指定专门的文件夹对其进行存放。当用户进入创作历史页面后,就可以读取下载目录下的所有剪辑作品,由用户进行选择播放。由于之前视频播放器 VideoView 已经完成,这里的重点是利用 RecyclerView 实现一个播放列表。
VideoRecyclerViewAdapter
在使用 RecyclerView 这个强大的滚动控件的时候,需要为它提供一个 Adapter 适配器,这个适配器继承自 RecyclerView.Adapter 这个类。
这个类里面有三个抽象方法需要我们去重写,分别是:
方法 | 作用 |
---|
onCreateViewHolder(ViewGroup parent, int viewType) | 用于加载 RecyclerView 子项的布局,然后返回一个 ViewHolder 对象,ViewHolder 是一个静态内部类,继承自 RecyclerView.ViewHolder 类 | onBindViewHolder(ViewHolder viewHolder, int i) | 为子项绑定数据 | getItemCount() | 用于获取 RecyclerView 一共有多少子项 |
这三个方法都是由安卓系统去调用的,其生命周期如下:
VideoHolder
创作历史页面的 RecyclerView 中的子 View 是 VideoView 对象,因此首先需要在 VideoRecyclerViewAdapter 创建内部类 VideoHolder 继承 ViewHolder
public class VideoHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public int mPosition;
public FrameLayout mPlayerContainer;
public TextView mTitle;
public ImageView mThumb;
public PrepareView mPrepareView;
VideoHolder(View itemView) {
super(itemView);
mPlayerContainer = itemView.findViewById(R.id.player_container);
mTitle = itemView.findViewById(R.id.tv_title);
mPrepareView = itemView.findViewById(R.id.prepare_view);
mThumb = mPrepareView.findViewById(R.id.thumb);
if (mOnItemChildClickListener != null) {
mPlayerContainer.setOnClickListener(this);
}
if (mOnItemClickListener != null) {
itemView.setOnClickListener(this);
}
itemView.setTag(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.player_container) {
if (mOnItemChildClickListener != null) {
mOnItemChildClickListener.onItemChildClick(mPosition);
}
} else {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(mPosition);
}
}
}
}
VideoRecyclerViewAdapter 实现需要重写的方法如下,为 RecyclerView 绑定了数据和布局:
public VideoRecyclerViewAdapter(List<VideoBean> videos) {
this.videos = videos;
}
@Override
@NonNull
public VideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video, parent, false);
return new VideoHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull VideoHolder holder, int position) {
VideoBean videoBean = videos.get(position);
Glide.with(holder.mThumb.getContext()).setDefaultRequestOptions(
new RequestOptions()
.frame(0)
.centerCrop()
)
.load(videoBean.getUrl())
.placeholder(android.R.color.darker_gray)
.into(holder.mThumb);
holder.mTitle.setText(videoBean.getTitle());
holder.mPosition = position;
}
HistoryFragment
创作历史页面也是继承 BaseFragment ,在进入页面后,首先需要获取读写内存权限,而后获取项目下载路径下所有视频的 url
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UrlList = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this.getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this.getContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this.getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
else{
getUrlList(AbsolutePath);
}
}
读写权限的 onRequestPermissionsResult
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PERMISSION_GRANTED) {
getUrlList(AbsolutePath);
}
else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this.getActivity(), permissions[i])){
AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
builder.setTitle("permission")
.setMessage("点击允许才可以使用我们的app哦")
.setPositiveButton("去允许", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
mDialog = builder.create();
mDialog.setCanceledOnTouchOutside(false);
mDialog.show();
}
else {
AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
builder.setTitle("permission")
.setMessage("点击允许才可以使用我们的app哦")
.setPositiveButton("去允许", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (alertDialog != null && alertDialog.isShowing()) {
alertDialog.dismiss();
}
ActivityCompat.requestPermissions(HistoryFragment.this.getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
});
alertDialog = builder.create();
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.show();
}
}
}
}
}
获取路径下的所有视频 url
public void getUrlList(String strPath) {
File dir = new File(strPath);
File[] files = dir.listFiles();
List<VideoBean> videoList = new ArrayList<>();
if (files != null){
for (int i = 0; i < files.length; i++) {
PageLog.dTag(TAG,files[i].getAbsolutePath());
UrlList.add(files[i].getAbsolutePath());
videoList.add(new VideoBean("预告片", files[i].getAbsolutePath()));
}
}
mVideos.addAll(videoList);
}
初始化页面,需要为视频播放器的页面设置监听以及Controller,并且为页面的RecycleView 绑定布局管理器与 VideoRecyclerViewAdapter
@Override
protected void initViews() {
initVideoView();
mRecyclerView = binding.review;
mLinearLayoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mAdapter = new VideoRecyclerViewAdapter(mVideos);
mAdapter.setOnItemChildClickListener(this);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.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(getActivity());
mVideoView.setOnStateChangeListener(new VideoView.SimpleOnStateChangeListener() {
@Override
public void onPlayStateChanged(int playState) {
if (playState == VideoView.STATE_IDLE) {
PlayerUtils.removeViewFormParent(mVideoView);
mLastPos = mCurPos;
mCurPos = -1;
}
}
});
mController = new StandardVideoController(getActivity());
mErrorView = new ErrorView(getActivity());
mController.addControlComponent(mErrorView);
mCompleteView = new CompleteView(getActivity());
mController.addControlComponent(mCompleteView);
mTitleView = new TitleView(getActivity());
mController.addControlComponent(mTitleView);
mController.addControlComponent(new VodControlView(getActivity()));
mController.addControlComponent(new GestureView(getActivity()));
mController.setEnableOrientation(true);
mVideoView.setVideoController(mController);
}
当点击播放页面的某个视频后,需要获取当前点击的播放器,找到绑定的VideoHolder,把列表中预置的PrepareView添加到控制器中,而后进行播放
protected void startPlay(int position) {
if (mCurPos == position) return;
if (mCurPos != -1) {
releaseVideoView();
}
VideoBean videoBean = mVideos.get(position);
mVideoView.setUrl(videoBean.getUrl());
mTitleView.setTitle(videoBean.getTitle());
View itemView = mLinearLayoutManager.findViewByPosition(position);
if (itemView == null) return;
VideoRecyclerViewAdapter.VideoHolder viewHolder = (VideoRecyclerViewAdapter.VideoHolder) itemView.getTag();
mController.addControlComponent(viewHolder.mPrepareView, true);
PlayerUtils.removeViewFormParent(mVideoView);
viewHolder.mPlayerContainer.addView(mVideoView, 0);
mVideoView.start();
mCurPos = position;
}
释放当前播放的视频资源
private void releaseVideoView() {
mVideoView.release();
if (mVideoView.isFullScreen()) {
mVideoView.stopFullScreen();
}
if(getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mCurPos = -1;
}
|