Android电视项目焦点跨层级流转

news2024/12/1 5:17:54

1. 背景

在智家电视项目中,主要操作方式不是触摸,而是遥控器,通过Focus进行移动,确定点击进行的交互,所以在电视项目中焦点、选中、确定、返回这几个交互比较重要。由于电视屏比较大,在一些复杂页面中会存在一级Tab选择,二级选择,三级选择等,这就涉及到了焦点与选中的联动实现业务逻辑。这块的逻辑比较复杂,在做好了一个页面后,把这块的内容记录一下,同时提炼出了一个辅助类,MultiLevelFocusHelper,后续可进行复用。

2. 基本使用:遥控器+焦点控制

2.1 使用原则

Android原生就能比较好的支持Focus及切换,使用时只要按照它本身的逻辑使用就好,如果碰到不能很好支撑业务的时候再进行扩展,如下是我们小组实践过后,总结出来的几项原则,实际效果很好:

  • 不进行过度控制,使用默认规则

  • 使用focusable、descendantFocusability把XML中的控件按照父控件统一管控,如必须下放时再进行子控件控制

  • nextFocusUp、nextFocusDown、nextFocusLeft、nextFocusRight、nextFocusForward这几个属性不要轻易使用,只要在需要定制的复杂页面才有可能用到

2.2 View中涉及到焦点的几个属性

属性

使用场景说明

focusable

物理按键时获得焦点的属性 android:focusable="false" android:focusable="true"

descendantFocusability

该属性是当一个view获取焦点时,定义viewGroup和其子控件两者之间的关系,属性的值有三种:

  • beforeDescendants:viewgroup会优先其子类控件而获取到焦点

  • afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点

  • blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

nextFocusUp

nextFocusDown

nextFocusLeft

nextFocusRight

android:nextFocusUp-定义当点up键时,哪个控件将获得焦点

android:nextFocusDown-定义当点down键时,哪个控件将获得焦点

android:nextFocusLeft-定义当点left键时,哪个控件将获得焦点

android:nextFocusRight--定义当点right键时,哪个控件将获得焦点

nextFocusForward

我是谁,我有什么用???

2.3 如何使用

1. XML中从顶到细,一层一层的看,如果此View及其子View不需要获得焦点,则直接把它的焦点屏蔽掉

android:focusable="false"
android:descendantFocusability="blocksDescendants"

2. 如果只有此ViewGroup需要获得焦点,它的子View不需要,则设置如下

android:focusable="true"
android:descendantFocusability="blocksDescendants"

3. RecyclerView或ListView,根据需要,如果是简单的能自动处理的则只修改XML即可,否则可以XML+代码进行控制

// 1. 第一种情况:recyclerView的 xml 设置 recyclerView 不获得焦点,子控件获得焦点
android:focusable="false"
android:descendantFocusability="afterDescendants"

// recyclerView的item 布局中添加
android:focusable="true"
android:descendantFocusability="blocksDescendants"


// 2. 第二种情况:代码控制时, recyclerView先获得焦点,然后根据需要,再在它的OnFocusChangeListener中进行焦点转移
android:focusable="true"
android:descendantFocusability="beforeDescendants"

4. 至此,如果没有特殊的需求,只是简单的焦点控制,通过XML配置属性+Android强大的默认功能即可完成

3. 高级用法:增加层级

3.1 层级是什么? 为什么要有三态?

如图,感兴趣的往下看,一切尽在图中,祭镇楼图

  • 图中的设备列表与全屋节能信息构成了一级焦点,后边的节电数据范围是二级焦点,它俩是一个整体,这里暂且起名叫节能数据查看

  • 其中全屋节能信息是一个ViewGroup,下边的设备列表是一个RecyclerView

  • 图中的帮助按钮是另一个可欺获得焦点的控件,与上边的节能数据查看是并列关系

  • 根据以上分析,得出:层级就是 完成同一个功能的多级多控件的可分别获得焦点的聚合体,特点如下:

    • 焦点可在多级中的多个控件中自由流转,同时只有一个控件具备焦点

    • 在同一级中,如果没有焦点,则需要有一个控件具备已选中状态,由此引出了三态:有焦点、无焦点选中、无焦点未选中

    • 焦点在多级流转时有一定的规则,大部分情况下是从一级流向另一级时,优先流到已选中的控件上

    • 多级具备方向性,比如1->2->3-4, 或 4->3->2->1, 在这个模型中,不可以跨级流转,如果后续有跨级流转的业务需求,再另说(产品经理不要搞太复杂呀...)

3.2 自定义的层级管理辅助类:MultiLevelFocusHelper

基于以上的层级焦点定义,我封装了一个辅助类,MultiLevelFocusHelper,可用于简化层级焦点的操作实现,它主要实现的功能有:

  • 当某一层级的控件获得焦点时,通过它可记录最新的有焦点控件,并同时设置其中选中状态

  • 设置当前层级有焦点的控件往下一级流转时的按键,并精准定位到下一级的选中控件上

  • 获得所有层级的当前控件对应的附加数据

  • 遵循了最小实现、不过渡设计的原则,当前只实现了两级,如果将来需要支持更多的级数,可扩展此类

代码如下:


class MultiLevelFocusHelper(private val totalLevel: Int) {
    private var mCurLevel1View: View? = null
    private var mCurLevel1ViewId: Int? = null
    private var mCurLevel1Data: Any? = null

    private var mCurLevel2View: View? = null
    private var mCurLevel2ViewId: Int? = null
    private var mCurLevel2Data: Any? = null

    /**
     * 某一个控件得到了焦点
     *  @param level: 得到焦点的控件的层级
     *  @param view:  得到焦点的控件
     *  @param viewId: 得到焦点的控件的Id,如果是recycleView,则设置它的父控件的Id,主要是为的是上下级Level切换
     *  @param extraData: 得到焦点的控件对应的附属数据,暂存一下,后续业务需要时可直接get
     *  @param nextLevelMoveDirect:
     */
    fun receiveFocus(level: Int, view: View, viewId: Int, extraData: Any) {
        if (level > totalLevel) return

        when(level) {
            1 -> {
                if (mCurLevel1View != null) {
                    mCurLevel1View!!.isSelected = false
                }
                mCurLevel1View = view
                mCurLevel1View!!.isSelected = true
                mCurLevel1ViewId = viewId

                mCurLevel1Data = extraData
            }
            2 -> {
                if (mCurLevel2View != null) {
                    mCurLevel2View!!.isSelected = false
                }
                mCurLevel2View = view
                mCurLevel2View!!.isSelected = true
                mCurLevel2ViewId = viewId

                mCurLevel2Data = extraData
            }
            else -> {
                // nothing
            }
        }
    }

    /**
     * 设置某一层级当前选中View的 nextFocusLeftId, nextFocusDownId 等
     * @param moveDirect, 移动方向,用于决定设置当前级Level的哪个属性 nextFocusLeftId 等。 传入值:使用本类中定义的四个常量值,多个方向时可进行&运算再传进来
     * @param moveCommander, 移动命令,是前进还是后退,用于确定要设置的Value是上一级还是下一步当前选中的View
     *                      为 null,忽略,如果是头尾的,只有一个方向,直接设就行。 如果是中间的则忽略不进行设置了。
     */
    fun setDirectToCurrentView(level: Int, moveDirect: Int, moveCommander: MoveCommander? = null) {
        if (level > totalLevel) return

        when(level) {
            1 -> {
                // 第一层,只能往下移,不能回移
                setNextMoveTarget(mCurLevel1View, moveDirect, mCurLevel2ViewId)
            }
            2 -> {
                if (level < totalLevel) {
                    if (moveCommander != null) {
                        if (moveCommander == MoveCommander.forward) {
                            // TODO, 当 totalLevel 大于等于 3 的时候,加上这一个分支, 它应该往 3 去移动了
                            // setNextMoveTarget(mCurLevel2View, moveDirect, mCurLevel3ViewId)
                        } else {
                            setNextMoveTarget(mCurLevel2View, moveDirect, mCurLevel1ViewId)
                        }
                    }
                } else {
                    // 这是最后一层, 只有一个方向
                    setNextMoveTarget(mCurLevel2View, moveDirect, mCurLevel1ViewId)
                }
            }
            else -> {
                // nothing
            }
        }
    }

    /**
     * 所有控件失去焦点, 暂时应该没有场景调到它,如果有的话,需要考虑一下行为是否正确
     */
    fun clearAllFocus() {
        if (mCurLevel1View != null) {
            mCurLevel1View!!.isSelected = false
        }
        mCurLevel1Data = null

        if (mCurLevel2View != null) {
            mCurLevel2View!!.isSelected = false
        }
        mCurLevel2Data = null
    }

    /**
     * 获得某一层当前选中控件对应的 View
     */
    fun getView(level: Int): View? {
        if (level > totalLevel) return null

        return when(level) {
            1 -> {
                mCurLevel1View
            }

            2 -> {
                mCurLevel2View
            }

            else -> {
                null
            }
        }
    }

    /**
     * 获得某一层当前选中控件对应的数据
     */
    fun getData(level: Int): Any? {
        if (level > totalLevel) return null

        return when(level) {
            1 -> {
                mCurLevel1Data
            }

            2 -> {
                mCurLevel2Data
            }

            else -> {
                null
            }
        }
    }

    private fun setNextMoveTarget(view: View?, direct: Int?, nextViewId: Int?) {
        if (view == null || direct == null || nextViewId == null) {
            return
        }

        if (direct and Direct_Up > 0) {
            view.nextFocusUpId = nextViewId
        }
        if (direct and Direct_Right > 0) {
            view.nextFocusRightId = nextViewId
        }
        if (direct and Direct_Down > 0) {
            view.nextFocusDownId = nextViewId
            view.nextFocusDownId
        }
        if (direct and Direct_Left > 0) {
            view.nextFocusLeftId = nextViewId
        }
    }
}

3.3 MultiLevelFocusHelper要点说明

  1. 构造函数中的参数 totalLevel

    1. 总级数,从1开始的, 比如totalLevel为3, 则所有级别即为1,2,3

    2. 目前 totalLevel 最大为 2,超过2 按 2 计算

  2. 对外函数receiveFocus(level: Int, view: View, viewId: Int, extraData: Any)

    1. 当层级中的某一个控件获得焦点时调用此函数

    2. 参数说明

      1. @param level: 得到焦点的控件的层级

      2. @param view: 得到焦点的控件

      3. @param viewId: 得到焦点的控件的Id,如果是recycleView,则设置它的父控件的Id,主要是为的是上下级Level切换

      4. @param extraData: 得到焦点的控件对应的附属数据,暂存一下,后续业务需要时可直接get

    3. 这里的 viewId 可以是 view 的Id,也可以不是, 基本用法是,如果是ListView或RecyclerView,则可以把viewId设置为 recyclerView 的Id,这样再在业务代码的 recyclerView 获得焦点事件中转一下即可

  3. 层级流转

    1. level 移动顺序: 目前是一个约定,不能自定义。 1->2->3->4, 或 4->3->2->1。 如果后续有不同需求,可以再进行扩充

    2. 两个概念:MoveCommander, MoveDirect:

      // 层级移动命令,向前进,还是后退,参考按照类说明了中的移动顺序
      enum class MoveCommander {
          forward,
          back
      }
      
      // 焦点移动方向,比如按了遥控器上的上下左右, 使用Int值表示, 多个方向时可以进行&运算
      val Direct_Up = 0x01
      val Direct_Right = 0x02
      val Direct_Down = 0x04
      val Direct_Left = 0x08
    3. 对外函数:setDirectToCurrentView(level: Int, moveDirect: Int, moveCommander: MoveCommander? = null)

      1. 设置某一层级当前选中View的 nextFocusLeftId, nextFocusDownId 等,当某一个控件获得焦点后,再马上调用此函数设置一下

      2. 参数说明

        1. moveDirect, 移动方向,用于决定设置当前级Level的哪个属性 nextFocusLeftId 等。 传入值:使用本类中定义的四个常量值,多个方向时可进行&运算再传进来

        2. moveCommander, 移动命令,是前进还是后退,用于确定要设置的Value是上一级还是下一步当前选中的View为 null,忽略,如果是头尾的,只有一个方向,直接设就行。 如果是中间的则忽略不进行设置了

4. 使用实例

这里附上全屋节能的使用示例,它结合了 MultiLevelFocusHelper,并在Activity中实现了业务关联的一部分代码

4.1 相关控件的XML设置

  1. 设置所有没有焦点的控件中的属性, focusable 和 descendantFocusability

  2. 有焦点的控件属性设置上, focusable 和 descendantFocusability

  3. recyclerView 设置为: android:focusable="true" android:descendantFocusability="beforeDescendants"

4.2 帮助按钮的Focus监听不必设置,使用系统默认的即可

4.3 初始化时,把默认的Focus给到 一级中的全屋信息

mMultiLevelFocusHelper.receiveFocus(1, mFullHouseSaveInfo, mFullHouseSaveInfo.id, "all") // 初始一化一下 mMultiLevelFocusChangeManager 中的状态
mMultiLevelFocusHelper.setDirectToCurrentView(1, MultiLevelFocusHelper.Direct_Right)
mMultiLevelFocusHelper.receiveFocus(2, mTextViewSaveElectricDurationLastMonth, mTextViewSaveElectricDurationLastMonth.id, ElectricIndexDateRange.LAST_MONTH)
mMultiLevelFocusHelper.setDirectToCurrentView(2, MultiLevelFocusHelper.Direct_Down)
mFullHouseSaveInfo.requestFocus()

4.4 RecyclerView 和 它的 item 设置 OnFocusChangeListener

mRecyclerViewDeviceDetailInfo.setOnFocusChangeListener(object : OnFocusChangeListener {
    override fun onFocusChange(v: View?, hasFocus: Boolean) {
        if (v == null) return
        if (!hasFocus) return
        val view = mMultiLevelFocusHelper.getView(1)
        val tag = view?.getTag()    // 看它有没有存 tag 来判断它是不是 recyclerView 的 item
        if (view == null || tag == null) {
            // 没有上一次的View 或 上一次的第一层View 不是 recyclerView的 item 时
            if (mRecyclerViewDeviceDetailInfo.getChildAt(0) != null) {
                mRecyclerViewDeviceDetailInfo.getChildAt(0).requestFocus()
            }
        } else {
            view.requestFocus()
        }
    }
})


// 这里的最后一个参数 OnFocusChangeListener, 内部又传给了 item, 当它有 FocusChange事件时,再转调用此参数实例
mAdapterDeviceDetailInfo = SaveEnergyAdapterDeviceDetailInfo(
    mViewModal.getAllSavingDevice(),
    mViewModal.getAllSavingDeviceRank(),
    mViewModal.getAllSavingSwitchStatus(),
    object: OnFocusChangeListener {
        // 给 设备列表的 recycleview item 设置焦点移动回调
        override fun onFocusChange(v: View?, hasFocus: Boolean) {
            if (v == null) {
                return
            }
            if (!hasFocus) {
                return
            }
            val deviceId = v.getTag()
            mMultiLevelFocusHelper.receiveFocus(1, v, mRecyclerViewDeviceDetailInfo.id, deviceId)
            mMultiLevelFocusHelper.setDirectToCurrentView(1, MultiLevelFocusHelper.Direct_Right)
            initSavingElectricData()
        }
    })

这里啰嗦一下,RecyclerView拿到焦点时,把焦点转给它下边的之前具有焦点的控件;item中的view有一个tag,存的是业务数据(deviceId),当它拿到焦点时,取到此业务数据,传入到了 mMultiLevelFocusHelper 中

4.5 设置全屋信息 和 所有二级控件的 setOnFocusChangeListener,代码略

5. 总结

  1. 如果没有特殊的需求,只是简单的焦点控制,通过XML配置属性+Android强大的默认功能即可完成。

  2. 如果具有多个层级,焦点需要在多层级间进行流转并需要记忆功能,则可使用MultiLevelFocusHelper类,经过实践检验,可完美应用于此场景。

6. 团队介绍

三翼鸟数字化技术平台-场景设计交互平台」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

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

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

相关文章

Linux -初识 与基础指令1

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【Linux】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 文章目录 &#x1f4da; 前言&#x1f5a5;️ 初识&#x1f510; 登录 root用户&#x1f465; 两种用户➕ 添加用户&#x1f9d1;‍&#x1f4bb; 登录 普通用户⚙️ 常见…

Oracle12.2 RAC集群管理修改IP地址(DNS解析)

Oracle12.2 RAC集群管理之修改IP地址 该章节实验是基于此章节基础上操作&#xff1a; Oracle LinuxR7安装Oracle 12.2 RAC集群实施&#xff08;DNS解析&#xff09;-CSDN博客 环境 改前IP&#xff1a; 172.30.21.101 hefei1 hefei1.hefeidb.com 172.30.21.102 hefei2 …

Git——本地仓库链接并推送到多个远程仓库

步骤 1. 新建仓库init 或 删除已有仓库远程链接 // 1.新建init git init// 2.已有仓库&#xff0c;查看链接的远程仓库 git remote -v// 3.已有远程连接仓库&#xff0c;需要删除连接 git remote rm origin(或对应远程仓库名) 2.新建远程仓库 在gitee、github等托管平台创建…

IDEA某个Impl下的引入的文件红色

IDEA某个Impl下的引入的文件红色&#xff0c;可以正常启动&#xff0c;而且文件是存在的 1.什么情况下会出现这个问题 我的是在不关闭项目的情况下就把电脑关机了&#xff0c;因为这样第二天开机&#xff0c;启动IDEA就会把昨天关机前所有开启的项目全部开启 &#xff0c;这样有…

docker使用(镜像、容器)

docker基础使用 文章目录 前言1.镜像操作1.1命令介绍1.2.案例实操1.2.1查找镜像1.2.2下载镜像1.2.3查看当前镜像 2.容器操作2.1命令2.1.1容器创建与启动2.1.2. 容器查看2.1.3. 容器操作2.1.4. 容器删除2.1.5. 容器日志2.1.6. 容器内文件操作2.1.7. 容器内命令执行2.1.8. 其他常…

6.STM32之通信接口《精讲》之IIC通信---硬件IIC(STM32自带的硬件收发器)

上一节&#xff0c;完成了对IIC软件的实验程序&#xff0c;也就是说只要我们编程能够模拟IIC协议规定的时序&#xff0c;所有IIC的外设就能解析IIC的读出我们数据&#xff0c;就能和相关IIC外设进行交互&#xff0c;然后&#xff0c;STM32自带硬件收发电路&#xff0c;接下来我…

springboot337校园失物招领系统pf(论文+源码)_kaic

校园失物招领网站的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定校园失物招领网站…

智能探针技术:实现可视、可知、可诊的主动网络运维策略

网络维护的重要性 网络运维是确保网络系统稳定、高效、安全运行的关键活动。在当今这个高度依赖信息技术的时代&#xff0c;网络运维的重要性不仅体现在技术层面&#xff0c;更关乎到企业运营的方方面面。网络运维具有保障网络的稳定性、提升网络运维性能、降低企业运营成本等…

mybatis笔记01——初始配置

JavaEE三层架构&#xff1a;表现层&#xff08;负责与用户的交互&#xff0c;通常实现了用户界面&#xff09;、业务逻辑层&#xff08;处理核心业务规则和逻辑&#xff0c;是应用程序的“心脏”。&#xff09;、数据访问层&#xff08;负责与数据源&#xff08;如数据库&#…

第7篇 寻找最大数___ARM C语言程序<三>

Q&#xff1a;可以将寻找到的最大数结果显示在DE1-SoC开发板的硬件外设如红色LED上吗&#xff1f; A&#xff1a;基本原理&#xff1a;对红色LED的Data寄存器进行写操作即可。DE1-SoC_Computer系统上连接到红色LED的并行端口的内存映射地址为0xFF200000&#xff0c;是一个18位…

力扣刷题TOP101:6.BM7 链表中环的入口结点

目录&#xff1a; 目的 思路 复杂度 记忆秘诀 python代码 目的 {1,2},{3,4,5}, 3 是环入口。 思路 这个任务是找到带环链表的环入口。可以看作是上一题龟兔赛跑&#xff08;Floyd 判圈算法&#xff09;的延续版&#xff1a;乌龟愤愤不平地举报兔子跑得太快&#xff0c;偷偷…

webrtc视频会议学习(三)

文章目录 关联&#xff1a;源码搭建coturn服务器nginx配置ice配置需服务器要开放的端口 效果 关联&#xff1a; webrtcP2P音视频通话&#xff08;一&#xff09; webrtcP2P音视频通话&#xff08;二&#xff09; webrtc视频会议学习&#xff08;三&#xff09; 源码 WebRTC…

mac上的建议xftp 工具

mac上的建议xftp 工具 最近使用mac比较频繁了&#xff0c;但是第一次重度使用mac里面有很多的工具都是新的&#xff0c;有的window版本的工具无法使用。 xftp 的平替 Cyberduck 从它的官网上下载是免费的&#xff0c;但是如果使用 Apple store 要花费198呢。这不就剩下一大笔…

paimon的四种changelog模式(1)-input模式

环境创建 CREATE CATALOG fs_catalog WITH (typepaimon,warehousefile:/data/soft/paimon/catalog );USE CATALOG fs_catalog;drop table if exists t_changelog_input;CREATE TABLE t_changelog_input (age BIGINT,money BIGINT,hh STRING,PRIMARY KEY (hh) NOT ENFORCED )WIT…

【趣味】斗破苍穹修炼文字游戏HTML,CSS,JS

目录 图片展示 游戏功能 扩展功能 完整代码 实现一个简单的斗破苍穹修炼文字游戏&#xff0c;你可以使用HTML、CSS和JavaScript结合来构建游戏的界面和逻辑。以下是一个简化版的游戏框架示例&#xff0c;其中包含玩家修炼的过程、增加修炼进度和显示经验值的基本功能。 图片…

一款现代化的轻量级跨平台Redis桌面客户端

Tiny RDM‌ 是一款现代化的轻量级跨平台Redis桌面客户端&#xff0c;专为开发和运维人员设计&#xff0c;旨在提供便捷、高效的Redis操作体验。它支持macOS、Windows和Linux操作系统&#xff0c;安装包大小约为10MB&#xff0c;具有广泛的兼容性和便携性‌。 功能特性 ‌轻量级…

【大数据学习 | Spark调优篇】Spark之JVM调优

1. Java虚拟机垃圾回收调优的背景 如果在持久化RDD的时候&#xff0c;持久化了大量的数据&#xff0c;那么Java虚拟机的垃圾回收就可能成为一个性能瓶颈。因为Java虚拟机会定期进行垃圾回收&#xff0c;此时就会追踪所有的java对象&#xff0c;并且在垃圾回收时&#xff0c;找…

《使用Python进行数据挖掘:理论、应用与案例研究》

嘿&#xff0c;今天我要给你们介绍一本使用Python进行数据挖掘的好书。这本书是由吴迪博士撰写的&#xff0c;他是雷曼学院商学院的助理教授&#xff0c;也是数据科学的实战派。 在这个时代&#xff0c;数据多得让人眼花缭乱&#xff0c;要从中找出有用的信息&#xff0c;那可不…

C++之C++11新特性(三)--- 智能指针

目录 一、智能指针 1.1 为什么需要智能指针 1.2 内存泄漏 1.2.1 内存泄漏的基本概念 1.2.2 内存泄漏的分类 1.2.3 如何避免内存泄漏 1.3 智能指针的使用及其原理 1.3.1 RAII 1.3.2 智能指针的基本原理 1.3.3 auto_ptr 1.3.4 unique_ptr 1.3.5 shared_ptr 1.3.6 sha…

Flink学习连载文章8--时间语义

Time的分类 (时间语义) EventTime:事件(数据)时间,是事件/数据真真正正发生时/产生时的时间 IngestionTime:摄入时间,是事件/数据到达流处理系统的时间 ProcessingTime:处理时间,是事件/数据被处理/计算时的系统的时间 EventTime的重要性 假设&#xff0c;你正在去往地下停…