android inset 管理

news2024/11/26 4:45:06

目录

简介

Insets管理架构

Insets相关类图

app侧的类

WMS侧的类

inset show的流程

接口

流程

WMS侧确定InsetsSourceControl的流程

两个问题

窗口显示时不改变现有的inset状态

全屏窗口上的dialog 不显示statusbar问题

View 和 DecorView 设置insets信息

输入法显示流程

1. 在某个app侧点击编辑框

2.输入法app接收到显示输入法消息

3.InputMethodManagerService接收到输入法app的显示状态信息

4.输入法窗口接收到showInsets@IWindow 

监听inset 变化

设置Insetscontroller变化监听

应用WindowInsets变化


简介

android 11上新增一套inset管理方法。

Insets 是指系统边衬区的窗口, 包括statusbar, navigation bar, 输入法等, 都在insets管理中。下面说的这些insets即为这些窗口。 

通过insets相关的接口, app可以控制insets窗口的显示, 隐藏, 沉浸式等。

inset 最基本的控制是show 和hide。 在inset 不同的状态下, 应用区的位置也会发生变化, 这部分的计算也是inset控制的重要内容之一。  

insets完整的实现逻辑, 包含app端和服务端,服务端主要是WMS(window manager service)。 本文梳理insets的管理架构和主要逻辑, 如show insets 等。

下面为dump 出来的insets信息, 使用命令adb shell dumpsys window。 InsetsState 是系统当前所有inset状态的集合,InsetsSource 对应每一个inset, 包括type, frame, visible项: 

 InsetsState
      InsetsSource type=ITYPE_STATUS_BAR frame=[0,0][2776,130] visible=false
      InsetsSource type=ITYPE_NAVIGATION_BAR frame=[0,0][744,1022] visible=false
      InsetsSource type=ITYPE_TOP_GESTURES frame=[0,0][2776,130] visible=true
      InsetsSource type=ITYPE_BOTTOM_GESTURES frame=[0,1017][744,1022] visible=true
      InsetsSource type=ITYPE_LEFT_GESTURES frame=[0,0][0,1022] visible=true
      InsetsSource type=ITYPE_RIGHT_GESTURES frame=[2776,0][2776,1022] visible=true
      InsetsSource type=ITYPE_TOP_TAPPABLE_ELEMENT frame=[0,0][2776,130] visible=true
      InsetsSource type=ITYPE_BOTTOM_TAPPABLE_ELEMENT frame=[0,1017][744,1022] visible=true
      InsetsSource type=ITYPE_IME frame=[0,0][0,0] visible=false

Insets管理架构

Insets相关类图

分为app侧和系统服务侧(wms)侧。 

app侧的类

  • InsetsState:  为Parceble, 在wms和app中互相传递。其中记录了所有系统Insets的InsetsSource。每个InsetsSource描述了Insets的状态。 可以参看简介中dump信息
  • InsetsSource: 为Parceble, 记录一个inset是否显示及frame。可以参看简介中dump信息。
  •  InsetsSourceControl :为Parceble。  InsetsSourceControl 与某一个insets窗口对应, 可以通过其控制inset show 和hide。app持有相应inset的InsetsSourceControl ,就可以控制该inset的显示和隐藏,如果不持有某个类型的InsetsSourceControl, 就不能控制该类型的inset的显示和隐藏  其中mLeash 为surfaceControl, 可由InsetsAnimationControlImpl获取并交给SyncRtSurfaceTransactionApplier 控制动画进度,如surface位置, 隐藏,显示等。app持有哪些inset的InsetsSourceControl,有wms确定并在ddWindow()和 relayoutWindow()时传回给app。

 app侧持有的InsetsSourceControl来自于添加窗口时 addWindow()和 relayoutWindow()时传回的 mTempControls。 实测 addWindow时mTempControls传回值为null. 在relayoutWindow 时传回mTempControls为实际值。WMS在在relayout 过程中, 会寻找inset 的target 窗口, 通过addToControlMaps@ InsetsStateController.java为该类型的窗口添加target, 然后在 relayoutWindow()中通过调用win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win); 获取该win的InsetsSourceControl 传回给app端的mTempControls。

ViewRootImp.setView()-->
    addWindow(...mTempControls) //获得mTempControls
     mInsetsController.onControlsChanged(mTempControls)-->
           consumer.setControl(control, showTypes, hideTypes);
  • InsetsController: 每个窗口对应一个InsetController。 为app端控制inset显示,隐藏, 更新状态,动画的总调度, 包含 InsetsSourceConsumer map 和 ViewRootInsetsControllerHost。每种insets类型对应一个InsetsSourceConsumer。InsetsController在ViewRootImpl初始化时创建。
创建InsetsController stack, 从下向上
      at android.view.InsetsController.<init>(InsetsController.java:525)
      at android.view.ViewRootImpl.<init>(ViewRootImpl.java:768)
      at android.view.ViewRootImpl.<init>(ViewRootImpl.java:720)
      at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:401)
      at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:109)
  • InsetsSourceConsumer: 中记录了InsetState(从wms传入)及InsetsSourceControl。 InsetsSourceControl来自wms中传入, 具体参看InsetsSourceControl说明。 
  • PendingInsetsController:为窗口被加入到wms之前记录APP要求的状态, 在ViewRootImpl.setview时调用mWindowSession.addToDisplayAsUser之后被同步到InsetsController中。 

WMS侧的类

  • InsetsStateController: 控制全局整体inset状态。

 mProviders为insets的provider的集合。

mTypeControlTargetMap为可以控制每种Insets(如显示或隐藏)的窗口的集合。

mState为InsetsState, 当前系统所有inset 的状态。

(注: 每个WindowState通过InsetsStateController.getInsetsForDispatch获取该窗口的state, 通过relayoutWindow或者addWindow 中的outInsetsState.set(win.getInsetsState(), win.isClientLocal())将当前窗口insetState 转给app。app端通过mInsetsController.onStateChanged @ViewRootImpl将状态设置给InsetsController。  app 根据这个insetstate计算内容区域。 app 计算inset区域:mInsetsController.calculateInsets。)

DisplayContent和InsetsPolicy均持有InsetsStateController,为同一实例。

  • InsetsPolicy: 为insets在wms端的总体策略。
  • InsetsControlTarget即为WindowState,如果某个窗口可以控制某种类型inset, 在该窗口addToWindow()和relayoutWindow()函数中, 返回对应inset的InsetsSourceControl。例如, 当前top窗口仅允许控制statusbar, 则statusbar的InsetsControlTarget为当前top窗口, 传回给当前top窗口InsetsSourceControl 仅有statusbar, 没有导航栏的InsetsSourceControl。 
  • InsetsSourceProvider: win: inset 窗口, 例如输入法窗口, 导航栏窗口。 其中mSource为某个 inset的frame及显示状态。 mFrameProvider:计算inset的frame大小的函数,用于设置mSource中的frame。 在updateSourceFrame()中调用。

inset show的流程

inset 基本的控制是show 和hide, inset show和hide流程基本一样,以show流程说明流程。 

  • 接口

app 控制insets显示或者隐藏调用的接口如下, 关于InsetsController参见InsetsController类说明。

getWindow().getInsetsController().show(insetTypeList) //实际对应InsetsController.show()

getWindow().getInsetsController().hide(insetTypeList) //实际对应InsetsController.hide()

  • 流程

调用InsetsController.show/hide的调用stack如下, 从下到上,这个stack只到InsetsController.notifyVisibilityChanged(),该stack只是记录和参考,不作详细说明。 具体需要注意的是InsetsController.notifyVisibilityChanged()之后流程。

//此函数先调用ViewRootInsetsControllerHost.notifyInsetsChanged  后调用updateRequestedState
	  at android.view.InsetsController.notifyVisibilityChanged(InsetsController.java:1208)  
	  at android.view.InsetsSourceConsumer.setRequestedVisible(InsetsSourceConsumer.java:344)
	  at android.view.InsetsSourceConsumer.show(InsetsSourceConsumer.java:196)
	  at android.view.InsetsController.showDirectly(InsetsController.java:1325)
	  at android.view.InsetsController.controlAnimationUnchecked(InsetsController.java:1013)
	  at android.view.InsetsController.applyAnimation(InsetsController.java:1305)
	  at android.view.InsetsController.show(InsetsController.java:870)
	  at android.view.InsetsController.show(InsetsController.java:826)
  • InsetsController.notifyVisibilityChanged()流程

InsetsController.notifyVisibilityChanged()函数如下, 先调用ViewRootInsetsControllerHost.notifyInsetsChanged  后调用updateRequestedState。

public void notifyVisibilityChanged() {
          mHost.notifyInsetsChanged();  //实际在viewRootImpl中requestLayout, 也就是在下一个vsync中与wms交互去Relayout()。
          updateRequestedState();  //调用了ViewRootInsetsControllerHost.onInsetsModified。 交互流程见下面的流程图。
  }
  • ViewRootInsetsControllerHost.notifyInsetsChanged 实际在viewRootImpl中调用requestLayout, 也就是在下一个vsync中与wms交互去Relayout()。从而获取新的InsetsSourceControl和InsetsState,并刷新界面layout。 这里的notify应该指的是通知本app重新刷新layout。在重新layout后, 会调用mInsetsController.calculateInsets根据inset的显示和占位情况, 计算应用区的大小。 
      at android.view.ViewRootImpl.notifyInsetsChanged(ViewRootImpl.java:1603)  //下一个vsync relayout 
	  at android.view.ViewRootInsetsControllerHost.notifyInsetsChanged(ViewRootInsetsControllerHost.java:54) // 
	  at android.view.InsetsController.notifyVisibilityChanged(InsetsController.java:1208)
  • updateRequestedState用于通知wms端app请求inset变化,wms处理相应的inset显示和隐藏, 并通知其他app系统inset变化。 调用了ViewRootInsetsControllerHost.onInsetsModified,交互流程见下面的流程图。app端调用IWindowSession.insetsModified(IWindow window, in InsetsState state)通知wms inset变化, 其中InsetsState为修改后的inset状态。 wms通过InsetSourceProvider.setClientVisible设置inset窗口显示状态。 然后发送消息给所有的活动窗口, 通知insetsChanged。 每个活动窗口调用mInsetsController.onStateChanged设置自己的inset窗口状态,onStateChanged也会调用notifyInsetsChanged重新relayout 窗口。而发起show流程的窗口,因为state已经修改为当前的状态, 所以onStateChanged不再执行该操作。
      at android.view.ViewRootInsetsControllerHost.onInsetsModified(ViewRootInsetsControllerHost.java:147) //调用WindowSession.insetsModified()通知wms 修改inset visibility
	  at android.view.InsetsController.updateRequestedState(InsetsController.java:1287)
	  at android.view.InsetsController.notifyVisibilityChanged(InsetsController.java:1209)

​​

注: app 调用notifyInsetsChanged的几个地方,仅做参考: 

1. 在onStateChanged@InsetsController中调用, 比如在ViewRootImpl.setView中调用onStateChanged, 从下到上: 

ViewRootInsetsControllerHost.notifyInsetsChanged()
 android.view.ViewRootInsetsControllerHost.notifyInsetsChanged(ViewRootInsetsControllerHost.java:54)
      at android.view.InsetsController.onStateChanged(InsetsController.java:630)  //在ViewRootImpl.setView中调用mWindowSession.addToDisplayAsUser后,wms 返回了当前窗口对应的InsetsSourceControl和InsetsState, 调用本函数      at android.view.ViewRootImpl.setView(ViewRootImpl.java:1059) 
      at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:411)
      at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:109)
      

2. 在setFrame@ViewRootImpl中调用, 如在ViewRootImpl.setView中调用setFrame(), 从小到上

ViewRootInsetsControllerHost.notifyInsetsChanged()

 android.view.ViewRootInsetsControllerHost.notifyInsetsChanged(ViewRootInsetsControllerHost.java:54)
      at android.view.InsetsController.onFrameChanged(InsetsController.java:594)
      at android.view.ViewRootImpl.setFrame(ViewRootImpl.java:7493)
      at android.view.ViewRootImpl.setView(ViewRootImpl.java:1036)
      at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:411)
      at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:109)
      

WMS侧确定InsetsSourceControl的流程

WMS侧如何确定某个app 可以持有哪些InsetsSourceControl。 在该窗口relayout中, 根据当前window 的focus情况, 只有focus的窗口可以获取InsetsSourceControl,也就是控制inset的显示和隐藏, 没有focus的窗口不能获取InsetsSourceControl,也就是不能控制inset的显示和隐藏。当一个窗口设置了FLAG_NOT_FOCUSABLE, 就不在能控制inset的显示和隐藏。流程从下到上:


// addToControlMaps 设置了mControlTargetTypeMap@InsetsStateController.java,   设置了mControlTargetTypeMap后, 通过在relayout 中调用getInsetsSourceControls传回给app进程的mTempControls。 
      at com.android.server.wm.InsetsStateController.addToControlMaps(InsetsStateController.java:523)
      at com.android.server.wm.InsetsStateController.onControlChanged(InsetsStateController.java:469)
      at com.android.server.wm.InsetsStateController.onBarControlTargetChanged(InsetsStateController.java:424)
      at com.android.server.wm.InsetsPolicy.updateBarControlTarget(InsetsPolicy.java:150)
      at com.android.server.wm.DisplayPolicy.updateSystemUiVisibilityLw(DisplayPolicy.java:3940)
      at com.android.server.wm.DisplayPolicy.focusChangedLw(DisplayPolicy.java:3736)
      at com.android.server.wm.DisplayContent.updateFocusedWindowLocked(DisplayContent.java:3286)
      at com.android.server.wm.RootWindowContainer.updateFocusedWindowLocked(RootWindowContainer.java:461)
      at com.android.server.wm.WindowManagerService.updateFocusedWindowLocked(WindowManagerService.java:5552)
      at com.android.server.wm.WindowManagerService.relayoutWindow(WindowManagerService.java:2348)

两个问题

窗口显示时不改变现有的inset状态

两种方法:

 一种就是控制inset 和上一个窗口相同,获取当前insets状态方法: getWindowManager().getCurrentWindowMetrics().getWindowInsets。示例

在onCreate 或 onResume中调用以下代码,也就是mWm.addView(mDecorView, l);之前调用。 
WindowInsets windowInsets = 
        getWindowManager().getCurrentWindowMetrics().getWindowInsets();
hideTypeList = getHideTypeList(windowInsets)//获取hide 的type list。 
showTypeList = getShowTypeList(windowInsets)//获取show 的type list。
WindowInsetsController controller = decorView.getWindowInsetsController();
controller.hide(hideTypeList);
controller.show(hideTypeList);

另一种,就是设置窗口为FLAG_NOT_FOCUSABLE。FLAG_NOT_FOCUSABLE, 导致没有focus window 变化, 从而不能设置获取InsetsSourceControl,也就无法控制所有insets窗口的show和hide。 代码:

 l.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                 // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  // 本句需要注释掉,才能获取InsetsSourceControl。 
                    | WindowManager.LayoutParams.FLAG_FULLSCREEN;

wms中判断应用是否focus的代码: mFindFocusedWindow
          at com.android.server.wm.WindowState.canReceiveKeys(WindowState.java:2877) // 如果设置FLAG_NOT_FOCUSABLE, canReceiveKeys 返回false, 认为改窗口非焦点窗口, 不改变inset设置。

全屏窗口上的dialog 不显示statusbar问题

 受全屏窗口设置影响。 系统单独对status bar做了设置:

​​

View 和 DecorView 设置insets信息

流程如下, DecorView的onApplyWindowInsets 会调用mInsetsController.calculateInsets,计算应用区的大小。

 at com.android.internal.policy.DecorView.onApplyWindowInsets(DecorView.java:1046)//demorview 将inset区域减去,其他的区域作为内容区
	  at android.view.View.dispatchApplyWindowInsets(View.java:11311)
	  at android.view.ViewGroup.dispatchApplyWindowInsets(ViewGroup.java:7320)
	  at android.view.ViewRootImpl.dispatchApplyInsets(ViewRootImpl.java:2311) //会通过calculateInsets 计算当前inset, 将当前inset应用到view。 
	  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2439)
	  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1948)
	  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8179)
	  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
	  at android.view.Choreographer.doCallbacks(Choreographer.java:796)
	  at android.view.Choreographer.doFrame(Choreographer.java:731)
	  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)

输入法显示流程

输入法也是一种inset, 其显示和隐藏的流程也与inset 显示隐藏一致。 下面介绍的是在点击编辑框时,输入法显示流程。

1. 在某个app侧点击编辑框

最后调用showSoftInput@InputMethodManager :

showSoftInput:1587, InputMethodManager (android.view.inputmethod)
onTouchEvent:11082, TextView (android.widget)
dispatchTouchEvent:14309, View (android.view)
dispatchTransformedTouchEvent:3118, ViewGroup (android.view)
dispatchTouchEvent:2799, ViewGroup (android.view)
dispatchTransformedTouchEvent:3118, ViewGroup (android.view)
dispatchTouchEvent:2799, ViewGroup (android.view)
dispatchTransformedTouchEvent:3118, ViewGroup (android.view)
dispatchTouchEvent:2799, ViewGroup (android.view)
dispatchTransformedTouchEvent:3118, ViewGroup (android.view)
dispatchTouchEvent:2799, ViewGroup (android.view)
superDispatchTouchEvent:515, DecorView (com.android.internal.policy)
superDispatchTouchEvent:1879, PhoneWindow (com.android.internal.policy)
dispatchTouchEvent:4135, Activity (android.app)
dispatchTouchEvent:473, DecorView (com.android.internal.policy)
dispatchPointerEvent:14568, View (android.view)
processPointerEvent:6024, ViewRootImpl$ViewPostImeInputStage (android.view)
onProcess:5827, ViewRootImpl$ViewPostImeInputStage (android.view)
deliver:5318, ViewRootImpl$InputStage (android.view)
onDeliverToNext:5375, ViewRootImpl$InputStage (android.view)
forward:5341, ViewRootImpl$InputStage (android.view)
forward:5493, ViewRootImpl$AsyncInputStage (android.view)
apply:5349, ViewRootImpl$InputStage (android.view)
apply:5550, ViewRootImpl$AsyncInputStage (android.view)
deliver:5322, ViewRootImpl$InputStage (android.view)
onDeliverToNext:5375, ViewRootImpl$InputStage (android.view)
forward:5341, ViewRootImpl$InputStage (android.view)
apply:5349, ViewRootImpl$InputStage (android.view)
deliver:5322, ViewRootImpl$InputStage (android.view)
deliverInputEvent:8088, ViewRootImpl (android.view)
doProcessInputEvents:8039, ViewRootImpl (android.view)
enqueueInputEvent:8000, ViewRootImpl (android.view)
onInputEvent:8211, ViewRootImpl$WindowInputEventReceiver (android.view)
dispatchInputEvent:220, InputEventReceiver (android.view)
nativePollOnce:-1, MessageQueue (android.os)
next:335, MessageQueue (android.os)
loop:183, Looper (android.os)
main:7664, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:592, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:947, ZygoteInit (com.android.internal.os)

2.输入法app接收到显示输入法消息

输入法app, 即实现inputmethodservice的app。 其与InputMethodManagerService的接口为IInputMethodWrapper。 InputMethodManagerService 调用 IInputMethodWrapper.showSoftInput, 其发送消息DO_SHOW_SOFT_INPUT。 处理DO_SHOW_SOFT_INPUT, 调用到InputMethodService.showSoftInput (), 最终调用到IInputMethodPrivilegedOperations.applyImeVisibility().IInputMethodPrivilegedOperations 为inputMethodService与InputMethodManagerService的通信接口。 

applyVisibilityInInsetsConsumerIfNecessary:2219, InputMethodService (android.inputmethodservice)
access$400:263, InputMethodService (android.inputmethodservice)
showSoftInput:748, InputMethodService$InputMethodImpl (android.inputmethodservice)
showSoftInputWithToken:718, InputMethodService$InputMethodImpl (android.inputmethodservice)
executeMessage:226, IInputMethodWrapper (android.inputmethodservice)
handleMessage:44, HandlerCaller$MyHandler (com.android.internal.os)
dispatchMessage:106, Handler (android.os)
loop:223, Looper (android.os)
main:7664, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:592, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:947, ZygoteInit (com.android.internal.os)

3.InputMethodManagerService接收到输入法app的显示状态信息

输入法app调用InputMethodManagerService.applyImeVisibility() 通知InputMethodManagerService(IMMS)显示状态变化,IMMS然后调用到scheduleShowImePostLayout@ImeInsetsSourceProvider,该函数在下一个vsync调用输入法窗口的windowState.showInsets通知输入法窗口: ims: show.  

scheduleShowImePostLayout:52, ImeInsetsSourceProvider (com.android.server.wm)
showImePostLayout:7615, WindowManagerService$LocalService (com.android.server.wm)
applyImeVisibility:4088, InputMethodManagerService (com.android.server.inputmethod)
access$4700:188, InputMethodManagerService (com.android.server.inputmethod)
applyImeVisibility:5935, InputMethodManagerService$InputMethodPrivilegedOperationsImpl (com.android.server.inputmethod)
onTransact:336, IInputMethodPrivilegedOperations$Stub (com.android.internal.inputmethod)
execTransactInternal:1154, Binder (android.os)
execTransact:1123, Binder (android.os)

4.输入法窗口接收到showInsets@IWindow 

输入法在接收到ims显示状态变化后, 如下面流程,调用InsetsController.show, 后面的流程和前面介绍的inset show的流程一致: 通知wms ims inset状态变化,  并通知给所有活动中的窗口。 在下一个vsync, 调用relayoutWindow(),重新布局。下面为输入法窗口的showInsets流程。

  • "main@15899" prio=5 tid=0x2 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
    	  at android.view.InsetsController.updateRequestedState(InsetsController.java:1262)
    	  at android.view.InsetsController.notifyVisibilityChanged(InsetsController.java:1209)
    	  at android.view.InsetsSourceConsumer.setRequestedVisible(InsetsSourceConsumer.java:344)
    	  at android.view.InsetsSourceConsumer.show(InsetsSourceConsumer.java:196)
    	  at android.view.InsetsController.showDirectly(InsetsController.java:1325)
    	  at android.view.InsetsController.controlAnimationUnchecked(InsetsController.java:1013)
    	  at android.view.InsetsController.applyAnimation(InsetsController.java:1305)
    	  at android.view.InsetsController.show(InsetsController.java:870)
    	  at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:5031)  //处理 MSG_SHOW_INSETS, MSG_SHOW_INSETS 为wms 调用iWindow.showInsets触发。
    	  at android.os.Handler.dispatchMessage(Handler.java:106)
    	  at android.os.Looper.loop(Looper.java:223)
    	  at android.app.ActivityThread.main(ActivityThread.java:7664)
    	  at java.lang.reflect.Method.invoke(Method.java:-1)
    	  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    	  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
    

监听inset 变化

窗口中的view可以在insets变化时, 改变默认的inset 占位行为。 监听inset变化, 然后自行设置insets如何占位。 如:

  • 设置Insetscontroller变化监听

InsetsController.addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener ...);
  • 应用WindowInsets变化

getWindow().getDecorView().setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener ...);
          @Override
          public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
              mImeVisible = insets.isVisible(ime());
              return v.onApplyWindowInsets(insets);
          }

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

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

相关文章

通讯基本概念

通信的方式有多种&#xff0c;按数据传输方式可分为串行通讯和并行通信&#xff1b;按通信数据同步方式可分为同步通信和异步通信&#xff1b;按数据通信的方向可分为 一、串行通信和并行通信 串行通信&#xff1a;设备之间通过少量的数据信号线&#xff08;一般是8根以下&am…

Windows自动化实现:系统通知和任务栏图标自定义

文章目录 Windows自动化的三个小工具系统通知任务栏图标使用pystray实现使用infi.systray实现 Windows自动化的三个小工具 系统通知 import win10toastwin10toast.ToastNotifier().show_toast("eee", "休息一下", icon_path"icon.ico", durati…

scikit-learn 1.3.X 版本 bug - F1 分数计算错误

如果您正在使用 scikit-learn 1.3.X 版本&#xff0c;在使用 f1_score() 或 classification_report() 函数时&#xff0c;如果参数设置为 zero_division1.0 或 zero_divisionnp.nan&#xff0c;那么函数的输出结果可能会出错。错误的范围可能高达 100%&#xff0c;具体取决于数…

第十三篇【传奇开心果系列】Python的OpenCV库技术点案例示例:光流估计

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例:光流估计短博文目录前言一、光流估计介绍二、Lucas-Kanade光流介绍和示例代码三、Horn-Schunck光流介绍和示例代码四、cv::calcOpticalFlowPyrLK()函数实现光流估计介绍和示例代码五、光流估计用于运动分析…

shell——2月3日总结

操作系统有什么用&#xff1f; 操作系统是计算机系统中的核心软件&#xff0c;负责管理和协调计算机的硬件和软件资源&#xff0c;提供用户界面&#xff0c;并执行用户程序。 硬件管理&#xff1a;操作系统管理计算机的硬件资源&#xff0c;包括处理器、内存、磁盘、外部设备等…

MySQL原理(一)架构组成之逻辑模块(2)缓存机制

前面提到了mysql的逻辑模块中包含Query Cache 。 一、查询缓存 1、作用 MySQL查询缓存即缓存查询数据的SQL文本及查询结果,用Key-Value的形式保存在服务器内存中。当查询命中缓存,MySQL会立刻返回结果,跳过了解析,优化和执行阶段。 2、查询缓存的命中条件 &#xff08;1&a…

基于ChatGLM.cpp实现低成本对ChatGLM3-6B的量化加速

文章目录 1. 参考2. ChatGLM3 介绍3. 本地运行3.1 硬件配置3.2 下载ChatGLM3代码3.3 量化模型3.4 编译和运行3.4.1 编译3.4.12 运行 4. python绑定4.1 安装4.2 使用预先转换的 GGML 模型 总结 前面两章分别有讲到基于MacBook Pro M1芯片运行chatglm2-6b大模型和如何在本地部署c…

《Python 网络爬虫简易速速上手小册》第1章:Python 网络爬虫基础(2024 最新版)

文章目录 1.1 网络爬虫简介1.1.1 重点基础知识讲解1.1.2 重点案例&#xff1a;社交媒体数据分析1.1.3 拓展案例1&#xff1a;电商网站价格监控1.1.4 拓展案例2&#xff1a;新闻聚合服务 1.2 网络爬虫的工作原理1.2.1 重点基础知识讲解1.2.2 重点案例&#xff1a;股票市场数据采…

玩家笔记:幻兽帕鲁搭建服务器开服教程

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

MyBatis之环境搭建以及实现增删改查

MyBatis之环境搭建以及实现增删改查 前言准备工作1.保证数据库已启动2. 创建Person表 MyBatis开发环境搭建1.下载MyBatis jar包2.下载MySQL的JDBC驱动3.新建Java工程&#xff08;Java8&#xff09;&#xff0c;导入MyBatis的jar包以及JDBC驱动 实现步骤1. 创建Peron类2. 编写Ma…

Log360,引入全新安全与风险管理功能,助力企业积极抵御网络威胁

ManageEngine在其SIEM解决方案中推出了安全与风险管理新功能&#xff0c;企业现在能够更主动地减轻内部攻击和防范入侵。 SIEM 这项新功能为Log360引入了安全与风险管理仪表板&#xff0c;Log360是ManageEngine的统一安全信息与事件管理&#xff08;SIEM&#xff09;解决方案…

【复现】WordPress html5-video-player SQL 注入漏洞_39

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 在WordPress中播放各种视频文件。一个简单&#xff0c;可访问&#xff0c;易于使用和完全可定制的视频播放器&#xff0c;适用于所…

0基础学习VR全景平台篇第141篇:如何制作卫星航拍全景

大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 很多人都看过或者拍摄过航拍全景&#xff0c;其效果相比于普通的地拍的确有着更加震撼的拍摄效果&#xff0c;但是受限于无人机高度&#xff0c;以及禁飞区等等限制&#xff0c;导致很多大场景无法展示完全&a…

绝地求生:盘点游戏内七款真人脸模,你最喜欢哪款?

从27.1版本更新后&#xff0c;游戏内上线了荣都地图代言人吴彦祖和李政宰的真人脸模&#xff0c;从此闲游盒的各位盒友灵魂搭配的资源库里又多了两位英俊脸庞&#xff0c;那么今天闲游盒来盘点一下游戏内上线的七款真人脸模&#xff0c;看看大家更喜欢哪款呢? 吴彦祖和李政宰 …

前端文艺复兴:Vue3真的需要Pinia吗?

前言 说起Pinia&#xff0c;熟悉 vue3 开发的程序员肯定不会陌生&#xff0c;甚至被vue官方推荐取代vuex&#xff0c;成为vue全家桶之一。 疑惑 还记得之前用 vuex 时&#xff0c;更改 state 还分同步和异步&#xff08;这里有尤雨溪的回答www.zhihu.com/question/48… &…

【实训】网络规划与部署实训

一 实训目的及意义 本周实训主要是了解网络规划与部署&#xff0c;熟悉三大厂商华为、思科、锐捷交换机路由器以及相关协议的原理和配置&#xff0c;提高学生的动手能力和分析规划部署能力。 实训主要针对计算机网络系统集成的设计与实现的实际训练&#xff0c;着重锻炼学生熟练…

【数据结构]排序算法之插入排序、希尔排序和选择排序

简单不先于复杂&#xff0c;而是在复杂之后。 文章目录 1. 排序的概念及其运用1.1 排序的概念1.2 排序运用1.3 常见的排序算法 2. 常见排序算法的实现2.1 插入排序2.1.1 基本思想2.1.2 直接插入排序2.1.3 希尔排序&#xff08;缩小增量排序&#xff09; 2.2. 选择排序2.2.1 基本…

重写Sylar基于协程的服务器(6、HOOK模块的设计)

重写Sylar基于协程的服务器&#xff08;6、HOOK模块的设计&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、日志模…

【STM32+HAL库+CubeMX】UART轮询收发、中断收发、DMA收发方法及空闲中断详解

&#xff08;转载&#xff09;原文链接&#xff1a;https://blog.csdn.net/qq_39344192/article/details/131470735 1. 什么是UART&#xff1f; UART是一种异步串行通信接口&#xff0c;常用于通过串口与外部设备进行通信。它通过发送和接收数据帧来实现数据传输&#xff0c;使…

ROS2 Humble学习笔记 (2)

本文发表于个人的github pages。因csdn本身显示插件和转载过程中导致显示不太友好。建议大家阅读原文。想查看完整内容&#xff0c;请移步到ROS2 Humble学习笔记2。 本文篇幅较长&#xff0c;可抽空按照章节阅读。本文只作为对入门教程的一种浮现和提升。 一、前言 在上一篇…