Android开发视频预览效果

news2024/12/23 10:00:31
Android开发视频预览效果

视频播放不是一个简单的事情,得有暂停,继续播放等功能,屏幕的适配也是头疼的事情

一、思路:

引用的是腾讯播放器TXVodPlayer

二、效果图:

在这里插入图片描述图片不是很直观,也可以看下视频

Android开发教程实战案例源码分享-视频预览效果

三、关键代码:
public class ActiveVideoPreviewDialog extends AbsDialogFragment implements View.OnClickListener, ITXLivePlayListener {

    public static final String VIDEO_PATH = "videoPath";

    private TXCloudVideoView mTXCloudVideoView;
    private TXVodPlayer mPlayer;
    private String mVideoPath;
    private ObjectAnimator mPlayBtnAnimator;//暂停按钮的动画
    private View mPlayBtn;
    private boolean mPlayStarted;//播放是否开始了
    private boolean mPaused;//生命周期暂停
    private boolean mClickPaused;//点击暂停
//    private boolean mVideoFileFromRecord;
    private ActionListener mActionListener;

    @Override
    protected int getLayoutId() {
        return R.layout.dialog_active_video_preview;
    }

    @Override
    protected int getDialogStyle() {
        return R.style.dialog2;
    }

    @Override
    protected boolean canCancel() {
        return true;
    }

    @Override
    protected void setWindowAttributes(Window window) {
        WindowManager.LayoutParams params = window.getAttributes();
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;
        window.setAttributes(params);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        findViewById(R.id.btn_close).setOnClickListener(this);

        findViewById(R.id.video_container).setOnClickListener(this);
        mPlayBtn = findViewById(R.id.btn_play);
        //暂停按钮动画
        mPlayBtnAnimator = ObjectAnimator.ofPropertyValuesHolder(mPlayBtn,
                PropertyValuesHolder.ofFloat("scaleX", 4f, 0.8f, 1f),
                PropertyValuesHolder.ofFloat("scaleY", 4f, 0.8f, 1f),
                PropertyValuesHolder.ofFloat("alpha", 0f, 1f));
        mPlayBtnAnimator.setDuration(150);
        mPlayBtnAnimator.setInterpolator(new AccelerateInterpolator());

        mTXCloudVideoView = findViewById(R.id.video_view);
        mPlayer = new TXVodPlayer(mContext);
        mPlayer.setConfig(new TXVodPlayConfig());
        mPlayer.setPlayerView(mTXCloudVideoView);
        mPlayer.enableHardwareDecode(false);
        mPlayer.setRenderRotation(TXLiveConstants.RENDER_ROTATION_PORTRAIT);
        mPlayer.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);
        mPlayer.setPlayListener(this);
        Bundle bundle = getArguments();
        if (bundle != null) {
            mVideoPath = bundle.getString(VIDEO_PATH);
            if (!TextUtils.isEmpty(mVideoPath)) {
//                mVideoFileFromRecord = mVideoPath.contains(CommonAppConfig.VIDEO_PATH_RECORD);
                int result = mPlayer.startPlay(mVideoPath);
                if (result == 0) {
                    mPlayStarted = true;
                }
            }
        }
    }


    @Override
    public void onPlayEvent(int e, Bundle bundle) {
        switch (e) {
            case TXLiveConstants.PLAY_EVT_PLAY_END://播放结束
                onReplay();
                break;
            case TXLiveConstants.PLAY_EVT_CHANGE_RESOLUTION:
                //onVideoSizeChanged(bundle.getInt("EVT_PARAM1", 0), bundle.getInt("EVT_PARAM2", 0));
                onVideoSizeChanged();
                break;
        }
    }


    @Override
    public void onNetStatus(Bundle bundle) {

    }

    /**
     * 获取到视频宽高回调
     */
    public void onVideoSizeChanged() {
        double videoWidth = 0;
        double videoHeight = 0;
        MediaMetadataRetriever mmr = null;
        try {
            mmr = new MediaMetadataRetriever();
            mmr.setDataSource(mVideoPath);
            String width = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);//宽
            String height = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);//高
            String rotation = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
            if ("0".equals(rotation)) {
                if (!TextUtils.isEmpty(width)) {
                    videoWidth = Double.parseDouble(width);
                }
                if (!TextUtils.isEmpty(height)) {
                    videoHeight = Double.parseDouble(height);
                }
            } else {
                if (!TextUtils.isEmpty(height)) {
                    videoWidth = Double.parseDouble(height);
                }
                if (!TextUtils.isEmpty(width)) {
                    videoHeight = Double.parseDouble(width);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mmr != null) {
                mmr.release();
            }
        }
        if (mTXCloudVideoView != null && videoWidth > 0 && videoHeight > 0) {
            FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mTXCloudVideoView.getLayoutParams();
            if (videoWidth / videoHeight > 0.5625f) {//横屏 9:16=0.5625
                params.height = (int) (mTXCloudVideoView.getWidth() / videoWidth * videoHeight);
                params.gravity = Gravity.CENTER;
                mTXCloudVideoView.requestLayout();
            }
        }
    }

    /**
     * 获取到视频宽高回调
     */
    public void onVideoSizeChanged(float videoWidth, float videoHeight) {
        if (mTXCloudVideoView != null && videoWidth > 0 && videoHeight > 0) {
            FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mTXCloudVideoView.getLayoutParams();
            if (videoWidth / videoHeight > 0.5625f) {//横屏 9:16=0.5625
                params.height = (int) (mTXCloudVideoView.getWidth() / videoWidth * videoHeight);
                params.gravity = Gravity.CENTER;
                mTXCloudVideoView.requestLayout();
            }
        }
    }

    /**
     * 显示开始播放按钮
     */
    private void showPlayBtn() {
        if (mPlayBtn != null && mPlayBtn.getVisibility() != View.VISIBLE) {
            mPlayBtn.setVisibility(View.VISIBLE);
        }
    }

    /**
     * 隐藏开始播放按钮
     */
    private void hidePlayBtn() {
        if (mPlayBtn != null && mPlayBtn.getVisibility() == View.VISIBLE) {
            mPlayBtn.setVisibility(View.INVISIBLE);
        }
    }


    /**
     * 点击切换播放和暂停
     */
    private void clickTogglePlay() {
        if (!mPlayStarted) {
            return;
        }
        if (mPlayer != null) {
            if (mClickPaused) {
                mPlayer.resume();
            } else {
                mPlayer.pause();
            }
        }
        mClickPaused = !mClickPaused;
        if (mClickPaused) {
            showPlayBtn();
            if (mPlayBtnAnimator != null) {
                mPlayBtnAnimator.start();
            }
        } else {
            hidePlayBtn();
        }
    }


    @Override
    public void onPause() {
        super.onPause();
        mPaused = true;
        if (!mClickPaused && mPlayer != null) {
            mPlayer.pause();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mPaused) {
            if (!mClickPaused && mPlayer != null) {
                mPlayer.resume();
            }
        }
        mPaused = false;
    }


    /**
     * 循环播放
     */
    private void onReplay() {
        if (mPlayStarted && mPlayer != null) {
            mPlayer.seek(0);
            mPlayer.resume();
        }
    }


    @Override
    public void onClick(View v) {
        int i = v.getId();
        if (i == R.id.btn_close) {
            dismiss();
        } else if (i == R.id.video_container) {
            clickTogglePlay();
        }
    }

    @Override
    public void onDestroy() {
        if (mPlayer != null) {
            mPlayer.stopPlay(false);
            mPlayer.setPlayListener(null);
        }
        mContext = null;
        mPlayer = null;
        mActionListener = null;
        super.onDestroy();
    }


    public void setActionListener(ActionListener actionListener) {
        mActionListener = actionListener;
    }

    public interface ActionListener {
        void onDeleteClick();
    }

}
四、项目demo源码结构图:

在这里插入图片描述有问题或者需要完整源码的私信我

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

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

相关文章

【unity进阶知识12】从零手搓unity存档存储数据持久化系统,实现对存档的创建,获取,保存,加载,删除,缓存,加密,支持多存档

文章目录 前言一、Unity对Json数据的操作方法一、JsonUtility方法二、Newtonsoft 二、持久化的数据路径三、数据加密/解密加密方法解密方法 四、条件编译指令限制仅在编辑器模式下进行加密/解密四、数据持久化管理器1、存档工具类2、一个存档数据3、存档系统数据类4、数据存档存…

【STM32单片机_(HAL库)】4-5-2【定时器TIM】【感应开关盖垃圾桶项目】HC-SR04超声波模块实验

1.硬件 STM32单片机最小系统HC-SR04超声波模块 2.软件 hcsr04驱动文件添加main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "uart1.h" #include "hcsr04.h"int main(void) {HAL_Init(); …

OceanBase管理着工具-oat安装

https://www.oceanbase.com/softwarecenter-enterprise https://www.oceanbase.com/docs/enterprise-oat-doc-cn-1000000000762607 (base) [rootlnpg soft]# pwd /db/ob/soft (base) [rootlnpg soft]# ll 总用量 4274536 -rw-r--r-- 1 root root 1730447360 6月 26 14:06 oa…

『网络游戏』客户端发送消息到服务器【17】

将上一章服务器的协议PEProtocol的.dll文件重新生成导入unity客户端中 命名为Net 点击生成 另一种导入.dll文件方式 在客户端粘贴即可 此时Net文件夹的.dll文件就导入进来了 创建脚本:NetSvc.cs 编写脚本:NetSvc.cs 修改脚本:GameRoot.cs 在…

Spring源码:SpringBoot启动流程分析

目录 一、演示代码二、功能介绍三、代码分析1、从主启动类中调用run()方法出发2、看一下SpringApplication的构造方法在干什么?3、看下run()方法的主要流程代码4、run():启动计时器计算springboot启动时间5、run():创建DefaultBootstrapConte…

基于单片机的山林远程环境监测仪设计

本设计基于单片机的智能化的远程山林环境检测仪,该检测仪由硬件系统和软件系统构成。电源管理模块给整个硬件系统提供工作所需电源,系统可完成山林环境有关的温度、湿度、火焰和海拔高度的采集,并且可通过与按键设置阈值作对比判断危险情况&a…

C++ | Leetcode C++题解之第470题用Rand7()实现Rand10()

题目&#xff1a; 题解&#xff1a; class Solution { public:int rand10() {int a, b, idx;while (true) {a rand7();b rand7();idx b (a - 1) * 7;if (idx < 40) {return 1 (idx - 1) % 10;}a idx - 40;b rand7();// get uniform dist from 1 - 63idx b (a - 1)…

Halcon OCR字符识别

create_text_model_reader创建一个文本模型描述要用于分割的文本find_text. 的parameter value文本分段方法的值为自动和手动. 通常&#xff0c;参数模式应设置为自动因为这种模式更稳定&#xff0c;需要更少 配置工作量。请注意&#xff0c;在这种情况下&#xff0c;还有一个…

Redis Windows最新安装教程(2024.10.10)

文章目录 redis介绍下载地址 安装流程基础操作测试Redis常用的服务指令 redis介绍 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、基于内存的数据结构存储系统&#xff0c;常用作数据库、缓存和消息中间件。Redis具有快速、灵活、可扩展和高可用性等特…

2024最新会声会影序列号及会声会影2023怎样添加画中画滤镜

深入简单直观的视频编辑&#xff01;使用 Corel VideoStudio会声会影2023&#xff0c;将您最美好的时刻和生活体验变成令人惊叹的电影&#xff0c;这是一款有趣且直观的视频编辑器&#xff0c;包含高级工具和高级效果会声会影2024免费下载。从自定义标题和过渡&#xff0c;到 M…

微服务(二)

目录 一、服务注册和发现 1、注册中心原理 2、nacos注册中心 3、服务注册 3.1 添加依赖 3.2 配置nacos 3.3 服务的发现 二、openfeign 1、快速入门 1.1 引入依赖 1.2 启用openfeign 1.3 编写openfeign客户端 2、连接池 2.1 引入依赖 2.2 开启连接池 3、 最佳实…

用godot4.3 C#开发一个APP登陆界面

一、下载godot godot 是一个游戏开发平台&#xff0c;可以用gd语言&#xff0c;这一个类似python,python程序员转gd很容易&#xff0c;也可以用C#开发。 https://godotengine.org/ 进行下载&#xff0c;下载 下载,net版本。当然mac也可以下载&#xff0c;linux上也有可以&#…

[含文档+PPT+源码等]精品基于Python实现的Django高校职业通的设计与实现

基于Python实现的Django高校招聘系统的设计与实现背景&#xff0c;主要源于以下几个方面的需求和趋势&#xff1a; 1. 就业压力的增加 随着高校毕业生数量的不断增加&#xff0c;就业压力日益加大。传统的招聘方式&#xff0c;如招聘会、报纸广告等&#xff0c;由于其信息传播…

C语言 | Leetcode C语言题解之第470题用Rand7()实现Rand10()

题目&#xff1a; 题解&#xff1a; // The rand7() API is already defined for you. // int rand7(); // return a random integer in the range 1 to 7int rand10() {while(true) {int index (rand7() - 1) * 7 rand7(); if(index < 40) return index % 10 1; } }

ThingsBoard规则链:Copy Keys节点详解

引言 复制键节点简介 用法 含义 应用场景 实际项目运用示例 智能农业监控系统 城市交通管理系统 结论 引言 ThingsBoard是一个功能丰富的物联网平台&#xff0c;它支持设备管理、数据收集与处理以及实时监控。其强大的规则引擎允许用户通过创建复杂的业务逻辑来自动处理…

探索高效的 PDF 拆分工具及其独特功能

当一份大型的PDF文档包含了多个不同主题或章节的内容时&#xff0c;将其拆分成独立的部分可以更方便我们的阅读、编辑和管理。接下来&#xff0c;让我们一起走进PDF拆分工具的世界&#xff0c;了解它们的功能和价值。 1.福昕PDF编辑器 链接一下>>https://editor.foxits…

6-基于TMS320C6678、FPGA XC5VSX95T的6U CPCI 8路光纤信号处理卡

1、板卡概述   本板卡由我公司自主研发&#xff0c;基于CPCI架构&#xff0c;符合CPCI2.0标准&#xff0c;采用两片TI DSP TMS320C6678芯片和Xilinx公司V5系列FPGA XC5VSX95T-1FF1136芯片。包含PCI接口、GMII的以太网接口、Nor Flash接口、8路SFP光 纤&#xff0c;4路RS232。…

Reqable小黄鸟抓包

搜索Reqable到官网进行下载 Windows端配置证书 可以直接自动安装&#xff0c;或者跟着内置的教程手动安装 下载安装安卓端并配置证书 配置证书有内置的教程跟着操作即可 手机协同配置 点击这个位置然后手机扫码就可以抓包了

网络安全知识|网安问答题|OSPF报文协议|抓包工具|路由器环路|序列化与反序列化|磁盘利用率|网络攻防

网络安全知识|网安问答题|OSPF报文协议|抓包工具|路由器环路|序列化与反序列化|磁盘利用率|网络攻防 作为网络工程师&#xff0c;怎么处理防火墙没网的问题&#xff1f; 检查防火墙规则&#xff1a;确保防火墙规则没有错误地阻止了网络访问。需要检查防火墙设置&#xff0c;特…

​ceph掉电后无法启动osd,pgs unknown

处理办法&#xff1a; 只有1个osd,单副本&#xff0c;掉电损坏osd&#xff0c;只能考虑重建pg&#xff0c;丢失部分数据了。生产环境务必考虑2&#xff0c;3副本设计。避免掉电故障风险。 掉电后osdmap丢失无法启动osd的解决方案 - 武汉-磨渣 - 博客园 https://zhuanlan.zhih…