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.botto            m);      
      }      
    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<WindowInsetsAnimationCompat> 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);          
            }      
        }  
    }
}
  1. 定义KeyBoardListener接口

public interface KeyBoardListener {
    void onAnimStart(int moveDistance);
    void onAnimDoing(int offsetX,int offsetY);
    void onAnimEnd();
}
  1. 自定义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<WindowInsetsAnimationCompat> 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();
    }
}
  1. 最后在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()
                    initListener()
                }
            })
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()
        layoutParams2.height = cHeight
        listLayout.layoutParams = layoutParams2
        layoutParams.height = cHeight + PANEL_HEIGHT
        contentLayout.layoutParams = layoutParams
 }

整个布局xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/layout_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        //滑动列表
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="Hello World!">
            ...
        </ScrollView>
        // 输入框
        <LinearLayout
            android:id="@+id/layout_edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingVertical="6dp"
            android:paddingHorizontal="16dp">

            <EditText
                android:id="@+id/et_input"
                android:layout_width="0dp"
                android:layout_height="36dp"
                android:layout_weight="1"
                android:background="@drawable/msg_editor_border" />

            <ImageView
                android:id="@+id/face_btn"
                android:layout_width="26.88dp"
                android:layout_height="26.88dp"
                android:layout_margin="5dp"
                android:scaleType="fitXY"
                android:src="@drawable/action_face_selector" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/view_panel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="这是表情面板" />
    </LinearLayout>

</LinearLayout>

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyu1273537654

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值