最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

天天快消息!Android 软键盘丝滑切换(一)

来源:博客园

在开发app中,软键盘弹出会有卡顿,闪一下的现象,会影响体验效果。为了实现微信软键盘与表情面板流畅的切换效果,查看了好多例子,查阅的很多资料,换了几种实现方式,都达不到流畅切换的效果,最终结合资料,参考网上的很多例子实现了想要的效果。(由于Demo中代码部分为Java代码,所以文中代码也由Kotlin与Java组成)Demo地址:https://github.com/xiaoyu00/KeyboardDemo


(资料图)

效果图

实现思路

在manifest android:windowSoftInputMode属性提供了系统自带的键盘弹出界面变化的几种方式,但都达不到流畅切换效果,更别说更复杂的微信键盘切换了,所以我们不用系统的方式,自己实现。首先要实时获取到软键盘弹出时的属性:是否弹出,动画执行过程,高度等;然后在界面加载完成后计算带表情面板布局的高度;最后围绕键盘弹出收回的过程中,在键盘属性变化时对整个布局做平移处理。

实现

一、键盘监听

键盘的变化监听是整个实现的核心,Android手机的键盘所有属性是直接获取不到的。监听键盘变化只能用其它方式,网上有几种方方法,我这选择的是根view Insets监听方式(实现OnApplyWindowInsetsListener接口,设置ViewCompat.setWindowInsetsAnimationCallback)。

1.自定义RootViewDeferringInsetsCallback继承WindowInsetsAnimationCompat.Callback实现OnApplyWindowInsetsListener接口

public class RootViewDeferringInsetsCallback extends WindowInsetsAnimationCompat.Callback implements OnApplyWindowInsetsListener {
private View view;  private WindowInsetsCompat lastWindowInsets;  private boolean deferredInsets = false;  private int persistentInsetTypes;  private int deferredInsetTypes;  private boolean isPadding = false;
...//构造方法
@Override  public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat windowInsets) {      view = v;      lastWindowInsets = windowInsets;      if (isPadding) {          int types;          if (deferredInsets) {              types = deferredInsetTypes;          } else {              types = persistentInsetTypes | deferredInsetTypes;          }          Insets typeInsets = windowInsets.getInsets(types);          v.setPadding(typeInsets.left, typeInsets.top, typeInsets.right, typeInsets.bottom);      }      return WindowInsetsCompat.CONSUMED;  }
@Override  public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {      if ((animation.getTypeMask() & deferredInsetTypes) != 0) {          deferredInsets = true;      }  }
@NonNull  @Override  public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List runningAnimations) {      return insets;  }
@Override  public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {      if (deferredInsets && (animation.getTypeMask() & deferredInsetTypes) != 0) {          deferredInsets = false;          if (lastWindowInsets != null && view != null) {              ViewCompat.dispatchApplyWindowInsets(view, lastWindowInsets);          }      }  }
}

2.定义KeyBoardListener接口

public interface KeyBoardListener {    void onAnimStart(int moveDistance);    void onAnimDoing(int offsetX,int offsetY);    void onAnimEnd();}

3.自定义KeyBoardInsetsCallBack继承RootViewDeferringInsetsCallback

public class KeyBoardInsetsCallBack extends RootViewDeferringInsetsCallback {    public static final int KEYBOARD_TYPE = WindowInsetsCompat.Type.ime();    public static final int SYSTEM_BAR_TYPE = WindowInsetsCompat.Type.systemBars();    private KeyBoardListener keyboardListener;    public KeyBoardInsetsCallBack(int dispatchMode, KeyBoardListener keyboardListener) {        super(dispatchMode);        this.keyboardListener = keyboardListener;    }    public KeyBoardInsetsCallBack(KeyBoardListener keyboardListener) {        this(DISPATCH_MODE_STOP, keyboardListener);    }    @NonNull    @Override    public WindowInsetsAnimationCompat.BoundsCompat onStart(@NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds) {        keyboardListener.onAnimStart(bounds.getUpperBound().bottom - bounds.getLowerBound().bottom);//计算键盘弹出高度        return super.onStart(animation, bounds);    }    @NonNull    @Override    public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List runningAnimations) {        Insets typesInset = insets.getInsets(KEYBOARD_TYPE);        Insets otherInset = insets.getInsets(SYSTEM_BAR_TYPE);        Insets subtract = Insets.subtract(typesInset, otherInset);        Insets diff = Insets.max(subtract, Insets.NONE);        keyboardListener.onAnimDoing(diff.left - diff.right, diff.top - diff.bottom);        return insets;    }    @Override    public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {        keyboardListener.onAnimEnd();    }}

4.最后在Activity中界面加载完成后添加键盘监听

val keyBoardInsetsCallBack =KeyBoardInsetsCallBack(object :                KeyBoardListener {                override fun onAnimStart(moveDistance: Int) {                    ...                }                override fun onAnimDoing(offsetX: Int, offsetY: Int) {                   ...                }                override fun onAnimEnd() {                   ...                }     }) ViewCompat.setWindowInsetsAnimationCallback(window.decorView, keyBoardInsetsCallBack)

这样就完成了键盘弹出收回过程的监听。

二、计算并设置View大小

在界面加载完成后计算View高度,contentLayout为根Layout

contentLayout.viewTreeObserver            .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {                override fun onGlobalLayout() {                    contentLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)                    calculationLayoutSize()                    initKeyBoardListener()                }            })
private fun calculationLayoutSize() {        val layoutParams = contentLayout.layoutParams as FrameLayout.LayoutParams        val layoutParams2 = listLayout.layoutParams as LinearLayout.LayoutParams        val cHeight: Int = contentLayout.height        PANEL_HEIGHT=(cHeight*0.45).toInt()//PANEL_HEIGHT为表情面板高度        layoutParams2.height = cHeight        listLayout.layoutParams = layoutParams2//listlayout为表情面板上面内容layout        layoutParams.height = cHeight + PANEL_HEIGHT        contentLayout.layoutParams = layoutParams    }

整个布局xml

          //滑动列表                ...             // 输入框                                                            

接下来在键盘弹出收起过程中对根布局做平移处理。

关键词: 切换效果 构造方法 所有属性