处理前后对比
问题现状
安卓上面,输入框被软键盘遮挡,很简单
- xml 配置
- android:windowSoftInputMode="adjustPan"
- 或者,java 配置
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
这样,软键盘弹出后,输入框就会自动上移。
鸿蒙上也有类似的设置,但是貌似没效果:
- getWindow().setInputPanelDisplayType(WindowManager.LayoutConfig.INPUT_ADJUST_PAN);
解决过程
原理:
布局文件用ScrollView包起来
监听根布局大小变化,变小了,证明输入法弹出了。
滚动ScrollView,使当前焦点控件显示在软键盘上方。
核心代码:
- public class MainAbilitySlice extends AbilitySlice {
- private EventHandler mainHandler = new EventHandler(EventRunner.getMainEventRunner());
- private MyTask myTask = null;
- class MyTask implements Runnable {
- private final int softHeight;
- private final ScrollView root;
- private final Rect decorRect;
- public MyTask(int softHeight, ScrollView root, Rect decorRect) {
- this.softHeight = softHeight;
- this.root = root;
- this.decorRect = decorRect;
- }
- @Override
- public void run() {
- Timber.d("onRefreshed() called with: softHeight = [ %s ]", softHeight);
- Component focusView = root.findFocus();
- int focusTop = focusView.getLocationOnScreen()[1];//焦点控件的左上角
- root.fluentScrollByY(focusTop + focusView.getHeight() - decorRect.top - decorRect.getHeight() + 100);
- }
- }
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- getWindow().setInputPanelDisplayType(WindowManager.LayoutConfig.INPUT_ADJUST_PAN);
- super.setUIContent(ResourceTable.Layout_ability_main);
- Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(getContext());
- Point pt = new Point();
- display.get().getSize(pt);
- int screenHeight = pt.getPointYToInt();//不包括状态栏(手机时间、wifi显示的那一部分,) 2211,状态栏是129,加起来就是2340
- Timber.d("onRefreshed() called with: screenHeight = [ %s ]", screenHeight);
- ScrollView root = (ScrollView) findComponentById(ResourceTable.Id_root);
- root.setLayoutRefreshedListener(new Component.LayoutRefreshedListener() {
- @Override
- public void onRefreshed(Component component) {
- //包括标题栏,但不包括状态栏。默认 大小 (0,129,1080,2340),top=129即状态栏 , height=2211。 同android的decorView
- Rect decorRect = new Rect();
- component.getWindowVisibleRect(decorRect);
- Timber.d("onRefreshed() called with: rect = [ %s ]", decorRect);
- if (decorRect.getHeight() == 0) {
- //刚进入界面可能为0
- return;
- }
- int softHeight = screenHeight - decorRect.getHeight();
- Timber.d("onRefreshed() called with: softHeight = [ %s ]", softHeight);
- if (softHeight > 100) {//当输入法高度大于100判定为输入法打开了
- if (myTask != null) {
- mainHandler.removeTask(myTask);
- myTask = null;
- }
- mainHandler.postTask(myTask = new MyTask(softHeight, root, decorRect), 100);
- }
- }
- });
- }
- }
完整代码见文末
特别说明: 滚动操作为什么要delay 100毫秒?因为点击一个输入框Component.LayoutRefreshedListener有时会反复调用多次,而且间隔时间小于10毫秒,所以会造成滚动距离不准确。用postTask之后,每次调用的时候会把之前的task remove掉,以最新的一次为准。
计算滚动距离
其中上面的大红框是decorRect(即当前Ability可视区域),下面的大黑框是输入法显示区域。其中,软键盘弹出后,输入框被软键盘挡住,图中的小红框。
所以,要滚动的距离就是图中的C=A-B。
可以优化的点:
如果是Dialog中的输入框,当前的计算方法是否正确?
如果不用ScrollView,还有别的解决办法吗?
抽取出工具类或工具方法,代码复用。
文章相关附件可以点击下面的原文链接前往下载
原文链接:https://harmonyos.51cto.com/posts/4776