Android仿滴答清单 自定义布局适配软键盘高度

1,791 阅读2分钟

效果图

记录仿写滴答清单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);
    }
}