{"id":15037268,"url":"https://github.com/dsappteam/panelswitchhelper","last_synced_at":"2025-05-14T04:09:05.950Z","repository":{"id":39221702,"uuid":"140263533","full_name":"DSAppTeam/PanelSwitchHelper","owner":"DSAppTeam","description":":heavy_check_mark:  A framework that helps the keyboard smoothly transition to the function panel    一个帮助键盘平稳过渡到功能面板的框架，支持动画无缝衔接，支持 activity/fragment/dialog/dialogFragment/popupWindow 容器，支持IM/直播/视频播放/信息流评论等场景，支持全屏模式。","archived":false,"fork":false,"pushed_at":"2025-03-26T09:12:53.000Z","size":109014,"stargazers_count":2402,"open_issues_count":17,"forks_count":285,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-04-10T22:35:10.386Z","etag":null,"topics":["keybord","keybordview","panel","switch"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DSAppTeam.png","metadata":{"files":{"readme":"README-zh.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-09T09:33:37.000Z","updated_at":"2025-04-08T11:02:17.000Z","dependencies_parsed_at":"2024-01-30T08:33:38.072Z","dependency_job_id":"5d013eb5-abaa-4c4b-8ebb-7f723d055efe","html_url":"https://github.com/DSAppTeam/PanelSwitchHelper","commit_stats":null,"previous_names":["yummylau/panelswitchhelper"],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FPanelSwitchHelper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FPanelSwitchHelper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FPanelSwitchHelper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FPanelSwitchHelper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DSAppTeam","download_url":"https://codeload.github.com/DSAppTeam/PanelSwitchHelper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254070041,"owners_count":22009559,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["keybord","keybordview","panel","switch"],"created_at":"2024-09-24T20:34:04.037Z","updated_at":"2025-05-14T04:09:00.933Z","avatar_url":"https://github.com/DSAppTeam.png","language":"Java","readme":"[![](https://travis-ci.org/YummyLau/PanelSwitchHelper.svg?branch=master)](https://travis-ci.org/YummyLau/panelSwitchHelper)\n![Language](https://img.shields.io/badge/language-java-orange.svg)\n![Language](https://img.shields.io/badge/language-kotlin-orange.svg)\n![Version](https://img.shields.io/badge/version-1.4.0-blue.svg)\n![Size](https://img.shields.io/badge/size-14K-brightgreen.svg)\n\nREADME: [English Doc](https://github.com/YummyLau/PanelSwitchHelper/blob/master/README.md)\n\n### 框架简介\n\n在开发聊天/视频/直播/信息流界面时，希望用户在输入法与功能面板（比如表情面板/更多选项面板等）的切换过程中保持平滑过渡。调研了市场上主流的app效果及实现，实现了一套兼容多场景的输入面板切换框架。目前该框架已测试使用。\n\n### 原理介绍\n\n* 方案1：在 setSoftInputMode = SOFT_INPUT_ADJUST_RESIZE 的场景下，通过ViewTreeObserver.OnGlobalLayoutListener或者ViewCompat.setOnApplyWindowInsetsListener来获取键盘高度，通过修改onLayout的方式调整输入面板的高度。\n* 方案2：在 setSoftInputMode = SOFT_INPUT_ADJUST_NOTHING 的场景下，通过ViewCompat.setWindowInsetsAnimationCallback监听键盘过渡动画，获取实时的键盘高度后，通过修改控件translationY的方式实现。\n\n其中方案2是在1.5.0版本中引入的，可以通过android11KeyboardFeature属性控制是否开启Android 11键盘特性（默认是开启的）。在部分应用场景中Android 11键盘特性无法生效，我们会降级成方案1的方式。\n\n备注：由于Android手机设备碎片化严重，可能会出现部分手机的兼容问题，我们为PanelSwitchLayout提供了两个兼容方法，当你的设备无法正常获取到键盘高度时，你可以尝试实现这两个方法来做兼容。\n\n```kotlin\n\n// 针对Android 11以上开启键盘动画特性，高度获取失败时，对外提供兼容方案\nvar softInputHeightCalculatorOnStart: ((animation: WindowInsetsAnimationCompat, bounds: WindowInsetsAnimationCompat.BoundsCompat) -\u003e Int)? = null\nvar softInputHeightCalculatorOnProgress: ((insets: WindowInsetsCompat, runningAnimations: MutableList\u003cWindowInsetsAnimationCompat\u003e) -\u003e Int)? = null\n\n\n```\n\n### 框架优势\n\n* 改进传统使用 `Weight+LinearLayout` 动态更改布局高度适配面板的技术方案，支持多种原生 ViewGroup 容器\n* 为了追求更平滑的适配效果，当输入法动态调整高度或动态隐藏导航栏时，功能面板能实时适配\n* 为了追求更流畅的切换效果，支持滑动模式，滑动会更流畅，同时也支持固定模式\n* 丰富的机型适配，适配 全面屏/刘海屏/挖孔屏/Pad 等非常规 Phone 机型\n* 丰富的场景支持，支持 Activity/Fragment/Dialog/PopupWindow，应用到聊天/视频/直播/信息流评论等场景\n* 丰富的 API 支持，可自定义内容容器，业务面板，灵活控制面板隐藏，灵活控制切换面板速度\n* 支持全屏模式，FullScreen 模式下也能处理面板切换\n\n更多细节可参考\n\n* [场景使用介绍](https://github.com/YummyLau/PanelSwitchHelper/blob/master/README_SENCE-zh.md)\n* [API 使用指南](https://github.com/YummyLau/PanelSwitchHelper/blob/master/README_API-zh.md)\n* [库版本更新日志](https://github.com/YummyLau/PanelSwitchHelper/blob/master/README_UPDATE-zh.md)\n\nDemo 内容如下\n\n\u003cimg src=\"https://github.com/YummyLau/PanelSwitchHelper/blob/master/source/demo.png\" width = \"360\" height = \"790\"/\u003e\n\n从二维码下载 Demo\n\n\u003cimg src=\"https://github.com/YummyLau/PanelSwitchHelper/blob/master/source/qr_code_apk.png\" width = \"256\" height = \"256\"/\u003e\n\n默认运行 Androidx 版本，如果需要打开非 Androidx，则在 `Settings.gradle` 中打开 `app` 并在 `gradle.properties` 中关闭 Androidx 配置即可。\n\n### 使用方法\n\n1. 在模块脚本 `build.gradle` 添加库依赖\n\n1.1、Add it in your root build.gradle at the end of repositories:\n\n```groovy\n\nallprojects {\n    repositories {\n        maven { url 'https://jitpack.io' }\n    }\n}\n```\n\n1.2、Add the dependency\n```groovy\n//1.4.0 版本及后续，仅支持 Androidx\ndependencies {\n    implementation 'com.github.DSAppTeam:PanelSwitchHelper:v1.5.12'\n}\n```\n\n2. 在布局文件 Xml 中使用框架提供的容器\n\n```\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003clayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\u003e\n\n    \u003cLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\u003e\n\n\t\t \u003c!-- 不需要被框架处理的布局，可自由布置 --\u003e\n        \u003cRelativeLayout\n            android:id=\"@+id/cus_title_bar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"50dp\"\n            android:background=\"@color/colorPrimary\"\n            android:visibility=\"gone\"\u003e\n\n            \u003cTextView\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:gravity=\"left|center_vertical\"\n                android:paddingLeft=\"20dp\"\n                android:text=\"自定义标题栏\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"20sp\"\n                android:textStyle=\"bold\" /\u003e\n        \u003c/RelativeLayout\u003e\n\n        \u003ccom.effective.android.panel.view.PanelSwitchLayout\n            android:id=\"@+id/panel_switch_layout\"\n            android:layout_width=\"match_parent\"\n            app:animationSpeed=\"standard\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\"\u003e\n\n            \u003c!-- 内容区域 --\u003e\n            \u003c!-- linear_edit_view 指定一个 EditText 用于输入 ，必须项--\u003e\n            \u003c!-- linear_auto_reset_enable 指定是否 LinearContentContainer 是否能够接受 Touch 事件自动隐藏面板--\u003e\n            \u003c!-- linear_auto_reset_area 指定是否 LinearContentContainer 只接受某个 View 区域的 Touch 事件来自定隐藏面板--\u003e\n            \u003ccom.effective.android.panel.view.content.LinearContentContainer\n                android:id=\"@+id/content_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\"\n                app:edit_view=\"@id/edit_text\"\u003e\n\n                \u003ccom.example.demo.scene.chat.view.HookActionUpRecyclerView\n                    android:id=\"@+id/recycler_view\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:layout_weight=\"1\"/\u003e\n\n                \u003cLinearLayout\n                    android:id=\"@+id/bottom_action\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@drawable/shape_input_layout\"\n                    android:gravity=\"bottom\"\n                    android:minHeight=\"@dimen/dp_50\"\n                    android:orientation=\"horizontal\"\n                    android:paddingLeft=\"@dimen/dp_10\"\n                    android:paddingRight=\"@dimen/dp_10\"\n                    android:paddingBottom=\"@dimen/dp_7.5\"\u003e\n\n                    \u003c!-- 更多入口 --\u003e\n                    \u003cImageView\n                        android:id=\"@+id/add_btn\"\n                        android:layout_width=\"@dimen/dp_35\"\n                        android:layout_height=\"@dimen/dp_35\"\n                        android:layout_marginRight=\"@dimen/dp_10\"\n                        android:src=\"@drawable/icon_add\" /\u003e\n\n                    \u003c!-- 输入入口 --\u003e\n                    \u003cEditText\n                        android:id=\"@+id/edit_text\"\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginEnd=\"@dimen/dp_10\"\n                        android:layout_marginRight=\"@dimen/dp_10\"\n                        android:layout_weight=\"1\"\n                        android:background=\"@drawable/selector_edit_focus\"\n                        android:imeOptions=\"actionSearch\"\n                        android:maxLines=\"5\"\n                        android:minHeight=\"@dimen/dp_35\"\n                        android:paddingLeft=\"@dimen/dp_3\"\n                        android:paddingTop=\"@dimen/dp_7.5\"\n                        android:paddingRight=\"@dimen/dp_3\"\n                        android:paddingBottom=\"@dimen/dp_3\"\n                        android:textCursorDrawable=\"@drawable/shape_edit_cursor\"\n                        android:textSize=\"@dimen/sp_16\" /\u003e\n\n                    \u003cLinearLayout\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"@dimen/dp_35\"\n                        android:orientation=\"horizontal\"\u003e\n\n                        \u003c!-- 表情入口 --\u003e\n                        \u003cImageView\n                            android:id=\"@+id/emotion_btn\"\n                            android:layout_width=\"@dimen/dp_35\"\n                            android:layout_height=\"@dimen/dp_35\"\n                            android:layout_marginEnd=\"@dimen/dp_10\"\n                            android:layout_marginRight=\"@dimen/dp_10\"\n                            android:src=\"@drawable/selector_emotion_btn\" /\u003e\n\n                        \u003cTextView\n                            android:id=\"@+id/send\"\n                            android:layout_width=\"@dimen/dp_50\"\n                            android:layout_height=\"@dimen/dp_35\"\n                            android:background=\"@drawable/selector_send_btn\"\n                            android:gravity=\"center\"\n                            android:text=\"@string/send\"\n                            android:textColor=\"@color/color_send_btn\"\n                            android:textSize=\"@dimen/sp_15\" /\u003e\n                    \u003c/LinearLayout\u003e\n\n                \u003c/LinearLayout\u003e\n\n            \u003c/com.effective.android.panel.view.content.LinearContentContainer\u003e\n\n\n            \u003c!-- 面板区域，仅能包含PanelView--\u003e\n            \u003ccom.effective.android.panel.view.panel.PanelContainer\n                android:id=\"@+id/panel_container\"\n                android:layout_width=\"match_parent\"\n                android:background=\"@color/common_page_bg_color\"\n                android:layout_height=\"wrap_content\"\u003e\n\n                \u003c!-- 每一项面板 --\u003e\n                \u003c!-- panel_layout 用于指定面板该 ID 对应的布局 ，必须项--\u003e\n                \u003c!-- panel_trigger 用于用户点击该 ID 对应的 View 时切换到该面板 --\u003e\n                \u003c!-- panel_toggle  用于当该面板显示时 ，用户再次点击 panel_trigger 对应的 View 时是否回切输入法--\u003e\n                \u003ccom.effective.android.panel.view.panel.PanelView\n                    android:id=\"@+id/panel_emotion\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    app:panel_layout=\"@layout/panel_emotion_layout\"\n                    app:panel_trigger=\"@id/emotion_btn\" /\u003e\n\n                \u003c!-- 除了使用框架提供的 PanelView，也可以使用自定义 Panel --\u003e\n                \u003ccom.example.demo.scene.api.CusPanelView\n                    android:id=\"@+id/panel_addition\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    app:cus_panel_trigger=\"@id/add_btn\"\n                    app:cus_panel_toggle=\"true\"/\u003e\n\n            \u003c/com.effective.android.panel.view.panel.PanelContainer\u003e\n        \u003c/com.effective.android.panel.view.PanelSwitchLayout\u003e\n\n    \u003c/LinearLayout\u003e\n\u003c/layout\u003e\n```\n\n3. 初始化 PanelSwitchHelper 对象，框架会自动收集布局信息。同时在返回键会调时拦截处理即可。\n\n```\n\t//Activity 场景，在 onStart 方法初始化，其他如 Fragment/Dialog/PopupWindow 参考 Demo\n   private PanelSwitchHelper mHelper;\n\n   @Override\n   protected void onStart() {\n        super.onStart();\n        if (mHelper == null) {\n            mHelper = new PanelSwitchHelper.Builder(this)\n                   .addKeyboardStateListener {\n                        onKeyboardChange {\n                            //可选实现，监听输入法变化\n                        }\n                    }\n                    .addEditTextFocusChangeListener {\n                        onFocusChange { _, hasFocus -\u003e\n\t\t\t\t\t\t\t\t //可选实现，监听输入框焦点变化\n                        }\n                    }\n                    .addViewClickListener {\n                        onClickBefore {\n \t\t\t\t\t\t\t\t//可选实现，监听触发器的点击\n                        }\n                    }\n                    .addPanelChangeListener {\n                        onKeyboard {\n \t\t\t\t\t\t\t\t//可选实现，输入法显示回调\n                        }\n                        onNone {\n \t\t\t\t\t\t\t\t//可选实现，默认状态回调\n                        }\n                        onPanel {\n \t\t\t\t\t\t\t\t//可选实现，面板显示回调\n                        }\n                        onPanelSizeChange { panelView, _, _, _, width, height -\u003e\n \t\t\t\t\t\t\t\t//可选实现，输入法动态调整时引起的面板高度变化动态回调\n                        }\n                    }\n                    .addContentScrollMeasurer { //可选，滑动模式下，可以针对内容面板内的view，定制滑动距离，默认滑动距离为 defaultDistance\n                        getScrollDistance { defaultDistance -\u003e defaultDistance - 200 }\n                        getScrollViewId { R.id.recycler_view }\n                    }\n                    .addPanelHeightMeasurer {   //可选 用于设置未获取输入法高度前面板的高度，如果不设置则默认以框架内高度为主\n                        synchronizeKeyboardHeight { false } // 设置面板高度是否和键盘高度同步\n                        getTargetPanelDefaultHeight { DisplayUtils.dip2px(this@DefaultHeightPanelActivity,400f)}\n                        getPanelTriggerId { R.id.add_btn }\n                    }   \n                    .contentScrollOutsideEnable(true)  //可选，默认为true    \n                    .logTrack(true)                   //可选，默认false，是否开启log信息输出\n                    .build(true)\t\t\t          //可选，默认false，是否默认打开输入法\n        }\n    }\n\n\n   @Override\n   public void onBackPressed() {\n   \t\t //用户按下返回键的时候，如果显示面板，则需要隐藏\n        if (mHelper != null \u0026\u0026 mHelper.hookSystemBackForHindPanel()) {\n            return;\n        }\n        super.onBackPressed();\n   }\n\n```\n\n### 期望\n\n编写该项目只是希望能提高日常开发的效率，专注于处理业务。如果更好的做法或者意见建议，欢迎写信到 yummyl.lau@gmail.com 。\n也可以微信“lym_llllll” 添加微信进群反馈。\n\n如果框架对你有帮助，可安利给身边的伙伴，每一个 star 都是对框架付出的肯定。\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsappteam%2Fpanelswitchhelper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdsappteam%2Fpanelswitchhelper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsappteam%2Fpanelswitchhelper/lists"}