Android事件分发

2023-05-16

基本知识
什么是触摸事件
触摸事件,是Android用来描述你的手对屏幕做的事情的最小单元。关键词有两个:手势(你的手对屏幕做的事情)、最小单元。

所谓手势,就是比如按下、移动、抬起、点击事件、长按事件、滑动事件。
所谓最小单元,就是不可再拆分的,比如按下(down)、移动(move)、抬起(up)就是不可再拆分的。而点击事件、长按事件、滑动事件则是由那些不可再拆分的事件组合起来的。比如点击事件是由按下、抬起组成的。长按事件是按下并保持一段时间不动。注意一下滑动事件和移动(move)的区别,滑动事件可以理解为是由若干个“move”组合起来的,系统对触摸的捕捉是很灵敏的,手指放在屏幕上稍微抖一下,都会产生“move”事件。用手指在屏幕上滑一段距离,我们感觉是一次滑动,但是系统已经产生了很多个“move”事件。
我们会遇到的触摸事件,大多就是按下(down)、移动(move)、抬起(up),有时候你可能会遇到取消(cancel)事件,但是事件分发重点关注的大多都是前三者。

MotionEvent对象
一个触摸事件包括事件类型和坐标两类信息:

事件类型(包括但不局限于):ACTION_DOWN(按下)、ACTION_ MOVE(移动)、ACTION_ UP(抬起)。
坐标:坐标还分参考系,有以屏幕作为参考系的坐标值,也有以被触摸的View作为参考系的坐标值。
站在面向对象的角度,MotionEvent对象就是对触摸事件相关信息的封装,这两类信息在MotionEvent对象里面都能找到。motionEvent.getAction()可以获得事件类型,每个类型在MotionEvent里都有对应的常量,motionEvent.getRawX()、motionEvent.getRawY()可以获得以屏幕作为参考系的坐标值,motionEvent.getX()、motionEvent.getY()可以获得以被触摸的View作为参考系的坐标值。

一个完整的事件序列
注意区分一个触摸事件和一个完整的事件序列两个概念。当手指在屏幕上按下、抬起,会产生一系列事件:ACTION_DOWN(一个)–>ACTION_MOVE(数量不定)–>ACTION_UP(一个),从ACTION_DOWN到ACTION_UP,称为一个完整的事件序列,即某一次手指按下到手指抬起算是一个完整的事件序列,下一次手指按下到抬起是另一个完整的事件序列。

笔者没有真的考究过Android官方是不是真的有“事件序列”这么一个概念性的名词,只是这个名词在一些讲解事件分发的博客里面有出现过。但是在ViewGroup的源码里面,确实是可以找到在遇到ACTION_DOWN时清除某些标记位(以避免受到前一个事件序列的影响),在非ACTION_DOWN的情况下,某次触摸事件传递受前一次触摸事件处理结果的影响之类的行为,确实反映了两个相邻的ACTION_DOWN和ACTION_UP之间的事件处理是有关联,下一个ACTION_DOWN会尽量和前一个ACTION_UP相互独立的情况。所以我们是可以认为确实有“事件序列”这么一个逻辑存在。

那么为什么要有事件序列这种处理逻辑呢,在搞懂了事件分发的流程之后,笔者个人的看法是,这是为了让Android在处理触摸事件的时候尽量“拟人化”。怎么就拟人化了呢,触摸事件是站在计算机的角度来看手势的,而事件序列是站在人(严格来讲是用户那种人,不是程序员那种人)的角度来看待手势的。比如我们点击一个按钮,人的反应应该是“我就点了一下”,而不是“我先按下、然后抬起”。比如刷新闻、刷资讯的动作,人的反应应该是“我向上滑了一下”,而不是“我先按下、然后向上移动了若干次(若干个ACTION_ MOVE事件),然后我抬起手”。将两个相邻的ACTION_DOWN和ACTION_UP及其中间所有的事件联系在一起,在需要的时候关联处理,这种结果更加符合人的感受,有更好的用户体验。

为什么要有事件分发
简单地讲,事件分发就是为了解决“谁来干活”的问题。当一个事件发生,有超过一个View(或者ViewGroup)能够对它进行响应(处理)时,就要确定到底谁来处理这个事件。比如下图:

为什么要有事件分发

如果触摸事件发生在点A,那么毫无疑问是由ViewGroup来处理的。如果触摸事件发生在点B,那就要确定到底是ViewGourp还是View来处理这个事件。

View事件分发
这里要讲的View的事件分发,是指View.java源码里对触摸事件进行传递的流程。

流程图
声明,以下关于View事件分发的流程图是根据Android源码2.3.7版本绘制的。流程如下图所示:

View事件分发

流程说明
View没有子View,不存在拦截行为,所以View的事件分发比较简单。

从dispatchTouchEvent()开始,进入当前View的事件分发流程,该方法只负责分发事件,没有实际进行事件处理。
有可能处理事件的有两个地方,一个是外部设置的OnTouchListener监听器,即OnTouchListener的onTouch(),一个是View内部的处理方法,即onTouchEvent()。而且外部设置的监听器优先获取事件。
当外部设置的监听器处理了事件(即有设置监听器、View处于ENABLE状态、监听器在onTouch()里面返回true三者均成立),dispatchTouchEvent()返回true,当前View的onTouchEvent()不会触发。
如果外部的监听器不处理事件,则dispatchTouchEvent()的返回值由View自己的onTouchEvent()决定。
注意一下,对于上级ViewGroup(也就是当前View的父容器)而言,它只认识子View的dispatchTouchEvent()方法,不认识另外两个处理事件的方法。子View的OnTouchListener和onTouchEvent()都是在自己的dispatchTouchEvent()里面调用的,他们两个会影响dispatchTouchEvent()的返回值,但是对于上级ViewGroup而言,它只认识dispatchTouchEvent()的返回值就足够了。

ViewGroup事件分发
这里要讲的ViewGroup的事件分发,是指ViewGroup.java源码里对触摸事件进行传递的流程。

流程图
声明,以下关于ViewGroup事件分发的流程图是根据Android源码2.3.7版本绘制的。这个流程图只是描述了事件分发的主要流程,言下之意如果你拿源码过来对照,你会发现有些东西这个流程图里面没有体现。比如对于ACTION_UP和ACTION_CANCEL事件有些清空标志位的逻辑在流程图里是没有的,比如把触摸事件传给子View时对触摸坐标进行转变的逻辑也没体现。笔者认为相对于事件传递本身而言,这些细枝末节的东西画出来既没有加大我们对事件传递的理解,还增加了理解复杂度,所以省略。

流程如下图所示:

ViewGroup事件分发

ViewGroup事件分发的特点(规律)
相对于View的事件分发,ViewGroup的就明显复杂多了。在说明这幅图之前,笔者先给出这幅图里面的特点,有了这些特点,你就不会觉得混乱。就笔者的梳理而言,ViewGroup的事件分发可以总结有这样几个特点:

ViewGroup的事件分发“基于”责任链模式,同时是不纯的责任链模式。之所以说是“基于”是因为事件分发其实和责任链模式不完全相同,应当说事件分发的流程更加复杂一些。
同一个事件序列里面的不同触摸事件之间的影响,通过变量target、disallowIntercept来实现,所以这两个变量的情况会影响事件的传递流程,这也是和一般的责任链模式不同的地方。
ACTION_DOWN事件在传递的同时,执行着“寻找target”的任务。而非ACTION_DOWN事件则在使用target(如果有target),即根据target以及和target相关的东西(disallowIntercept)控制事件传递流程。
事件的传递有“抛回来”的行为,就像是两个相邻的节点之间推卸责任。ViewGroup和它的target(如果有)之间就像在对话:“这锅你背”、“你背你背,不用客气”。
为什么要梳理这些规律?我们从事件分发的流程总结出规律,再通过这些规律倒回去理解事件分发的流程为什么要这样设计。在接下来的流程说明里面,我们通过具体流程来理解这几个特点。对于上图我们采取自上而下,从左向右的顺序来看。每一个触摸事件相当于责任链模式里面的一个请求,每一个ViewGroup或者是子View相当于责任链模式里面的一个处理器(处理者),在流程说明当中笔者会不断地对流程图、责任链模式、上面说的几个特点这三者进行对比,以加深理解。

流程说明:ACTION_DOWN事件
我们先来看ACTION_DOWN事件的流程:

ACTION_DOWN事件流程图

流程说明:

从ViewGroup的dispatchTouchEvent()开始,进入当前ViewGroup的事件分发流程。
首先要做的事情,自然是判断是不是迎来了一个新的事件序列,所以要判断该事件是否是ACTION_DOWN事件。
如果是ACTION_DOWN事件,作为一个事件序列的开头,应当要消除前面的事件序列可能留下的影响,所以接下来做的事情就是清空target。既然没有了target,自然也就不需要考虑disallowIntercept的影响,所以整个ACTION_DOWN事件里面没有去根据target和disallowIntercept的情况进行事件传递的。
接下来的事情就和一般的责任链模式类似,onInterceptTouchEvent()就像在责任链模式里面某个处理器判断是否处理当前请求,如果不处理(即不拦截),就沿着责任链往后传。
事件传给了子View,如果子View处理了,ViewGroup就找到target了,同时由于返回true,当前ViewGroup成了上级ViewGroup的target,找到了target就相当于找到了同个事件序列的后续触摸事件的“背锅侠”。
现在我们回退一步,如果子View不处理,这个“锅”就得ViewGroup自己担着,这就是我们前面说的“推卸责任”的特点。所以事件会传递到super.dispatchTouchEvent()。ViewGroup类继承自View类,也就是进入了前文说的View事件分发流程,就相当于询问当前ViewGroup自己是否处理这个事件,细节这里就不重复了。如果当前ViewGroup自己处理了,对于上级ViewGroup而言,还是找到了target,如果当前ViewGroup不处理,这个“锅”继续抛给上级ViewGroup。
接着继续回退一步,如果拦截了,就和一般的责任链模式类似,所以就不需要经过子View,直接由ViewGroup自己处理该事件。如果处理了,则当前ViewGroup就是上级ViewGroup的target,否则,事件还是抛回给上级ViewGroup。
以上,ACTION_DOWN事件的传递就说完了。简单地讲,ACTION_DOWN事件传递的最终结果,就是为ViewGroup找到target,如果找到了,target就是后续触摸事件的“背锅侠”,如果找不到,当前ViewGroup选择是否成为上级ViewGroup的target。

流程说明:除了ACTION_DOWN以外的事件
接下来看非ACTION_DOWN事件的流程:

非ACTION_DOWN事件流程图

流程说明:

既然ACTION_DOWN事件负责寻找target,那么非ACTION_DOWN事件做的第一件事,就是看看有没有target。这里也体现了同个事件序列里面某个触摸事件受前面的触摸事件的影响。
如果没有“背锅侠”(没有target),那这个锅就得当前ViewGroup自己担着,所以直接传递给了super.dispatchTouchEvent()。从这里你可以看出,如果在ACTION_DOWN里面没有找到target,那么同个事件序列后续的所有事件,都不会再有“背锅侠”,都是当前ViewGroup自己担着(如果ViewGroup能收到这些事件的话)。将target指向某个子View这个行为,只在ACTION_DOWN里面才有可能发生。
对于有target的情况,你从图里可以看到,往下总共分了三条路线:右边的路线是禁止拦截、左边的路线是不禁止拦截然后拦截、中间的路线是不禁止拦截然后不拦截。接下来我们单独对这共计三个情况进行说明,因为从有target开始往下走,才是我们在处理事件冲突的时候最经常遇到的情况。
流程说明:对于有target情况下的事件传递
接下来看在非ACTION_DOWN事件时,对于有target情况下的事件传递流程说明:

首先说明一下disallowIntercept变量,它是一个boolean类型的变量,表示子View是否禁止拦截触摸事件。它的取值,可以通过ViewGroup的requestDisallowInterceptTouchEvent(boolean)来修改,这个方法是public修饰的,如果子View在前一个触摸事件里面通过调用该方法设置了禁止拦截标识,后续的事件就会直接传给子View。注意一下,如果这个标识为禁止拦截,说明子View必须要这个事件,但是如果标识为不禁止拦截,并不是说明子View就不要这个事件了,只能认为子View“还没确定”要它。什么叫做“还没确定”要?我们举个例子来谈这个问题。
如果现在要你自己处理类似ViewPager嵌套ListView(即ViewGroup要横向滑动,里面的子View要竖向滑动)的冲突,基本思路是比较简单的,ViewGroup和子View均捕获触摸事件,如果ViewGroup发现是横向滑动,就拦截并且处理。如果子View发现是竖向滑动,就设置disallowInterceptTouchEvent为禁止拦截,然后自己处理事件。那么紧接着的问题就是,如何发现横/竖向滑动?自然是收集若干个触摸事件,然后根据这几个连续的触摸事件分析手指到底是朝哪个方向滑动。那么在你能够确定用户在朝哪个方向滑动之前,ViewGroup和子View都处于“迷茫”状态:它们两者均不知道手指到底朝哪滑动,也就是它们均不知道自己到底要不要处理这些事件。我们之前讨论事件传递过程的时候总在说如果ViewGroup要处理(或者不要处理),如果子View要处理(或者不要处理),而事实是存在这样一个情况:有段时间它们两者均不知道自己要不要处理这个事件。这个时候怎么办呢?合理的作法应当是让事件继续流经ViewGroup和子View,直到其中一方做出处理。此时,子View不应该禁止ViewGroup拦截事件,同时,ViewGroup也不应该拦截事件。这样,触摸事件就会经过中间这条路线:不禁止拦截然后不拦截。所以这条路线,是用来服务于ViewGroup和子View都还没确定要不要处理事件的情况。
如果子View认为手指是在竖向滑动,它决定要处理事件,它就会将diallowInterceptTouchEvent设置为禁止拦截,事件的传递就会走向右边的路线。自此,同一个事件序列后面的所有触摸事件,都会交给子View处理。
如果ViewGroup认为手指是在横向滑动,它就要拦截并处理后续事件,事件的传递就会走向左边的路线,于是后续事件不再需要传给子View,所以会将target置空。接着要告诉上级ViewGroup,自己需要接手这些事件,于是返回true。接下来的行为和ACTION_DOWN事件在拦截之后的行为有些不同,刚刚被拦截下来的事件并不传给super.dispatchTouchEvent(),后续的事件才会传给 super.dispatchTouchEvent()。这个行为笔者是这样理解的,从ACTION_DOWN开始,到最后一个流经onInterceptTouchEvent()事件之间的所有事件,构成了一个判断条件(类似开关),这个判断条件供onInterceptTouchEvent()用来判断是否拦截后续事件,这些判断条件本身是不需要被处理的,它们只是用来提供判断的,在这些判断条件之后的事件,才是需要处理的。所以是在被拦截的事件之后的事件,才开始流向super.dispatchTouchEvent()。
事件分发的应用套路

ViewGroup事件分发重复贴图
这里主要说的是两级View(一个ViewGroup和里面一个子View)的情况下,如何根据实际需要控制事件传递。所谓的套路,其实还是说规律,但是我们前面已经说了事件传递的规律了,所以现在说的是实际操作时的使用规律。为了方便阅读,重新把图再贴一次:

我们分几大类情况来说,旨在说明在不同的情况下,应该如何选择不同支路,也就是控制事件传递的流程。

ViewGroup需要一切事件:只有你的ViewGroup明确需要一切触摸事件,而且不打算传递给任何子View的时候,会在ACTION_DOWN事件里面拦截事件,这样所有的事件都会传给ViewGroup自己,并且不会下发给任何子View。此时从一开始就要选择的路线是:ACTION_DOWN事件–>onInterceptTouchEvent()拦截–>super.dispatchTouchEvent()处理。后续事件全部传给super.dispatchTouchEvent()。
子View需要一切事件:只有你的子View明确需要一切事件,而且不打算让ViewGroup处理任何事件的时候,要求ViewGroup不要拦截ACTION_DOWN事件,子View必须处理ACTION_DOWN事件,并且设置disallowIntercept标记为禁止拦截。此时从一开始需要走的路线是:ACTION_DOWN事件–>onInterceptTouchEvent()不拦截–>子View处理并且设置disallowIntercept为禁止拦截。后续事件全部传给子View,ViewGroup不会再有机会拦截。
ViewGroup和子View只需要特定情况下的事件(就好比前面我们说的例子,横竖两个方向的事件冲突):此时对于ACTION_DOWN事件,ViewGroup不能拦截,子View必须处理,这样才能保证两者均能收到后续事件。对于ACTION_DOWN以外的事件,在两者都还没能决定是否要处理事件的时候,需要走的路线是:有target–>不禁止拦截–>不拦截–>传给子View的dispatchTouchEvent()。这样能够让ViewGroup和子View均能不断收集事件,以便进行是否处理的判断。一旦子View决定要处理事件,设置disallowIntercept为禁止拦截,避免后续事件被ViewGroup拦截。一旦ViewGroup决定要处理事件,在onInterceptTouchEvent()里面进行拦截,避免后续事件被子View抢走。
至此,我们已经分析完了常见的事件处理需要流经的路线,从实际需求的角度出发反过去看事件传递,你是否能更深入地理解ViewGroup的事件分发为什么要设计成这样呢?

总结
正如本文开头所说,本文的侧重点是通过流程图来分析Android事件分发,除了文章开头介绍一些基本知识,全文基本都在说明流程图。

ViewGroup的事件分发是Android事件分发的重点,也是本文花最大篇幅描述的内容。不单止要说清楚ViewGroup的事件分发流程,还要说清楚这个流程有什么规律、为什么要这么设计、流程上的每个节点都在干什么。最后,站在实践的角度,说明在面对不同需求的时候,该如何选择流程图里的不同路线,实现需求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android事件分发 的相关文章

  • 从队列更新活动的最佳方法

    我有一个LinkedBlockingQueue在我的 生产者 调解者 消费者 模型中的调解者中 Producer 首先更新将 Mediator 添加到 ActivityQueue 中 接下来 消费者 活动在队列中等待 侦听并获取下一个项目
  • RecyclerView 未显示列表中的所有项目

    我在用RecyclerView在我的应用程序中 每次我打开屏幕时 我只能看到一项 但当我调试时 它每次都会出现onBindViewHolder method 这是我的适配器 Override public ViewHolder onCrea
  • Android 布局不需要的填充

    所以我有这个布局文件 如下 正如您所看到的 没有填充或边距 dimen xml 文件也没有任何填充 边距 最后 我根本不以编程方式更改布局
  • 在Android中将半径边框绘制到imageview或textview的一个角落

    我需要在我的应用程序中为图像视图或文本视图绘制边框 但我只需要在一个角落绘制它 就像图像一样 我做了一个形状 但我在所有 4 个边上都有边框
  • Android 从键盘读取

    我的登录屏幕根本没有文本字段 当用户使用 RFID 扫描仪扫描他的 id 令牌时 我会得到一个 8 个字符长的字符串 其原理与使用键盘相同 只是更快 我希望我的登录活动在用户扫描其令牌时而不是之前执行 有一个聪明的方法来实现这个吗 我不能有
  • Notification.Builder 中 setGroup() 的用途是什么?

    我对目标的理解有些困难setGroup http developer android com reference android app Notification Builder html setGroup java lang String
  • Android Accessibility 执行触摸操作

    我想知道是否可以使用 Android 辅助功能服务在屏幕上的位置执行触摸操作 例如 Bundle arguments new Bundle arguments putInt coord X X value arguments putInt
  • Android:拍照后调用裁剪活动

    我在解析拍摄照片的 uri 来裁剪活动时遇到问题 在我的应用程序中 用户可以拍摄一张照片或从图库中选择一张照片 然后裁剪并上传 一切听起来都很简单 从图库中选择时 图库应用程序会返回所选照片的 uri 如下所示 content media
  • 如何找到特定路线上两点之间的距离?

    我正在为我的大学开发一个 Android 应用程序 可以帮助学生跟踪大学巴士的当前位置 并为他们提供巴士到达他们的预计时间 截至目前 我获取了公交车的当前位置 通过公交车上的设备 和学生的位置 我陷入了必须找到两个 GPS 坐标之间的距离的
  • 仅在 Android 应用程序中使用 XHDPI 可绘制对象?

    如果您计划在不久的将来支持 LDPI MDPI HPDI 或许还有 XHDPI 那么是否可以在项目中仅包含 XHDPI 可绘制对象并让设备将其缩放到所需的分辨率 我已经测试过在 Photoshop 中将可绘制对象的大小调整为 MDPI 和
  • opencv人脸检测示例

    当我在设备上运行应用程序时 应用程序崩溃并显示以下按摩 java lang UnsatisfiedLinkError 无法加载 detector based tracker findLibrary 返回 null 我正在使用 OpenCV
  • 取消通知

    我使用Onesignal推送通知 需要取消所有onPause和onResume的通知 NotificationManager notificationManager NotificationManager getApplicationCon
  • 使用 DiffUtil 在 RecyclerView 上添加拖放

    我有一个从房间数据库更新的列表 我从 Room 收到更新的数据作为新列表 然后将其传递给列表适配器 https developer android com reference androidx recyclerview widget Lis
  • 无法在 Java 中输出正确的哈希值。怎么了?

    在我的 Android 应用程序中 我有一个 SHA256 哈希值 我必须使用 RIPEMD160 消息摘要算法进一步对其进行哈希值 我可以输出任何字符串的正确 sha256 和ripemd160 哈希值 但是当我尝试使用ripemd160
  • BadPaddingException:无效的密文

    我需要一些帮助 因为这是我第一次编写加密代码 加密代码似乎工作正常 但解密会引发错误 我得到的错误是 de flexiprovider api exceptions BadPaddingException 无效的密文 in the 解密函数
  • 从多个 TextView 中选择文本

    如何在android中从多个文本视图中选择文本 我已经尝试过以下代码 该代码一次仅适用于一个文本视图 我想一次性从许多文本视图中复制文本 android textIsSelectable true 你不能同时这样做 您需要在单个文本视图中设
  • SQLiteDatabase.openDatabase 与 SQLiteOpenHelper.getReadableDatabase

    这两种方法有什么区别吗 两者都返回一个打开的 SQLiteDatabase 如果数据库不存在 两者都可以创建数据库 当需要读 写时 SQLiteOpenHelper 还具有 getWriteableDatabase 我应该使用哪种方法以及在
  • Android BLE 扫描永远找不到设备

    几天以来 我尝试在我的应用程序中实现 BLE 连接 我知道我尝试连接的设备功能齐全 因此问题一定是我的代码 我用BluetoothLeScanner startScan 方法 但回调方法永远不会被调用 public void startSc
  • 画布:尝试使用回收的位图错误

    我是一个相当新的程序员 所以任何建议将不胜感激 我有一个类 每次调用它时都会在循环中运行 AsyncTask AsyncTask 看起来像这样 public class LoadImageTask extends AsyncTask
  • 进程被杀死后不会调用 onActivityResult

    我有一个主要活动 Main 和另一个活动 Sub 由 Main 调用 startActivityForResult new Intent this SubActivity class 25 当我在 Sub 时 我终止该进程 使用任务管理器或

随机推荐

  • docker安装使用系列二之容器、镜像、仓库使用实例分析

    可能大家对docker了解不深 xff0c 这里再简单赘述一下docker这款利器 1 什么是docker Doker是基于GO语言实现的云开源项目 xff0c 通过对应用组件的封装 分发 部署 运行等生命周期的管理 xff0c 达到应用组
  • 图像处理之opencv库使用小结

    OpenCV是一个基于BSD许可 xff08 开源 xff09 发行的跨平台计算机视觉库 xff0c 可以运行在Linux Windows Android和Mac OS操作系统上 它轻量级而且高效 由一系列 C 函数和少量 C 43 43
  • react 启动项目遇到的问题

    当启动react 项目时遇到 xff1a 39 react scripts 39 不是内部或外部命令 xff0c 也不是可运行的程序 npm install npm install 下载依赖遇到安装失败 xff0c 则依赖包删除不干净 xf
  • Android LED电子表时钟字体digital font

    字体效果如下图所示 xff1a 这种类型的字体样式会被一些UI设计用于Android APP中时钟显示 xff0c 比如交通灯倒计时 实现这种字体样式 xff0c 先导入一个字体包 xff1a digital ttf 这个digital t
  • Android音视频处理之MediaCodec

    MediaCodec是Android中媒体编解码器 xff0c 可以对媒体进行编 解码 MediaCodec采用同步 异步方式处理数据 xff0c 并且使用了一组输入输出缓存 xff08 ByteBuffer xff09 通过请求一个空的输
  • 计算相机投影矩阵(含代码)(Python)

    计算相机投影矩阵 xff08 含代码 xff09 xff08 Python xff09 前几天处理点云时 xff0c 需要使用到像片与3D点云的对应关系 在这边找了一圈没有发现直接可用的代码 xff0c 于是去GitHub试了一下 xff0
  • H264 SPS中得到宽高的代码(java)

    数据需要去掉头 xff0c SPS测试数据 byte buffer 61 new byte 103 66 64 12 38 5 7 56 7 124 2 得到结果宽320高240 public class H264SpsParser pri
  • git 调换提交顺序

    前两个commit交换顺序 1 查看提交历史 git log oneline 2 把要调整顺序的commit显示在vim中 git rebase i a33d521 a33d521用来确定commit范围 xff0c 表示从此提交开始到当前
  • android hmacSha256 加密

    public class HMACSHA256 public static String hmacSha256 String KEY String VALUE return hmacSha KEY VALUE 34 HmacSHA256 3
  • Java生成固定长度的随机字符串(以大小写字母和数字)

    public class RandomStringUtil public static ArrayList lt String gt strList 61 new ArrayList lt String gt public static R
  • Android reckon 控制项目打包版本

    reckon 用法 github地址 xff1a https github com ajoberstar reckon 根项目 gradle配置 buildscript apply from 39 versions gradle 39 re
  • ArrayList源码解析

    构造函数 Constructs an empty list with an initial capacity of ten 使用10个初始容量构造一个空的集合 public ArrayList super 用一个空的数组进行初始化 this
  • 2023年有效的rtsp,rtmp,hls流媒体测试地址整理汇总

    rtsp rtsp wowzaec2demo streamlock net vod mp4 BigBuckBunny 115k mov 已停用 rtsp wowzaec2demo streamlock net vod mp4 BigBuck
  • http请求

    HTTP请求报文 一个HTTP请求报文由请求行 xff08 request line xff09 请求头部 xff08 header xff09 空行和请求数据4个部分组成 1 请求行 请求行分为三个部分 xff1a 请求方法 请求地址和协
  • http响应报文

    HTTP响应报文主要由状态行 响应头部 空行以及响应数据组成 1 状态行 由3部分组成 xff0c 分别为 xff1a 协议版本 xff0c 状态码 xff0c 状态码描述 其中协议版本与请求报文一致 xff0c 状态码描述是对状态码的简单
  • centos7+jdk8+安装Elasticsearch6.0

    一 xff1a 为Elasticsearch准备用户 1 添加用户 Elasticsearch6 0需要使用非root用户启动 root 64 66 adduser ela root 64 66 passwd ela 2 授权用户 查看文件
  • Retrofit2 源码解析

    0 基本使用 1 Retrofit 将我们的 HTTP API 转换成一个 接口形式 所以我们第一步定义一个 interface public interface GitHubService 64 GET 34 user user repo
  • Android Studio插件的源文件位置——mac端

    有些时候安装插件后 xff0c 整个android studio都卡住了 xff0c 无法通过Android Studio gt preferences gt plugins来卸载 xff0c 这时候就需要找到安装位置 xff0c 进行删除
  • H.264编码基础知识详解

    一 编码基础概念 1 为什么要进行视频编码 xff1f 视频是由一帧帧图像组成 xff0c 就如常见的gif图片 xff0c 如果打开一张gif图片 xff0c 可以发现里面是由很多张图片组成 一般视频为了不让观众感觉到卡顿 xff0c 一
  • Android事件分发

    基本知识 什么是触摸事件 触摸事件 xff0c 是Android用来描述你的手对屏幕做的事情的最小单元 关键词有两个 xff1a 手势 xff08 你的手对屏幕做的事情 xff09 最小单元 所谓手势 xff0c 就是比如按下 移动 抬起