开源项目之 PanelSwitchHelper

用于做什么

在开发聊天页面时,开发者希望用户在输入法与功能面板(比如表情面板/更多选项面板等)切换过程中保持平滑过渡不闪烁。 参考了市场上主流的社交app效果及实现,综合互联网上的多种实现思路,最总整合成一个模版框架,该模版框架已经过测试使用。

效果展示

activity layout

实现方法

通过监听 Window 窗口变化来获取输入法高度并动态调整布局来达到平滑过渡切换面板。

界面布局

涉及的核心类有:

  • PanelSwitchLayout ,即黄色区域 ,仅能包含 PanelContainerPanelSwitchLayout 并实现一些辅助性功能。
  • ContentContainer ,即蓝色区域 ,用于存放显示内容 ,比如列表内容等 。 并存放可触发切换的布局,比如输入框表情按钮等 。
  • PanelContainer , 即绿色区域 , 仅用于存放可切换的面板 (PanelView),开发者自主定制 PanelView 面板。

以 activity_sample_layout.xml 为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<com.effective.android.panel.view.PanelSwitchLayout
android:id="@+id/panel_switch_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<!-- 内容区域 -->
<!-- edit_view 指定一个 EditText 用于输入 ,必须项-->
<!-- empty_view 指定用户点击该 ID 对应的 View 时实现面板或者输入法隐藏,非必须项 -->
<com.effective.android.panel.view.ContentContainer
android:id="@+id/content_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
app:edit_view="@id/edit_text"
app:empty_view="@id/empty_view">

<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ebebeb">

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<View
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shape_input_layout"
android:gravity="bottom"
android:minHeight="@dimen/dp_50"
android:orientation="horizontal"
android:paddingBottom="@dimen/dp_7.5"
android:paddingLeft="@dimen/dp_10"
android:paddingRight="@dimen/dp_10">

<!-- 更多入口 -->
<ImageView
android:id="@+id/add_btn"
android:layout_width="@dimen/dp_35"
android:layout_height="@dimen/dp_35"
android:layout_marginRight="@dimen/dp_10"
android:src="@drawable/icon_add" />

<!-- 输入入口 -->
<EditText
android:id="@+id/edit_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:layout_weight="1"
android:background="@drawable/selector_edit_focus"
android:maxLines="5"
android:minHeight="@dimen/dp_35"
android:paddingLeft="@dimen/dp_3"
android:paddingRight="@dimen/dp_3"
android:imeOptions="actionSearch"
android:paddingBottom="@dimen/dp_3"
android:paddingTop="@dimen/dp_7.5"
android:textCursorDrawable="@drawable/shape_edit_cursor"
android:textSize="@dimen/sp_16" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_35"
android:orientation="horizontal">

<!-- 表情入口 -->
<ImageView
android:id="@+id/emotion_btn"
android:layout_width="@dimen/dp_35"
android:layout_height="@dimen/dp_35"
android:layout_marginEnd="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:src="@drawable/selector_emotion_btn" />

<TextView
android:id="@+id/send"
android:layout_width="@dimen/dp_50"
android:layout_height="@dimen/dp_35"
android:background="@drawable/selector_send_btn"
android:gravity="center"
android:text="@string/send"
android:textColor="@color/color_send_btn"
android:textSize="@dimen/sp_15" />
</LinearLayout>

</LinearLayout>

</com.effective.android.panel.view.ContentContainer>


<!-- 面板区域,仅能包含PanelView-->
<com.effective.android.panel.view.PanelContainer
android:id="@+id/panel_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<!-- 每一项面板 -->
<!-- panel_layout 用于指定面板该 ID 对应的布局 ,必须项-->
<!-- panel_trigger 用于用户点击该 ID 对应的 View 时切换到该面板 -->
<!-- panel_toggle 用于当该面板显示时 ,用户再次点击 panel_trigger 对应的 View 时是否回切输入法-->
<com.effective.android.panel.view.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" />

<com.effective.android.panel.view.PanelView
android:id="@+id/panel_addition"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:panel_layout="@layout/panel_add_layout"
app:panel_trigger="@id/add_btn" />

</com.effective.android.panel.view.PanelContainer>
</com.effective.android.panel.view.PanelSwitchLayout>
</layout>

如何引用

  1. 在对应模块下 build.gradle 添加依赖。

    1
    implementation 'com.effective.android:panelSwitchHelper:1.0.0'
  2. 在 activity#onStart 方法中初始化 PanelSwitchHelper 对象,在 activity#onBackPressed hook 返回键 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private PanelSwitchHelper mHelper;

    @Override
    protected void onStart() {
    super.onStart();
    if (mHelper == null) {
    mHelper = new PanelSwitchHelper.Builder(this)
    .bindPanelSwitchLayout(R.id.panel_switch_layout) //绑定PanelSwitchLayout 对象
    .bindPanelContainerId(R.id.panel_container) //绑定ContentContainer 对象
    .bindContentContainerId(R.id.content_view) //绑定PanelContainer 对象
    .build();
    }
    }

    @Override
    public void onBackPressed() {
    if (mHelper != null && mHelper.hookSystemBackForHindPanel()) {
    return;
    }
    super.onBackPressed();
    }

期望

编写该项目只是希望能提高日常开发的效率,专注于处理业务 。如果更好的做法或者意见建议,欢迎写信到 yummyl.lau@gmail.com