【android开发】解决输入法与表情面板切换时的界面抖动问题

昨天琢磨了下android的输入法弹出模式,突然发现利用动态切换输入法的弹出模式可以解决输入法抖动的问题。具体是怎样的抖动呢?我们先看微博的反面教材。
微博的输入法与表情面板切换时抖动问题
【具体表现为:表情面板与输入法面板高度不一致,从而导致弹出输入法(layout被挤压)时,同时又需要隐藏表情面板(layout被拉升),最终让界面产生了高度差抖动,所以在切换时明显会有不大好的抖动体验)】

使用了解决抖动的解决方案后,效果如下:
解决了抖动后的输入法与表情面板切换效果
【这样的方案明显比微博的切换更平滑】

老样子,先说思路。主要我们要用到两个输入法弹出模式,分别是:adjustResize(调整模式) 、adjustNothing(不做任何调整) 。(更多介绍请参看我的上一篇文章:输入法弹出参数分析)

  1. 初始情况时(键盘和表情面板都未展开):我们为表情面板设置一个默认高度(因为我们还不知道键盘有多高)并将输入发弹出模式设置为adjustResize模式。
  2. 当我们点击了EditText时,系统将会弹出输入法,由于之前我们设置的模式为adjustResize,因此,输入法会挤压Layout,并且挤压的高度最终会固定到一个值(键盘的高度),当我们检测到挤压后,将这个挤压差值(也就是键盘高度)记录下来,作为表情面板的新高度值。于此同时,我们将表情面板隐藏。
  3. 当我们点击了表情按钮时,我们需要先判断输入法是否已展开。
    1)如果已经展开,那么我们的任务是将键盘平滑隐藏并显示表情面板。具体做法为:先将Activity的输入法弹出模式设置为adjustNothing,然后将上一步记录下来的键盘高度作为表情面板的高度,再将表情面板显示,此时由于键盘弹出模式为adjustNothing,所以键盘不会有任何抖动,并且由于表情面板与键盘等高,因此EditText也不会下移,最后将输入法隐藏。
    2)如果输入法未展开,我们再判断表情面板是否展开,如果展开了就隐藏并将输入法弹出模式归位为adjustResize,如果未展开就直接显示并将输入法弹出模式设置为adjustNothing。

大致的实现思路就是上面说到的,但是,既然都准备动手做帮助类了,就顺便将点击空白处折叠键盘和表情面板一起做了。具体实现思路为:在Activity的DecorView上面遮罩一层FrameLayout,用于监听触摸的Aciton_Down事件,如果在输入范围之外,则折叠表情面板和键盘。示意图如下:
点击空白处折叠表情面板和输入法原理事宜图
该说的说完了,开动。


1、创建InputMethodUtils类,构造方法需要传递Activity参数,并申明所需要的成员变量,并实现View.OnClickListener接口(因为我们要监听表情按钮的点击事件)。代码如下:

public class InputMethodUtils implements View.OnClickListener {
   
    // 键盘是否展开的标志位
    private boolean sIsKeyboardShowing;
    // 键盘高度变量
    private int sKeyBoardHeight = 0;
    // 绑定的Activity
    private Activity activity;
    /**
     * 构造函数
     * 
     * @param activity
     *            需要处理输入法的当前的Activity
     */
    public InputMethodUtils(Activity activity) {
        this.activity = activity;
        //DisplayUtils为屏幕尺寸工具类
        DisplayUtils.init(activity);
        // 默认键盘高度为267dp
        setKeyBoardHeight(DisplayUtils.dp2px(267));
    }
    @Override
    public void onClick(View v) {
    }
}

//DisplayUtils的实现代码为:

/**
 * 屏幕参数的辅助工具类。例如:获取屏幕高度,宽度,statusBar的高度,px和dp互相转换等
 * 【注意,使用之前一定要初始化!一次初始化就OK(建议APP启动时进行初始化)。 初始化代码 DisplayUtils.init(context)】
 * @author 蓝亭书序
 */
private static class DisplayUtils {
   
    // 四舍五入的偏移值
    private static final float ROUND_CEIL = 0.5f;
    // 屏幕矩阵对象
    private static DisplayMetrics sDisplayMetrics;
    // 资源对象(用于获取屏幕矩阵)
    private static Resources sResources;
    // 记录是否获取了键盘高度
    private boolean haskownKeyboardHeight = false;
    // statusBar的高度(由于这里获取statusBar的高度使用的反射,比较耗时,所以用变量记录)
    private static int statusBarHeight = -1;
    /**
     * 初始化操作
     * 
     * @param context
     *            context上下文对象
     */
    public static void init(Context context) {
        sDisplayMetrics = context.getResources().getDisplayMetrics();
        sResources = context.getResources();
    }

    /**
     * 获取屏幕高度 单位:像素
     * 
     * @return 屏幕高度
     */
    public static int getScreenHeight() {
        return sDisplayMetrics.heightPixels;
    }

    /**
     * 获取屏幕宽度 单位:像素
     * 
     * @return 屏幕宽度
     */
    public static float getDensity() {
        return sDisplayMetrics.density;
    }

    /**
     * dp 转 px
     * 
     * @param dp
     *            dp值
     * @return 转换后的像素值
     */
    public static int dp2px(int dp) {
        return (int) (dp * getDensity() + ROUND_CEIL);
    }

    /**
     * 获取状态栏高度
     * 
     * @return 状态栏高度
     */
    public static int getStatusBarHeight() {
        // 如果之前计算过,直接使用上次的计算结果
        if (statusBarHeight == -1) {
            final int defaultHeightInDp = 19;// statusBar默认19dp的高度
            statusBarHeight = DisplayUtils.dp2px(defaultHeightInDp);
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object obj = c.newInstance();
                Field field = c.getField("status_bar_height");
                statusBarHeight = sResources.getDimensionPixelSize(Integer
                        .parseInt(field.get(obj).toString()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}

【搬砖去了,等会继续写… … 】好了,继续写… …

2、在继续往下写之前,我们得考虑如何设计表情按钮、表情按钮点击事件、表情面板之间的问题。我的做法是创建一个ViewBinder内部类。(因为在逻辑上来说,这三个属于一体的)
ViewBinder的实现代码如下:

/**
 * 用于控制点击某个按钮显示或者隐藏“表情面板”的绑定bean对象。<br/>
 * 例如:我想点击“表情”按钮显示“表情面板”,我就可以这样做:<br/>
 * ViewBinder viewBinder = new ViewBinder(btn_emotion,emotionPanel);<br/>
 * 这样就创建出了一个ViewBinder对象<br/>
 * <font color='red'>【注意事项,使用此类时,千万不要使用trigger的setOnClickListener来监听事件(
 * 使用OnTriggerClickListener来代替),也不要使用setTag来设置Tag,否则会导致使用异常】</font>
 * @author 蓝亭书序
 */
public static class ViewBinder {
   
    private View trigger;//表情按钮对象
    private View panel;//表情面板对象
    //替代的监听器
    private OnTriggerClickListener listener;

    /**
     * 创建ViewBinder对象<br/>
     * 例如:我想点击“表情”按钮显示“表情面板”,我就可以这样做:<br/>
     * ViewBinder viewBinder = new
     * ViewBinder(btn_emotion,emotionPanel,listener);<br/>
     * 这样就创建出了一个ViewBinder对象
     * 
     * @param trigger
     *            触发对象
     * @param panel
     *            点击触发对象需要显示/隐藏的面板对象
     * @param listener
     *            Trigger点击的监听器(千万不要使用setOnClickListener,否则会覆盖本工具类的监听器)
     */
    public ViewBinder(View trigger, View panel,
            OnTriggerClickListener listener) {
        this.trigger = trigger;
        this.panel = panel;
        this.listener = listener;
        trigger.setClickable(true);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ViewBinder other = (ViewBinder) obj;
        if (panel == null) {
            if (other.panel != null)
                return false;
        } else if (!panel.equals(other.panel))
            return false;
        if (trigger == null) {
            if (other.trigger != null)
                return false;
        } else if (!trigger.equals(other.trigger))
            return false;
        return true;
    }
    public OnTriggerClickListener getListener() {
        return listener;
    }
    public void setListener(OnTriggerClickListener listener) {
        this.listener = listener;
    }
    public View getTrigger() {
        return trigger;
    }
    public void 
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android 输入开发是指为Android系统开发一款输入应用程序的过程。输入是一种用于输入文字的工具,通过输入,用户可以在Android设备上输入各种语言的文字。 Android 输入开发需要掌握一定的编程知识和技巧。首先,开发者需要了解Android系统的架构和输入的工作原理。其次,需要使用Java等编程语言来编写输入的代码。开发者还需要使用Android Studio等开发工具来调试和测试输入的功能。 在Android 输入开发过程中,需要考虑以下几个关键点。首先,输入需要正确地处理用户输入的字符,并将其显示在屏幕上。其次,输入需要提供联想和自动完成的功能,以帮助用户更快地输入文字。此外,输入还需要支持多种输入方式,例如手写输入、语音输入等。 同开发者还应该关注输入的用户体验。输入应该具有良好的界面设计和交互方式,方便用户使用。此外,输入还应该具备一定的智能化功能,例如根据用户的输入习惯进行个性化设置,提供更准确的输入建议等。 最后,输入开发完成后,开发者还需要将其发布到应用商店供用户下载和使用。在发布前,需要对输入进行充分的测试和迭代,确保其稳定性和功能完善性。 总的来说,Android 输入开发是一个复杂而有挑战的过程,但随着技术的不断发展和改进,越来越多的开发者参与到输入开发中,推动了输入的创新和提升。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值