效果图

记录仿写滴答清单App 过程中的技术点
本文记录自定义布局对软键盘的响应,主要分为以下章节,读者可按需阅读:
- 1.windowSoftInputMode基础配置
- 2.软键盘监听
- 3.自定义布局适配软键盘高度
一、windowSoftInputMode基础介绍
adjustResize与adjustPan属性的区别
adjustPan:使当前的焦点不会被软键盘挡住,用户可以看到他们输入的内容,但不可以滑动,哪怕该界面是可以滑动。
adjustResize:总是调整视窗大小腾出空间让软键盘可以显示,但可以滑动使其显示(如果该界面是可滑动的)。
这里我选择设置adjustResize,不让其他布局被顶出界面。
android:windowSoftInputMode="adjustResize"
二、软键盘监听
监听软键盘的打开和关闭,这个类是通过软键盘打开和关闭时,界面显示的变化来判断。并且可以获取到软键盘打开后的高度差heightDifference
public class KeyboardStateObserver {
private static final String TAG = KeyboardStateObserver.class.getSimpleName();
public static KeyboardStateObserver getKeyboardStateObserver(Activity activity) {
return new KeyboardStateObserver(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private OnKeyboardVisibilityListener listener;
public void setKeyboardVisibilityListener(OnKeyboardVisibilityListener listener) {
this.listener = listener;
}
private KeyboardStateObserver(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
if (listener != null) {
listener.onKeyboardShow(heightDifference);
}
} else {
if (listener != null) {
listener.onKeyboardHide();
}
}
usableHeightPrevious = usableHeightNow;
LogUtils.d(TAG,"usableHeightNow: " + usableHeightNow + " | usableHeightSansKeyboard:" +
usableHeightSansKeyboard + " | heightDifference:" + heightDifference);
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
LogUtils.d(TAG,"rec bottom>" + r.bottom + " | rec top>" + r.top);
return (r.bottom - r.top);// 全屏模式下: return r.bottom
}
public interface OnKeyboardVisibilityListener {
void onKeyboardShow(int heightDifference);
void onKeyboardHide();
}
}
三、自定义布局适配软键盘高度
键盘监听器的使用
KeyboardStateObserver.getKeyboardStateObserver(this).
setKeyboardVisibilityListener(new KeyboardStateObserver.OnKeyboardVisibilityListener() {
@Override
public void onKeyboardShow(int heightDifference) {
setEtQuickTodo(ScreenUtils.getScreenHeight() - BarUtils.getStatusBarHeight() -
heightDifference - etQuickTodo.getHeight());
}
@Override
public void onKeyboardHide() {
setEtQuickTodo(ScreenUtils.getScreenHeight() - BarUtils.getStatusBarHeight() - lineQuick.getHeight());
}
});
setCusView方法设置自定义布局的位置和大小,mCusView是仿滴答清单的自定义布局
private void setEtQuickTodo(int dpTop) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
lineQuick.getHeight());
layoutParams.setMargins(0, dpTop, 0, 0);
lineQuick.setLayoutParams(layoutParams);
}
自定义xml代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:fillViewport="true"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@color/white"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_arrow_back_black_24dp"
app:popupTheme="@style/popup_theme"
app:theme="@style/toolbar_theme"
app:titleTextAppearance="@style/Toolbar_TextAppearance_White"
app:titleTextColor="@color/black" />
<LinearLayout
android:id="@+id/line_quick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<EditText
android:id="@+id/et_quick_todo"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="8dp"
android:hint="@string/ReadyTodo"
android:paddingTop="8dp"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/shape_quick_et_bg" />
<RelativeLayout
android:id="@+id/relative_btn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorBackgroundDark">
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
完整代码
public class QuickToDoActivity extends BaseActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.et_quick_todo)
EditText etQuickTodo;
@BindView(R.id.line_quick)
LinearLayout lineQuick;
@BindView(R.id.relative_btn)
RelativeLayout relativeBtn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Objects.requireNonNull(getWindow()).addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
Objects.requireNonNull(getWindow()).setStatusBarColor(getResources().getColor(R.color.white));
}
super.onCreate(savedInstanceState);
setContentView(R.layout.pop_quick_todo);
ButterKnife.bind(this);
toolbar.setPadding(0, BarUtils.getStatusBarHeight(), 0, 0);
KeyboardStateObserver.getKeyboardStateObserver(this).
setKeyboardVisibilityListener(new KeyboardStateObserver.OnKeyboardVisibilityListener() {
@Override
public void onKeyboardShow(int heightDifference) {
setEtQuickTodo(ScreenUtils.getScreenHeight() - BarUtils.getStatusBarHeight() -
heightDifference - etQuickTodo.getHeight());
}
@Override
public void onKeyboardHide() {
setEtQuickTodo(ScreenUtils.getScreenHeight() - BarUtils.getStatusBarHeight() - lineQuick.getHeight());
}
});
}
@Override
protected void onResume() {
super.onResume();
KeyboardUtils.showSoftInput(lineQuick);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
out();
}
return super.onKeyDown(keyCode, event);
}
private void setEtQuickTodo(int dpTop) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
lineQuick.getHeight());
layoutParams.setMargins(0, dpTop, 0, 0);
lineQuick.setLayoutParams(layoutParams);
}
private void out() {
finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
}