- 框架视图结构
- 内容区域
- 面板区域
- PanelSwitchHelper 构建及使用细节
框架定义了 PanelSwitchLayout 容器,该容器内容由内容区域和面板区域构成。
同时基于上述结构,框架框支持以下两种模式
框架提供了多种 Container 容器
LinearContentContainer
可当作LinearLayout
功能使用RelativeContentContainer
可当作RelativeLayout
功能使用FrameContentContainer
可当作FrameLayout
功能使用
所有提供的 Container 容器内部都是由实现了 IContentContainer
接口的 ContentContainerImpl
对象代理处理。开发者可使用 ContentContainerImpl
模仿框架提供的各种 Container 来实现自定义的 Container。 Demo 中 CusContentContainer
就是基于约束布局实现的一个例子。
例子可参考 Demo:
- API-内容容器(线性布局)
- API-内容容器(相对布局)
- API-内容容器(帧布局)
- API-内容容器(自定义布局)
开发中你可能会遇到:“在某种场景中,希望点击 Container 区域来隐藏已显示的面板,可能是输入法(输入法也是一种年版),也可能是业务面板。”
框架基于这些场景,各个容器提供了自动隐藏面板的API,详情可查看 Demo 中的 ResetActivity
。
自动隐藏面板功能被定义在 container 的扩展属性内,包含
-
auto_reset_enable
表示是否支持点击内容区域内隐藏面板,默认打开。打开时,当区域内子view没有消费事件时,则会默认消费该事件并自动隐藏。 -
auto_reset_area
, 当且仅当 auto_reset_enable 为 true 才有效,指定一个 view 的id,为auto_reset_enable
的消费事件限定区域。- 比如场景一,指定了空白透明 view ,view 没有消费事件时,则才会自动隐藏面板;
- 比如场景二,指定了列表的 recyclerview ,则recyclerview 没有消费事件时,则才会自动隐藏面板;
- 比如场景三,场景二 recyclerview 时显然很难不消费事件,如果 holder 被点击(比如聊天项),则应该被正常消费如果点击 recyclerview 内的空白,recyclerview 也会默认消费,因为需要滑动.
为了解决这种下层应该消费点击滑动事件,而上层容器应该获取点击并自动隐藏,
HookActionUpRecyclerView
就是该场景的 Demo,需要把下层消费完之后的 ACTION_UP 返回 false 让上层有机会处理。ContentContainerImpl
内的实现预留了这种可能,用于处理该复杂场景。
例子可参考 Demo:
- API-点击内容容器收起面板(默认处理)
- API-点击空白 View 收起面板
- API-点击原生 Recyclerview 收起面板
- API-点击自定义 RecyclerView 收起面板
- API-关闭点击内容容器收起面板
面板区域是一个 FrameLayout
容器,内部可存放各个功能面板。
<!-- 面板区域,仅能包含PanelView-->
<com.effective.android.panel.view.panel.PanelContainer
android:id="@+id/panel_container"
android:layout_width="match_parent"
android:background="@color/common_page_bg_color"
android:layout_height="wrap_content">
<!-- 默认的 PanelView -->
<com.effective.android.panel.view.panel.PanelView
android:id="@+id/panel_emotion"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:panel_layout="@layout/panel_emotion_layout"
app:panel_trigger="@id/emotion_btn" />
<!-- 除了使用框架提供的 PanelView,也可以使用自定义 Panel -->
<com.example.demo.scene.api.CusPanelView
android:id="@+id/panel_addition"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cus_panel_trigger="@id/add_btn"
app:cus_panel_toggle="true"/>
</com.effective.android.panel.view.panel.PanelContainer>
框架默认提供了 PanelView
来定义一个功能面板,其定义了多个属性:
panel_trigger
用户点击该 ID 对应的 View 时切换到该面板panel_layout
用于扩展该面板的布局,功能和 标签相似panel_trigger
用于当该面板显示时 ,用户再次点击 panel_trigger 对应的 View 时是否回切输入法
同时嗨提供 IPanelView
接口用于自主实现 PanelView
。例子可参考 Demo:API-自定义PanelView 容器
可在 Activity/Fragment/Dialog/DialogFragment/PopupWindow 等使用 PanelSwitchHelper,如下代码
PanelSwitchHelper mHelper = new PanelSwitchHelper.Builder(this)
.addKeyboardStateListener {
onKeyboardChange {
//可选实现,监听输入法变化
}
}
.addEditTextFocusChangeListener {
onFocusChange { _, hasFocus ->
//可选实现,监听输入框焦点变化
}
}
.addViewClickListener {
onClickBefore {
//可选实现,监听触发器的点击
}
}
.addPanelChangeListener {
onKeyboard {
//可选实现,输入法显示回调
}
onNone {
//可选实现,默认状态回调
}
onPanel {
//可选实现,面板显示回调
}
onPanelSizeChange { panelView, _, _, _, width, height ->
//可选实现,输入法动态调整时引起的面板高度变化动态回调
}
}
.addContentScrollMeasurer { //可选,滑动模式下,可以针对内容面板内的view,定制滑动距离,默认滑动距离为 defaultDistance
getScrollDistance { defaultDistance -> defaultDistance - 200 }
getScrollViewId { R.id.recycler_view }
}
.addPanelHeightMeasurer { //可选 用于设置未获取输入法高度前面板的高度,如果不设置则默认以框架内高度为主
synchronizeKeyboardHeight { false } // 设置面板高度是否和键盘高度同步
getTargetPanelDefaultHeight { DisplayUtils.dip2px(this@DefaultHeightPanelActivity,400f)}
getPanelTriggerId { R.id.add_btn }
}
.logTrack(true)
.build(true)
其中,builder 构建过程可指定的功能如下:
- addKeyboardStateListener,用于监听输入法状态,可获取输入法的可见性及高度
- addEditTextFocusChangeListener,监听指定的输入源焦点变化
- addViewClickListener,监听 trigger 及输入源 的点击,比如点击表情切换按钮,输入源点击等
- addPanelChangeListener,监听面板变化,包括输入法显示,面板显示,输入法高度变化引起面板高度变化回调,隐藏面板状态
- addContentScrollMeasurer,用于干预框架的滑动,比如 ContentContainer 内部的的子View 不跟随父布局一起滑动
- getScrollDistance 参数 defaultDistance 为框架默认距离,外部可自主返回其他距离
- getScrollViewId,要干预处理的子view的id
- addPanelHeightMeasurer,用于设置面板的默认高度,当框架未获取输入法高度时,优先读取设置的高度,如果不存在则使用框架内默认高度
- synchronizeKeyboardHeight 面板高度是否同步使用键盘高度,返回值 = false 时,使用getTargetPanelDefaultHeight的值作为面板高度
- getPanelTriggerId 对应面板的触发器id
- getTargetPanelDefaultHeight 触发面板的默认高度
- logTrack 是否输出 log 信息
- build,返回 PanelSwitchHelper 对象,可传递参数指定第一次是否自动显示输入法,默认不显示。
下图是通过 addContentScrollMeasurer 进行干预之后,软键盘/面板拉起后,内容区域内的多个 view 的行为
其中列表及右侧两个 View 都做了干预,左侧 View 没有做干预,见 Demo 类 ChatCusContentScrollActivity
除了上述构建过程中提供的功能,还提供以下重要方法使用:
- setContentScrollOutsideEnable 动态更改模式
- isContentScrollOutsizeEnable 获取模式
- toKeyboardState,切换成输入法面板
- toPanelState,切换成对应功能面板
- resetState,隐藏所有面板
- hookSystemBackByPanelSwitcher 拦截返回,如果当前用户按下返回或者业务返回键,则优先隐藏面板
- addSecondaryInputView/removeSecondaryInputView 添加额外输入源,用于驱动输入法显示
- setTriggerViewClickInterceptor 支持动态控制 TriggerView 是否自动响应"点击触发切换面板"行为,默认相应