Android 视频播放器dkplayer

gihub地址

https://github.com/Doikki/DKVideoPlayer

GitHub - Doikki/DKVideoPlayer: Android Video Player. 安卓视频播放器,封装MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音并实现预加载,列表播放,悬浮播放,广告播放,弹幕,视频水印,视频滤镜

列表播放如图所示:

一、依赖

     //添加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.rhkb.cn/news/87304.html

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

相关文章

adb用法,安卓的用户CA证书放到系统CA证书下

设备需root&#xff01;&#xff01;设备需root&#xff01;&#xff01;设备需root&#xff01;&#xff01; ​​​​​​​测试环境&#xff1a;redmi 5 plus、miui10 9.9.2dev&#xff08;安卓8.1&#xff09;、已root win下安装手机USB驱动&#xff08;过程略&#xff0c…

大数据扫盲(1): 数据仓库与ETL的关系及ETL工具推荐

在数字化时代&#xff0c;数据成为了企业决策的关键支持。然而&#xff0c;随着数据不断增长&#xff0c;有效地管理和利用这些数据变得至关重要。数据仓库和ETL工具作为数据管理和分析的核心&#xff0c;将帮助企业从庞杂的数据中提取有价值信息。 一、ETL是什么&#xff1f; …

redis学习笔记(九)

文章目录 python对redis基本操作&#xff08;1&#xff09;连接redis&#xff08;2&#xff09;数据类型操作 python对redis基本操作 &#xff08;1&#xff09;连接redis # 方式1 import redisr redis.Redis(host127.0.0.1, port6379) r.set(foo, Bar) print(r.get(foo))# …

VS中.cu文件属性中项目类型没有cuda

问题 VS中.cu文件属性中项目类型没有cuda 解决办法 右键项目“自定义” ![请添加图片描述](https://img-blog.csdnimg.cn/9717093332604b5982e67b15108c9ec8.png 再回到cu文件右键属性就会出现cuda选项了 请添加图片描述

(7)原神各属性角色的max与min

在对全部角色进行分析之后&#xff0c;还有必要对各属性角色的生命值/防御力/攻击力进行max与min显示&#xff1a; 话不多说&#xff0c;上货&#xff01; from pyecharts.charts import Radar from pyecharts import options as opts import pandas as pd from pyecharts.ch…

插入排序(Java实例代码)

目录 插入排序 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 InsertionSort.java 文件代码&#xff1a; 插入排序 一、概念及其介绍 插入排序(InsertionSort)&#xff0c;一般也被称为直接插入排序。 对于少量元素的排序&#xff0c;它是一个有效的算…

【LangChain】Memory

概要 大多数LLM应用都有对话界面。对话的一个重要组成部分是能够引用对话中先前介绍的信息。至少&#xff0c;对话系统应该能够直接访问过去消息的某些窗口。更复杂的系统需要有一个不断更新的世界模型&#xff0c;这使得它能够执行诸如维护有关实体及其关系的信息之类的事情。…

21、stm32使用LTDC驱动LCD

注&#xff1a;本文基于stm32使用FMC驱动SDRAM(IS42S32800G-6BLI)工程继续开发 本例使用安富莱的H743XIH板子驱动LTDC点亮7寸LCD 硬件接线&#xff1a;RGB888 一、cubemx配置 1、LTDC配置 注意此引脚应于上面的硬件接线图一致 2、配置DMA2D 3、背光引脚和触摸引脚 4、时钟…

【NLP】深入浅出全面回顾注意力机制

深入浅出全面回顾注意力机制 1. 注意力机制概述2. 举个例子&#xff1a;使用PyTorch带注意力机制的Encoder-Decoder模型3. Transformer架构回顾3.1 Transformer的顶层设计3.2 Encoder与Decoder的输入3.3 高并发长记忆的实现self-attention的矩阵计算形式多头注意力&#xff08;…

认识http的方法、Header、状态码以及简单实现一个http的业务逻辑

文章目录 http的方法http状态码http重定向http常见Header实现简单业务逻辑Protocol.hppUtil.hppServer.hppServer.cc 效果 http的方法 方法说明支持的HTTP版本GET获取资源1.0/1.1POST传输实体主体1.0/1.1PUT传输文件1.0/1.1HEAD获得报文首部1.0/1.1DELETE删除文件1.0/1.1OPTIO…

策略模式实战应用

场景 假设做了个卖课网站&#xff0c;会员等级分为月vip、年vip、终生vip&#xff0c;每个等级买课的优惠力度不一样&#xff0c;传统的写法肯定是一堆的 if-else&#xff0c;现在使用策略模式写出代码实现 代码实现 策略模式的核心思想就是对扩展开放&#xff0c;对修改关闭…

网络安全渗透测试之靶场训练

NWES: 7月26号武汉地震检测中心遭受境外具有政府背景的黑客组织和不法分子的网络攻击。 目前网络攻击主要来自以下几种方式: DDOS&#xff1a;分布式拒绝服务攻击。通过制造大量无用的请求向目标服务器发起访问&#xff0c;使其因短时间内无法处理大量请求而陷入瘫痪。主要针对…

实时通信应用的开发:Vue.js、Spring Boot 和 WebSocket 整合实践

目录 1. 什么是webSocket 2. webSocket可以用来做什么? 3. webSocket协议 4. 服务器端 5. 客户端 6. 测试通讯 1. 什么是webSocket WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务…

【LeetCode】870 . 优势洗牌

870 . 优势洗牌 方法&#xff1a;贪心 思路 这道题的思想类似于 “田忌赛马” &#xff0c;把 nums1 当成是田忌的马&#xff0c;nums2 当成是齐威王的马。 讨论田忌的下等马&#xff08;nums1 的最小值&#xff09;&#xff1a; 如果它能比过齐威王的下等马&#xff08;nums…

AD域机器KMS自动激活

1、打开AD域控&#xff0c;点击DNS管理 2、创建其它记录 3、选择服务位置 SRV 4、输入相关信息 服务&#xff1a;_VLMCS协议&#xff1a;_TCP权重&#xff1a;100端口号&#xff1a;1688KMS服务器地址&#xff1a;10.3.0.211 5、成功&#xff0c;这时域内主机重启后&#xff0…

第48节:cesium 面交集计算(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><vc-navigation

postgresql之内存池-GenerationContext

创建GenerationContext MemoryContext GenerationContextCreate(MemoryContext parent,const char *name,Size blockSize) {GenerationContext *set; ...set (GenerationContext *) malloc(MAXALIGN(sizeof(GenerationContext))); .../* Fill in GenerationContext-specific …

Angular安全专辑 —— CSP防止XSS攻击

什么是 CSP&#xff08;Content Security Policy&#xff09; CSP&#xff08;Content Security Policy&#xff09;是一种Web安全策略&#xff0c;用于减轻和防止跨站脚本攻击&#xff08;XSS&#xff09;等安全漏洞。它通过允许网站管理员定义哪些资源可以加载到网页中&#…

计算机网络(8) --- IP与IP协议

计算机网络&#xff08;7&#xff09; --- UDP协议和TCP协议_哈里沃克的博客-CSDN博客UDP协议和TCP协议https://blog.csdn.net/m0_63488627/article/details/132125374?spm1001.2014.3001.5501 目录 1.IP与IP协议 IP作用 协议​编辑 2.网段划分 DHCP划分 CIDR划分 特殊…

旷视科技AIoT软硬一体化走向深处,生态和大模型成为“两翼”?

齐奏AI交响曲的当下&#xff0c;赛道玩家各自精彩。其中&#xff0c;被称作AI四小龙的商汤科技、云从科技、依图科技、旷视科技已成长为业内标杆&#xff0c;并积极追赶新浪潮。无论是涌向二级市场还是布局最新风口大模型&#xff0c;AI四小龙谁都不甘其后。 以深耕AIoT软硬一…