Android 视频播放器dkplayer

列表播放如图所示:

一、依赖

     //添加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;}/*** 将View从父控件中移除*/public static void removeViewFormParent(View v) {if (v == null) return;ViewParent parent = v.getParent();if (parent instanceof FrameLayout) {((FrameLayout) parent).removeView(v);}}/*** Returns a string containing player state debugging information.*/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);}/*** Returns a string containing player state debugging information.*/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<RecyclerView.ViewHolder> {private Context context;private List<HashMap<String,String>> datas;private OnItemChildClickListener mOnItemChildClickListener;// 播放控件点击事件/*** 构造函数* */public VideoAdapter(Context context,List<HashMap<String,String>> datas){this.context = context;this.datas = datas;}@NonNull@Overridepublic 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;}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position) {ViewHolder vh = (ViewHolder) holder;HashMap<String,String> entity = datas.get(position);// 设置播放占位图(不设置默认黑色)Picasso.with(context).load(entity.get("thumbUrl")).into(vh.mThumb);vh.mPosition = position;}@Overridepublic 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;// 当前视图indexpublic 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);}@Overridepublic 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

<?xml version="1.0" encoding="utf-8"?>
<com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/refreshLayout"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent"/></com.scwang.smartrefresh.layout.SmartRefreshLayout>

item_video_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><FrameLayoutandroid:id="@+id/player_container"android:layout_width="match_parent"android:layout_height="187dp"android:layout_marginTop="8dp"android:background="@android:color/black"app:layout_constraintDimensionRatio="16:9"app:layout_constraintTop_toTopOf="parent"><com.dueeeke.videocontroller.component.PrepareViewandroid:id="@+id/prepare_view"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout></LinearLayout>

四、实现

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<HashMap<String,String>> 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;@Overrideprotected 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<String,String> 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() {@Overridepublic void onRefresh(@NonNull RefreshLayout refreshLayout) {refreshLayout.finishRefresh(2000/*,false*/);// 传入false表示刷新失败}});// 上拉加载事件refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {@Overridepublic void onLoadMore(@NonNull RefreshLayout refreshLayout) {refreshLayout.finishLoadMore(2000/*,false*/);// 传入false表示加载失败runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic void onItemChildClick(int position) {/*** PrepareView被点击*/startPlay(position);}});recyclerView.setAdapter(videoAdapter);// RecyclerView监听事件recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {@Overridepublic void onChildViewAttachedToWindow(@NonNull View view) {}@Overridepublic 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() {@Overridepublic 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);}@Overridepublic void onPause() {super.onPause();pause();}/*** 由于onPause必须调用super。故增加此方法,* 子类将会重写此方法,改变onPause的逻辑*/protected void pause() {releaseVideoView();}@Overridepublic void onResume() {super.onResume();resume();}/*** 由于onResume必须调用super。故增加此方法,* 子类将会重写此方法,改变onResume的逻辑*/protected void resume() {if (mLastPos == -1)return;//恢复上次播放的位置startPlay(mLastPos);}/*** 开始播放** @param position 列表位置*/protected void startPlay(int position) {if (mCurPos == position) return;if (mCurPos != -1) {releaseVideoView();}HashMap<String,String> 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();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/29832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes工作原理

一、案例概述 传统部署时代&#xff1a; 早期是在物理服务器上运行应用程序。无法为物理服务器中的应用程序定义资源边界&#xff0c;这会导致资源分配出现问题。例如&#xff1a;如果在物理服务器上运行多个应用程序&#xff0c;则可能会出现一个应用程序占用大部分资源的情况…

ffplay简介

本文为相关课程的学习记录&#xff0c;相关分析均来源于课程的讲解&#xff0c;主要学习音视频相关的操作&#xff0c;对字幕的处理不做分析 ffplay播放器的意义 ffplay.c是FFmpeg源码⾃带的播放器&#xff0c;调⽤FFmpeg和SDL API实现⼀个⾮常有⽤的播放器。 ffplay实现了播…

Spring Data学习笔记Day01-SpringData入门

Spring Data基本介绍 目录 Spring Data Redis 官方API参考手册&#xff01;★ Spring Data的价值★ Spring Data及其子项目★ 强大的Spring Data★ Repository接口★ 具体Repository接口★ Spring Data JPA开发★ Spring Boot如何选择DataSource★ 数据源相关配置★ 配置第三方…

爬虫014_文件操作_打开关闭_读写_序列化_反序列化---python工作笔记033

报错,没有指定路径,没有指定路径无法创建文件 这样可以在当前目录下创建一个可写的文件 可以看到找到刚才生成的文件,看看内容

Android进阶之SeekBar动态显示进度

SeekBar 在开发中并不陌生,默认的SeekBar是不显示进度的,当然用吐司或者文案在旁边实时显示也是可以的,那能不能移动的时候才显示&#xff0c;默认不显示呢,当然网上花哨的三方工具类太多了&#xff0c;但是我只是单纯的想在SeekBar的基础上去添加一个可以跟随移动显示的气泡而…

虹科新闻 | 虹科与Power-MI正式建立合作伙伴关系

近日&#xff0c;虹科与Power-MI正式建立合作伙伴关系&#xff0c;双方就工业预测性维护领域进行深入的交流与合作&#xff0c;未来将共同致力于为亚洲市场提供完整的、更高质量的预测性维护解决方案&#xff0c;解决亚洲客户的工业自动化挑战。 虹科与Power-MI都表示十分期待…

MySQL的查询方法

单表查询 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 要求&#xff1a; 1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 3、求出所有职工的人数。 4、…

【MySql】RR有幻读问题吗?MVCC能否解决幻读?

文章目录 前言RR 隔离级别MVCCRR MVCC 有幻读问题吗&#xff1f;幻读问题演示如何彻底解决幻读&#xff1f;小结 前言 幻读是 MySQL 中一个非常普遍&#xff0c;且面试中经常被问到的问题&#xff0c;如果你还搞不懂什么是幻读&#xff1f;什么是 MVCC&#xff1f;以及 MySQL…

RabbitMQ 发布确认机制

发布确认模式是避免消息由生产者到RabbitMQ消息丢失的一种手段 发布确认模式 原理说明实现方式开启confirm&#xff08;确认&#xff09;模式阻塞确认异步确认 总结 原理说明 生产者通过调用channel.confirmSelect方法将信道设置为confirm模式&#xff0c;之后RabbitMQ会返回Co…

R语言APSIM模型高级应用及批量模拟

随着数字农业和智慧农业的发展&#xff0c;基于过程的农业生产系统模型在模拟作物对气候变化的响应与适应、农田管理优化、作物品种和株型筛选、农田固碳和温室气体排放等领域扮演着越来越重要的作用。APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生…

云服务器SVN仓库搭建(以阿里云为例)

远程连接阿里云服务器 安装svn(注意需要root权限使用命令sudo su) yum install subversion 安装成功后查看svn版本 svnserve --version 创建版本库的根目录 mkdir /var/svn 创建代码仓库 svnadmin create /var/svn/test 当前生成的目录结构 此处为svn的配置文件 创建用户名…

信创环境下 FTP如何进行国产化替代?

一、政策驱动&#xff0c;倡导自主、创新、协同 信创&#xff0c;即信息技术应用创新产业&#xff0c;其是数据安全、网络安全的基础&#xff0c;也是新基建的重要组成部分。为打破国外巨头的垄断&#xff0c;解决核心技术关键环节“卡脖子”问题&#xff0c;在核心芯片、基础…

C++项目:在线五子棋对战(网页版)

项目介绍 本项⽬主要实现⼀个⽹⻚版的五⼦棋对战游戏&#xff0c;其主要⽀持以下核⼼功能&#xff1a; • 用户管理:实现用户注册&#xff0c;用户登录、获取用户信息、用户天梯分数记录、用户比赛场次记录等。 • 匹配对战:实现两个玩家在网页端根据天梯分数匹配游戏对⼿&…

Java版企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis tbms

​ 功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查…

HarmonyOS应用开发者基础认证考试题库

此博文为HarmonyOS应用开发者基础认证考试的最后的大考&#xff0c;要求100分取得90分方可获取证书、现将考试的题库进行分享&#xff0c;希望能帮到大家。但是需要注意的是&#xff0c;题库会不定时的进行题目删减&#xff0c;但是大概的内容是不会进行改变的。真心希望这篇博…

第126天:内网安全-隧道技术SSHDNSICMPSMB上线通讯LinuxMac

知识点 #知识点&#xff1a; 1、入站规则不出网上线方案 2、出站规则不出网上线方案 3、隧道技术-SMB&ICMP&DNS&SSH 4、控制上线-Linux&Mac&IOS&Android-连接方向&#xff1a;正向&反向&#xff08;基础课程有讲过&#xff09; -内网穿透&#xf…

centos linux 安装RDMA Soft-RoCE|虚拟机安装Soft-RoCE

什么是Soft-RoCE softRoCE的目标是在所有支持以太网的设备上都可以部署RDMA传输&#xff0c;可以使不具备RoCE能力的硬件和支持RoCE的硬件间进行基于IB语义的交流。 大白话就是模拟RDMA的软件栈&#xff0c;使得在没有RDMA网卡的环境上&#xff0c;也可以运行基于RDMA写的传输…

leetcode870. 优势洗牌(java)

优势洗牌 leetcode870. 优势洗牌题目描述双指针 排序代码 滑动窗口 leetcode870. 优势洗牌 难度 - 中等 leetcode870. 优势洗牌 题目描述 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描…

博客网站添加复制转载提醒弹窗Html代码

网站如果是完全禁止右键&#xff08;复制、另存为等&#xff09;操作&#xff0c;对用户来说体验感会降低&#xff0c;但是又不希望自己的原创内容直接被copy&#xff0c;今天飞飞和你们分享几行复制转载提醒弹窗Html代码。 效果展示&#xff1a; 复制以下代码&#xff0c;将其…

matplotlib 笔记:基本用法

1 axis 1.0 对比原始图像 import numpy as np import matplotlib.pyplot as plt xrange(5) yrange(10,20,2) plt.plot(x,y) 1.1 plt.axis(equal) x轴和y轴单位长度相同 import numpy as np import matplotlib.pyplot as plt plt.axis(equal) xrange(5) yrange(10,20,2) pl…