自定义View实战《弹幕》

news2024/9/27 23:33:55

自定义View实战《弹幕》

  • 前言
  • 一、步骤讲解
  • 二、缓存优化
  • 三、弹幕框架DanmakuFlameMaster
    • 介绍DanmakuFlameMaster
    • DanmakuFlameMaster的使用
  • 四、初步实现的完整代码
  • 总结

前言

前面已经学习了自定义的View《渐变色的文字》是继承View的。现在我们在继承ViewGroup来实现我们的《弹幕》View。
本文分为三部分

1、步骤讲解和分步实战
2、缓存优化
3、三方弹幕框架DanmakuFlameMaster
4、第一步初步实现的完整代码

继承View和ViewGroup实现的View有什么明显的区别吗?
一句话打通你任督二脉:
自定义View主要实现的是onMeasure和onDraw
自定义ViewGroup主要实现的是onMeasure和onLayout

好的现在让我们来实现我们的《弹幕》吧。

一、步骤讲解

实现弹幕听起来复杂其实很简单,不信的话往下看。
步骤:
1、初始化弹幕子View

  • 可以自定义弹幕子View(我这里就先用TextView代替了)
  • 添加方法可以是单个和多个
fun addBarrage(data: BarrageItem){ //BarrageItem自定义的View的数据实体
    //1、初始化弹幕子View
    val childView : TextView = TextView(context).apply {
    text = data.text
    textSize = data.textSize
}

fun addBarrageList(dataList : MutableList<BarrageItem>){
    this.barrageList = dataList
    dataList.forEach {
        addBarrage(it)
    }
}

2、测量子View

  • 测量的那肯定就是onMeasure了,前面《View的绘制流程》中说过子onMeasure的两个参数widthMeasureSpec和heightMeasureSpec是父View给他的测量信息。
  • 这里我弹幕是整个View大小的,XML中用了match_parent,所以就不用写他的onMeasue方法了
//2、测量弹幕子View,也就是measure呗
childView.measure(measuredWidth, measuredHeight)

3、向ViewGroup中添加子View

//3、添加弹幕子View
addView(childView)

4、测量完事了,也添加完了,那就该摆放位置了

  • 摆放位置不就是onLayout吗。
//4、设置弹幕子View的布局,也就是layout呗
//这里我想让他从右面到左面移动,上下的位置是随机的
val left = measuredWidth
val top = nextInt(1, measuredHeight) //kotlin的Random.nextInt函数
childView.layout(left, top, left + childView.measuredWidth, top + childView.measuredHeight)

5、开启弹幕滚动的动画

  • 那从左到右的动画那岂不是很简单吗
  • 可以使用TranslateAnimation相对简单
  • 但是后面大家需要扩展一下效果还是用ValueAnimator比较好(透明度、旋转角度、缩放比例)
//5、开启弹幕子View动画
val anim : ValueAnimator = ValueAnimator.ofFloat(1.0f).apply {
    duration = 7000
    interpolator = LinearInterpolator() //线性的插值器
    addUpdateListener {
        val value = it.animatedFraction
        val left = (measuredWidth - value *(measuredWidth + childView.measuredWidth)).toInt()
        //通过改变布局来实现弹幕的滚动
        childView.layout(left, top, left + childView.measuredWidth, top + childView.measuredHeight)
    }

    addListener(onEnd = {
        removeView(childView)
        barrageList.remove(data)
    })
}

anim.start()

完事初版的弹幕就完成了。
完整代码我放在最后吧还是,在中间的话太乱了。

二、缓存优化

实现其实大家一看就都懂了,那么稍微进阶一点那肯定是要优化一下。
那么最明显的优化那肯定是View的绘制消耗的资源了。因为这样实现的话,每发送一条弹幕就意味这要绘制一个弹幕的子View,那肯定是消耗资源的。

有什么方法能减轻压力呢,那通过之前讲解的RecyclerView的缓存我们受到了启发。

1、我们把移除屏幕的弹幕子View不去做销毁,给他缓存起来不就行了吗
2、再需要新的弹幕子View添加的时候,给他复用一下,就会减少绘制,减轻压力了
3、这样就利用了RecyclerView的缓存思想,进行了优化

马上更新()

三、弹幕框架DanmakuFlameMaster

介绍DanmakuFlameMaster

1、弹幕数据的解析:DanmakuFlameMaster支持多种弹幕数据格式,包括ASS、XML、JSON等。它首先根据不同的格式,对弹幕数据进行解析,将其转化为统一的数据结构。

2、弹幕的布局和渲染:通过对解析得到的弹幕数据进行布局,确定每条弹幕在屏幕上的位置和显示时间。然后,使用图像渲染技术,将弹幕绘制到屏幕上。

3、弹幕的控制和交互:DanmakuFlameMaster提供了丰富的弹幕控制和交互功能。用户可以设置弹幕的显示样式、字体颜色、速度等属性。同时,还支持弹幕的暂停、播放、弹幕屏蔽等操作。

4、弹幕的优化和性能提升:为了提高弹幕的渲染效率和用户体验,DanmakuFlameMaster采用了多线程技术和缓存策略。它将弹幕的渲染和显示分离开来,使得弹幕的处理和渲染可以并行进行,从而提高了整体的性能和效率。

总的来说,DanmakuFlameMaster通过对弹幕数据的解析和渲染,以及弹幕的控制和交互,实现了弹幕的高效显示和优化。它在很多弹幕视频网站和应用中被广泛应用。

DanmakuFlameMaster的使用

1、引入DanmakuFlameMaster库
首先,在你的项目的build.gradle文件中添加以下依赖项,以引入DanmakuFlameMaster库:

implementation 'com.github.ctiao:DanmakuFlameMaster:0.8.7'

然后,进行同步操作,确保依赖项成功导入。
2、DanmakuView的基本使用
在你的布局文件中添加DanmakuView控件,并设置相关属性。例如,指定宽高、背景色等:

<su.litvak.danmaku.view.DanmakuView
    android:id="@+id/danmaku_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000" />

在代码中,获取DanmakuView的实例,并进行基本的配置和初始化操作:

DanmakuView danmakuView = findViewById(R.id.danmaku_view);
danmakuView.enableDanmakuDrawingCache(true);
danmakuView.enableDanmakuDropping(true);
danmakuView.setCallback(new DrawHandler.Callback() {
    @Override
    public void prepared() {
        danmakuView.start();
    }

    @Override
    public void updateTimer(DanmakuTimer timer) {
        
    }

    @Override
    public void danmakuShown(BaseDanmaku danmaku) {
        
    }

    @Override
    public void drawingFinished() {
        
    }
});
danmakuView.prepare(parser, mContext);  // 通过解析器解析弹幕数据

3、解析和添加弹幕数据
DanmakuFlameMaster支持从多种来源解析和添加弹幕数据,包括本地文件、URL、InputStream等。
例如,从本地文件解析和添加弹幕数据:

BaseDanmakuParser parser = new BiliDanmukuParser();
DefaultDanmakuContext danmakuContext = DefaultDanmakuContext.create();
danmakuView.prepare(parser, danmakuContext);
danmakuView.showFPS(true);  // 如果需要显示FPS,可以设置为true

try {
    FileInputStream fis = new FileInputStream("path/to/your/danmaku.xml");
    danmakuView.loadData(parser, fis);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

另外,DanmakuFlameMaster还提供了多种配置参数,例如字体、字号、显示区域等,开发者可以根据需要进行自定义设置。
4、控制弹幕的播放和暂停
DanmakuView提供了一些方法,用于控制弹幕的播放和暂停。例如:

danmakuView.start();  // 开始播放弹幕
danmakuView.pause();  // 暂停播放弹幕
danmakuView.resume();  // 恢复播放弹幕
danmakuView.stop();  // 停止播放弹幕
danmakuView.release();  // 释放资源

5、弹幕的发送和屏蔽
DanmakuFlameMaster还支持用户发送弹幕和屏蔽指定类型的弹幕。例如:
发送弹幕:

BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
danmaku.text = "这是一条弹幕";
danmaku.padding = 5;
danmaku.textSize = 25f;
danmaku.textColor = Color.WHITE;
danmaku.setTime(danmakuView.getCurrentTime() + 1200);
danmakuView.addDanmaku(danmaku);

屏蔽指定类型的弹幕:

// 屏蔽顶部弹幕
danmakuContext.setDanmakuFilter(new IDanmakuFilter() {
    @Override
    public boolean filter(BaseDanmaku danmaku, int index, danmakuContext context) {
        if (danmaku.getType() == BaseDanmaku.TYPE_SCROLL_LR) {
            return false;
        }
        return true;
    }
});

ok了,DanmakuFlameMaster的使用就讲解到这里,还有很多玩法自己探索吧。
通过使用这个强大的弹幕库,开发者可以轻松实现弹幕的播放、发送和屏蔽等功能,为视频、直播等场景提供更加丰富的交互体验。

四、初步实现的完整代码

CustomBarrageView.kt

class CustomBarrageView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {

    //弹幕的数据列表
    private var barrageList = mutableListOf<BarrageItem>()

    fun addBarrage(data: BarrageItem){
        //1、初始化弹幕子View
        val childView : TextView = TextView(context).apply {
            text = data.text
            textSize = data.textSize
        }
        //2、测量弹幕子View,也就是measure呗
        childView.measure(measuredWidth, measuredHeight)
        //3、添加弹幕子View
        addView(childView)
        //4、设置弹幕子View的布局,也就是layout呗
        //这里我想让他从右面到左面移动,上下的位置是随机的
        val left = measuredWidth
        val top = nextInt(1, measuredHeight) //kotlin的Random.nextInt函数
        childView.layout(left, top, left + childView.measuredWidth, top + childView.measuredHeight)
        //5、开启弹幕子View动画
        val anim : ValueAnimator = ValueAnimator.ofFloat(1.0f).apply {
            duration = 7000
            interpolator = LinearInterpolator() //线性的插值器
            addUpdateListener {
                val value = it.animatedFraction
                val left = (measuredWidth - value *(measuredWidth + childView.measuredWidth)).toInt()
                //通过改变布局来实现弹幕的滚动
                childView.layout(left, top, left + childView.measuredWidth, top + childView.measuredHeight)
            }

            addListener(onEnd = {
                removeView(childView)
                barrageList.remove(data)
            })
        }

        anim.start()
    }

    fun addBarrageList(dataList : MutableList<BarrageItem>){
        this.barrageList = dataList
        dataList.forEach {
            addBarrage(it)
        }
    }

    override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {

    }
}

BarrageItem.kt

//可以定义图片头像、字体颜色、等等。我用的TextView给大家演示,
//大家可以扩展到自己的自定义弹幕子View上
data class BarrageItem(var text: String, var textSize: Float = 16f)

效果如下:
在这里插入图片描述

总结

总结就是,后面一篇是自定义View实战《圆形头像》,之后给我点点赞。

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

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

相关文章

C++【初识哈希】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 &#x1f307;前言&#x1f3d9;️正文1、哈希思想2、哈希函数2.1、哈希函数的设计原则2.2、常见的哈希函数 3、哈希…

曹操最有名的4首诗

在中国帝王级的人物中间&#xff0c;真正称得上诗人的&#xff0c;曹操必定是最杰出一个。 曹操简介&#xff1a; 曹操&#xff08;155年&#xff0d;220年正月庚子&#xff09;&#xff0c;字孟德&#xff0c;一名吉利&#xff0c;小字阿瞒&#xff0c;沛国谯&#xff08;今…

Redis实现用户签到

用户签到 现在许多的APP或者网上应用为了提高用户的活跃度&#xff0c;都会设置一个每日签到的活动&#xff0c;签到完成后会给予一定的奖励&#xff0c;从而达到其目的。 而记录用户的签到信息可以使用如下一张数据库表来进行记录。 在用户数量并不多的情况下&#xff0c;数…

排序算法--冒泡排序(Java语言)

冒泡排序&#xff08;Bubble Sort&#xff09;是啥&#xff1f; 冒泡排序是一种简单的排序算法。它重复地走访过要排序的元素列&#xff0c;依次比较两个相邻的元素&#xff0c;如果他们的顺序&#xff08;如从大到小、首字母从A到Z&#xff09;错误就把他们交换过来。走访元素…

Android:Lifecycle

整体架构 LifecycleOwner Lifecycle持有者 在ComponentActivity中new出来&#xff0c;所以Activity和Fragment自带 实现了LifecycleOwner接口的类 可以获得生命周期信息类Lifecycle Fragment和Activity都已经实现了该接口 Lifecycle Registry 生命周期注册器 可以提交生命周…

栈板识别的思考

0.啰嗦几句 最近公司又变动了&#xff0c;所以又做了一个关于视觉的项目。简单说就是栈板定位&#xff0c;主要应用在AGV叉车上&#xff0c;当然这一套流程基本适用于所有的视觉项目。主要是看算法的设计和一些人为的经验。 1.结果图 可以看到在图像上3个托盘都分割出来了&am…

青少年机器人技术一级核心知识点:机械结构及模型(三)

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

2024考研408-计算机组成原理第七章-输入输出系统学习笔记

文章目录 前言一、IO系统基本概念1.1、输入/输出系统基本概念1.1.1、现代计算机的结构1.1.2、常见的I/O设备1.1.3、主机与I/O设备如何进行交互&#xff1f;&#xff08;认识I/O接口&#xff09;1.1.4、I/O系统的基本组成&#xff08;I/O硬件、I/O软件&#xff09;本节回顾 1.2、…

标配8155,智驾延续「高低配」,小鹏G6能否挽回颜面?

对于小鹏汽车来说&#xff0c;G6就是最后的尊严。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-5月&#xff0c;小鹏汽车交付量为3.22万辆&#xff0c;同比上年同期下滑39.81%&#xff0c;是「蔚小理」三家新势力中唯一下滑的一家。 去年上市的G9&#xff08;何小鹏口中…

基于声音信号的工业设备异常检测

异常检测主要目标是将异常事件与正常事件区分开来&#xff0c;因此才有了“异常”一词。本文将介绍基于声音信号的工业机械异常检测&#xff0c;使用的数据集是MIMII声音数据集&#xff0c;该数据集很容易在网上获得。 异常检测的任务可以通过多种方式实现。其中最简单的一种方…

Hadoop基础——HDFS知识点梳理

HDFS基础知识 1. 介绍一下HDFS组成架构&#xff1f; 组成部分&#xff1a; HDFS Client,NameNode,DataNodeSecondary NameNode( HA模式下是 StandBy NameNode) Client: 客户端 文件切分&#xff0c;文件上传HDFS时&#xff0c;client将文件切分成一个一个的block&#xff0…

玩转中文AI模型攻略——ModaHub魔搭社区攻略

目录 1. 注册和登录&#xff1a; 2. 浏览和搜索&#xff1a; 3. ModaHub排行榜&#xff1a; 4. AI应用导航&#xff1a; 5. 向量数据库&#xff1a; 6. 企业Chatbot&#xff1a; ModaHub魔搭社区是一个中文AI模型开源社区&#xff0c;旨在为开发者和研究者提供一个交流、…

抓住出海中东热潮,茄子科技(海外SHAREit Group)有效助力游戏企业出海

近年来&#xff0c;随着国际形势的紧张&#xff0c;不少中国出海企业在海外市场营收受到了不同程度的影响。为摆脱这一困境&#xff0c;大家开始将目光瞄准了海外新兴市场&#xff0c;其中中东以其高速的增长&#xff0c;量利双收的回报赢得了不少中国出海企业的青睐。 与此同…

FastDDS源码剖析:源码结构分析

include: 包含Fast-DDS的头文件。这些头文件定义了库使用的公共API和数据结构。 src: 包含Fast-DDS库的源代码文件。这包括各种组件的实现&#xff0c;例如发布者、订阅者、主题和RTPS协议。 cpp: c实现Fast-DDS的主要源代码。 pcp -fastdds: Fast-DDS公共API的实现。 rtps: …

【AI】清华开源中英双语对话模型ChatGLM2-6B本地安装笔记

清华开源中英双语对话模型ChatGLM2-6B本地安装笔记 首先&#xff0c;直接上资源&#xff0c;网盘中是ChatGLM2-6B源码及模型文件&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1DciporsVT-eSiVIAeU-YmQ 提取码&#xff1a;cssa 官方的Readme已经很详尽了&#xff0…

Unreal Engine游戏引擎

Unreal Engine&#xff08;下文简称为UE&#xff09;&#xff0c;是一款由Epic Games开发的游戏引擎&#xff0c;用于创建电子游戏、虚拟现实和增强现实应用、数字孪生等内容。UE支持实时渲染、高品质的图形效果和物理模拟、可扩展的蓝图视觉脚本语言&#xff0c;以及跨平台和多…

Appium Server GUI + Appium Inspector 定位UI元素

Appium Server GUI 下载 Appium Server GUI 安装包并完成安装&#xff0c;完成安装后启动&#xff0c;点击 startServer 快速启动 Appium 服务。 Appium Inspector 下载 Appium Inspector 安装包并完成安装&#xff0c;完成安装后启动&#xff0c;填入以下内容。 Remote Hos…

蓝牙运动耳机推荐、最好用的蓝牙运动耳机排名

在现代快节奏的生活中&#xff0c;健康和锻炼成为越来越多人追求的目标。无论是户外跑步、健身房锻炼&#xff0c;还是徒步旅行&#xff0c;一个好的运动耳机是不可或缺的伴侣。它能为您提供高品质的音乐享受&#xff0c;同时保持舒适的佩戴体验&#xff0c;让您在运动中时刻保…

基于Python和MD5算法的公寓管理系统设计与实现-计算机毕设 附源码77723

基于Python和MD5算法的公寓管理系统设计与实现 摘 要 本论文主要论述了如何使用Python开发一个公寓管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述公寓管理系统的当前背景以及…

jetson nano如何读取pwm波信号?通过编码器读取车速,使用sbus串口读取,解决方案

jetson nano如何读取pwm波信号&#xff1f; 通常我们采用的是通过gpio读取&#xff0c;但是linux系统的时钟特别的不准导致计算的不对。 所以我推荐用sbus 用外围硬件&#xff0c;有pwm转sbus设备&#xff0c; 这个设备可以将pwm波转换位串口数据&#xff0c;我们只需要将输出的…