文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android输入法与表情面板切换时的界面抖动问题解决方法

2022-06-06 05:28

关注

昨天琢磨了下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;
  
  public InputMethodUtils(Activity activity) {
    this.activity = activity;
    //DisplayUtils为屏幕尺寸工具类
    DisplayUtils.init(activity);
    // 默认键盘高度为267dp
    setKeyBoardHeight(DisplayUtils.dp2px(267));
  }
  @Override
  public void onClick(View v) {
  }
}
//DisplayUtils的实现代码为:

private static class DisplayUtils {
  // 四舍五入的偏移值
  private static final float ROUND_CEIL = 0.5f;
  // 屏幕矩阵对象
  private static DisplayMetrics sDisplayMetrics;
  // 资源对象(用于获取屏幕矩阵)
  private static Resources sResources;
  // statusBar的高度(由于这里获取statusBar的高度使用的反射,比较耗时,所以用变量记录)
  private static int statusBarHeight = -1;
  
  public static void init(Context context) {
    sDisplayMetrics = context.getResources().getDisplayMetrics();
    sResources = context.getResources();
  }
  
  public static int getScreenHeight() {
    return sDisplayMetrics.heightPixels;
  }
  
  public static float getDensity() {
    return sDisplayMetrics.density;
  }
  
  public static int dp2px(int dp) {
    return (int) (dp * getDensity() + ROUND_CEIL);
  }
  
  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的实现代码如下:



public static class ViewBinder {
  private View trigger;//表情按钮对象
  private View panel;//表情面板对象
  //替代的监听器
  private OnTriggerClickListener listener;
  
  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 setTrigger(View trigger) {
    this.trigger = trigger;
  }
  public View getPanel() {
    return panel;
  }
  public void setPanel(View panel) {
    this.panel = panel;
  }
}

其中OnTriggerClickListener是为了解决trigger占用监听器的问题(我们内部逻辑需要占用监听器,如果外部想实现额外的点击逻辑不能再为trigger添加监听器,所以使用OnTriggerClickListener来代替原原声的OnClickListener)。OnTriggerClickListener为一个接口,实现代码如下:



public static interface OnTriggerClickListener {
  
  public void onClick(View v);
}

3、实现了ViewBinder后,我们还需要实现一个遮罩View,用于监听ACTION_DOWN事件。代码如下:



private class CloseKeyboardOnOutsideContainer extends FrameLayout {
  public CloseKeyboardOnOutsideContainer(Context context) {
    this(context, null);
  }
  public CloseKeyboardOnOutsideContainer(Context context,
      AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public CloseKeyboardOnOutsideContainer(Context context,
      AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }
  
  @Override
  public boolean dispatchTouchEvent(MotionEvent event) {
  //这段逻辑不复杂,看一遍应该就懂
    boolean isKeyboardShowing = isKeyboardShowing();
    boolean isEmotionPanelShowing = hasPanelShowing();
    if ((isKeyboardShowing || isEmotionPanelShowing)
        && event.getAction() == MotionEvent.ACTION_DOWN) {
      int touchY = (int) (event.getY());
      int touchX = (int) (event.getX());
      if (isTouchKeyboardOutside(touchY)) {
        if (isKeyboardShowing) {
          hideKeyBordAndSetFlag(activity.getCurrentFocus());
        }
        if (isEmotionPanelShowing) {
          closeAllPanels();
        }
      }
      if (isTouchedFoucusView(touchX, touchY)) {
        // 如果点击的是输入框(会弹出输入框),那么延时折叠表情面板
        postDelayed(new Runnable() {
          @Override
          public void run() {
            setKeyboardShowing(true);
          }
        }, 500);
      }
    }
    return super.onTouchEvent(event);
  }
}

private boolean isTouchKeyboardOutside(int touchY) {
  View foucusView = activity.getCurrentFocus();
  if (foucusView == null) {
    return false;
  }
  int[] location = new int[2];
  foucusView.getLocationOnScreen(location);
  int editY = location[1] - DisplayUtils.getStatusBarHeight();
  int offset = touchY - editY;
  if (offset > 0 && offset < foucusView.getMeasuredHeight()) {
    return false;
  }
  return true;
}

private boolean isTouchedFoucusView(int x, int y) {
  View foucusView = activity.getCurrentFocus();
  if (foucusView == null) {
    return false;
  }
  int[] location = new int[2];
  foucusView.getLocationOnScreen(location);
  int foucusViewTop = location[1] - DisplayUtils.getStatusBarHeight();
  int offsetY = y - foucusViewTop;
  if (offsetY > 0 && offsetY < foucusView.getMeasuredHeight()) {
    int foucusViewLeft = location[0];
    int foucusViewLength = foucusView.getWidth();
    int offsetX = x - foucusViewLeft;
    if (offsetX >= 0 && offsetX <= foucusViewLength) {
      return true;
    }
  }
  return false;
}

4、准备工作做完,我们可以继续完善InputMethodUtils类了,由于我们需要存储ViewBinder对象(主要用于控制按钮和面板之间的关联关系),所以,我们还需要在InputMethodUtils中申明一个集合。代码如下:


// 触发与面板对象集合(使用set可以自动过滤相同的ViewBinder)
private Set<ViewBinder> viewBinders = new HashSet<ViewBinder>();

5、与viewBinders 随之而来的一些常用方法有必要写一下(例如折叠所有表情面板、获取当前哪个表情面板展开着等),代码如下:



public void setViewBinders(ViewBinder... viewBinder) {
  for (ViewBinder vBinder : viewBinder) {
    if (vBinder != null) {
      viewBinders.add(vBinder);
      vBinder.trigger.setTag(vBinder);
      vBinder.trigger.setOnClickListener(this);
    }
  }
  updateAllPanelHeight(sKeyBoardHeight);
}

private void resetOtherPanels(View dstPanel) {
  for (ViewBinder vBinder : viewBinders) {
    if (dstPanel != vBinder.panel) {
      vBinder.panel.setVisibility(View.GONE);
    }
  }
}

public void closeAllPanels() {
  resetOtherPanels(null);
  //重置面板后,需要将输入法弹出模式一并重置
  updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}

public boolean hasPanelShowing() {
  for (ViewBinder viewBinder : viewBinders) {
    if (viewBinder.panel.isShown()) {
      return true;
    }
  }
  return false;
}

private void updateAllPanelHeight(int height) {
  for (ViewBinder vBinder : viewBinders) {
    ViewGroup.LayoutParams params = vBinder.panel.getLayoutParams();
    params.height = height;
    vBinder.panel.setLayoutParams(params);
  }
}

6、通过监听Layout的变化来判断输入法是否已经展开。代码如下:



private void detectKeyboard() {
  final View activityRootView = ((ViewGroup) activity
      .findViewById(android.R.id.content)).getChildAt(0);
  if (activityRootView != null) {
    ViewTreeObserver observer = activityRootView.getViewTreeObserver();
    if (observer == null) {
      return;
    }
    observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        final Rect r = new Rect();
        activityRootView.getWindowVisibleDisplayFrame(r);
        int heightDiff = DisplayUtils.getScreenHeight()
            - (r.bottom - r.top);
        //Layout形变超过键盘的一半表示键盘已经展开了
        boolean show = heightDiff >= sKeyBoardHeight / 2;
        setKeyboardShowing(show);// 设置键盘是否展开状态
        if (show) {
          int keyboardHeight = heightDiff
              - DisplayUtils.getStatusBarHeight();
          // 设置新的键盘高度
          setKeyBoardHeight(keyboardHeight);
        }
      }
    });
  }
}

7、完成键盘的显示/隐藏和动态控制输入法弹出模式的常用方法。代码如下:



public static void hideKeyboard(View currentFocusView) {
  if (currentFocusView != null) {
    IBinder token = currentFocusView.getWindowToken();
    if (token != null) {
      InputMethodManager im = (InputMethodManager) currentFocusView
          .getContext().getSystemService(
              Context.INPUT_METHOD_SERVICE);
      im.hideSoftInputFromWindow(token, 0);
    }
  }
}

public static void updateSoftInputMethod(Activity activity,
    int softInputMode) {
  if (!activity.isFinishing()) {
    WindowManager.LayoutParams params = activity.getWindow()
        .getAttributes();
    if (params.softInputMode != softInputMode) {
      params.softInputMode = softInputMode;
      activity.getWindow().setAttributes(params);
    }
  }
}

public void updateSoftInputMethod(int softInputMode) {
  updateSoftInputMethod(activity, softInputMode);
}

8、在构造方法中将这些组件都初始化,并做相关设置,代码如下:



public InputMethodUtils(Activity activity) {
  this.activity = activity;
  DisplayUtils.init(activity);
  // 默认键盘高度为267dp
  setKeyBoardHeight(DisplayUtils.dp2px(267));
  updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
  detectKeyboard();// 监听View树变化,以便监听键盘是否弹出
  enableCloseKeyboardOnTouchOutside(activity);
}

private void setKeyBoardHeight(int keyBoardHeight) {
  sKeyBoardHeight = keyBoardHeight;
  updateAllPanelHeight(keyBoardHeight);
}

private void enableCloseKeyboardOnTouchOutside(Activity activity) {
  CloseKeyboardOnOutsideContainer frameLayout = new CloseKeyboardOnOutsideContainer(
      activity);
  activity.addContentView(frameLayout, new ViewGroup.LayoutParams(
      ViewGroup.LayoutParams.MATCH_PARENT,
      ViewGroup.LayoutParams.MATCH_PARENT));
}

【突然有事,先写到这,等会来完善…】回来了,接着写。
上面的代码基本完成需求,需要重点说的是如何检测键盘弹出/隐藏状态的问题(有人可能会说用InputMethodManager.isActive()啊,恩…反正我用有这个方法问题,他永远都给我返回true),下面简单介绍下如何实现的键盘的弹出和隐藏状态的检测。

1、如果当前输入法是adjustResize模式,那么我们直接可以用Layout的形变监听即可实现,也就是之前detectKeyboard()实现的代码。

2、如果当前输入法是adjustNoting模式,这个就有点难处理了,因为没有形变可以监听。我的实现方式是:通过遮罩View判断ACTION_DOWN的坐标,如果该坐标落在输入框内(就是用户点击了输入框,此时系统将会弹出输入框),那么我们就可以认为键盘为弹出模式。代码体现在CloseKeyboardOnOutsideContainer的dispatchTouchEvent()方法中。

到此,开发就告一段落了。按照惯例,完整代码如下:



public class InputMethodUtils implements View.OnClickListener {
  // 键盘是否展开的标志位
  private boolean sIsKeyboardShowing;
  // 键盘高度
  private int sKeyBoardHeight = 0;
  // 绑定的Activity
  private Activity activity;
  // 触发与面板对象集合
  private Set<ViewBinder> viewBinders = new HashSet<ViewBinder>();
  
  public InputMethodUtils(Activity activity) {
    this.activity = activity;
    DisplayUtils.init(activity);
    // 默认键盘高度为267dp
    setKeyBoardHeight(DisplayUtils.dp2px(267));
    updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    detectKeyboard();// 监听View树变化,以便监听键盘是否弹出
    enableCloseKeyboardOnTouchOutside(activity);
  }
  
  public void setViewBinders(ViewBinder... viewBinder) {
    for (ViewBinder vBinder : viewBinder) {
      if (vBinder != null) {
        viewBinders.add(vBinder);
        vBinder.trigger.setTag(vBinder);
        vBinder.trigger.setOnClickListener(this);
      }
    }
    updateAllPanelHeight(sKeyBoardHeight);
  }
  @Override
  public void onClick(View v) {
    ViewBinder viewBinder = (ViewBinder) v.getTag();
    View panel = viewBinder.panel;
    resetOtherPanels(panel);// 重置所有面板
    if (isKeyboardShowing()) {
      updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
      panel.setVisibility(View.VISIBLE);
      hideKeyBordAndSetFlag(activity.getCurrentFocus());
    } else if (panel.isShown()) {
      updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
      panel.setVisibility(View.GONE);
    } else {
      updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
      panel.setVisibility(View.VISIBLE);
    }
    if (viewBinder.listener != null) {
      viewBinder.listener.onClick(v);
    }
  }
  
  public boolean isKeyboardShowing() {
    return sIsKeyboardShowing;
  }
  
  public int getKeyBoardHeight() {
    return sKeyBoardHeight;
  }
  
  public void closeAllPanels() {
    resetOtherPanels(null);
    updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
  }
  
  public boolean hasPanelShowing() {
    for (ViewBinder viewBinder : viewBinders) {
      if (viewBinder.panel.isShown()) {
        return true;
      }
    }
    return false;
  }
  
  public void updateSoftInputMethod(int softInputMode) {
    updateSoftInputMethod(activity, softInputMode);
  }
  
  public static void hideKeyboard(View currentFocusView) {
    if (currentFocusView != null) {
      IBinder token = currentFocusView.getWindowToken();
      if (token != null) {
        InputMethodManager im = (InputMethodManager) currentFocusView
            .getContext().getSystemService(
                Context.INPUT_METHOD_SERVICE);
        im.hideSoftInputFromWindow(token, 0);
      }
    }
  }
  
  public static void updateSoftInputMethod(Activity activity,
      int softInputMode) {
    if (!activity.isFinishing()) {
      WindowManager.LayoutParams params = activity.getWindow()
          .getAttributes();
      if (params.softInputMode != softInputMode) {
        params.softInputMode = softInputMode;
        activity.getWindow().setAttributes(params);
      }
    }
  }
  
  private void hideKeyBordAndSetFlag(View currentFocusView) {
    sIsKeyboardShowing = false;
    hideKeyboard(currentFocusView);
  }
  
  private void resetOtherPanels(View dstPanel) {
    for (ViewBinder vBinder : viewBinders) {
      if (dstPanel != vBinder.panel) {
        vBinder.panel.setVisibility(View.GONE);
      }
    }
  }
  
  private void updateAllPanelHeight(int height) {
    for (ViewBinder vBinder : viewBinders) {
      ViewGroup.LayoutParams params = vBinder.panel.getLayoutParams();
      params.height = height;
      vBinder.panel.setLayoutParams(params);
    }
  }
  
  private void setKeyboardShowing(boolean show) {
    sIsKeyboardShowing = show;
    if (show) {
      resetOtherPanels(null);
      updateSoftInputMethod(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    }
  }
  
  private void setKeyBoardHeight(int keyBoardHeight) {
    sKeyBoardHeight = keyBoardHeight;
    updateAllPanelHeight(keyBoardHeight);
  }
  
  private boolean isTouchKeyboardOutside(int touchY) {
    View foucusView = activity.getCurrentFocus();
    if (foucusView == null) {
      return false;
    }
    int[] location = new int[2];
    foucusView.getLocationOnScreen(location);
    int editY = location[1] - DisplayUtils.getStatusBarHeight();
    int offset = touchY - editY;
    if (offset > 0 && offset < foucusView.getMeasuredHeight()) {
      return false;
    }
    return true;
  }
  
  private boolean isTouchedFoucusView(int x, int y) {
    View foucusView = activity.getCurrentFocus();
    if (foucusView == null) {
      return false;
    }
    int[] location = new int[2];
    foucusView.getLocationOnScreen(location);
    int foucusViewTop = location[1] - DisplayUtils.getStatusBarHeight();
    int offsetY = y - foucusViewTop;
    if (offsetY > 0 && offsetY < foucusView.getMeasuredHeight()) {
      int foucusViewLeft = location[0];
      int foucusViewLength = foucusView.getWidth();
      int offsetX = x - foucusViewLeft;
      if (offsetX >= 0 && offsetX <= foucusViewLength) {
        return true;
      }
    }
    return false;
  }
  
  private void enableCloseKeyboardOnTouchOutside(Activity activity) {
    CloseKeyboardOnOutsideContainer frameLayout = new CloseKeyboardOnOutsideContainer(
        activity);
    activity.addContentView(frameLayout, new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT));
  }
  
  private void detectKeyboard() {
    final View activityRootView = ((ViewGroup) activity
        .findViewById(android.R.id.content)).getChildAt(0);
    if (activityRootView != null) {
      ViewTreeObserver observer = activityRootView.getViewTreeObserver();
      if (observer == null) {
        return;
      }
      observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
          final Rect r = new Rect();
          activityRootView.getWindowVisibleDisplayFrame(r);
          int heightDiff = DisplayUtils.getScreenHeight()
              - (r.bottom - r.top);
          boolean show = heightDiff >= sKeyBoardHeight / 2;
          setKeyboardShowing(show);// 设置键盘是否展开状态
          if (show) {
            int keyboardHeight = heightDiff
                - DisplayUtils.getStatusBarHeight();
            // 设置新的键盘高度
            setKeyBoardHeight(keyboardHeight);
          }
        }
      });
    }
  }
  
  public static interface OnTriggerClickListener {
    
    public void onClick(View v);
  }
  
  public static class ViewBinder {
    private View trigger;
    private View panel;
    private OnTriggerClickListener listener;
    
    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 setTrigger(View trigger) {
      this.trigger = trigger;
    }
    public View getPanel() {
      return panel;
    }
    public void setPanel(View panel) {
      this.panel = panel;
    }
  }
  
  private class CloseKeyboardOnOutsideContainer extends FrameLayout {
    public CloseKeyboardOnOutsideContainer(Context context) {
      this(context, null);
    }
    public CloseKeyboardOnOutsideContainer(Context context,
        AttributeSet attrs) {
      this(context, attrs, 0);
    }
    public CloseKeyboardOnOutsideContainer(Context context,
        AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
      boolean isKeyboardShowing = isKeyboardShowing();
      boolean isEmotionPanelShowing = hasPanelShowing();
      if ((isKeyboardShowing || isEmotionPanelShowing)
          && event.getAction() == MotionEvent.ACTION_DOWN) {
        int touchY = (int) (event.getY());
        int touchX = (int) (event.getX());
        if (isTouchKeyboardOutside(touchY)) {
          if (isKeyboardShowing) {
            hideKeyBordAndSetFlag(activity.getCurrentFocus());
          }
          if (isEmotionPanelShowing) {
            closeAllPanels();
          }
        }
        if (isTouchedFoucusView(touchX, touchY)) {
          // 如果点击的是输入框,那么延时折叠表情面板
          postDelayed(new Runnable() {
            @Override
            public void run() {
              setKeyboardShowing(true);
            }
          }, 500);
        }
      }
      return super.onTouchEvent(event);
    }
  }
  
  private static class DisplayUtils {
    // 四舍五入的偏移值
    private static final float ROUND_CEIL = 0.5f;
    // 屏幕矩阵对象
    private static DisplayMetrics sDisplayMetrics;
    // 资源对象(用于获取屏幕矩阵)
    private static Resources sResources;
    // statusBar的高度(由于这里获取statusBar的高度使用的反射,比较耗时,所以用变量记录)
    private static int statusBarHeight = -1;
    
    public static void init(Context context) {
      sDisplayMetrics = context.getResources().getDisplayMetrics();
      sResources = context.getResources();
    }
    
    public static int getScreenHeight() {
      return sDisplayMetrics.heightPixels;
    }
    
    public static float getDensity() {
      return sDisplayMetrics.density;
    }
    
    public static int dp2px(int dp) {
      return (int) (dp * getDensity() + ROUND_CEIL);
    }
    
    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;
    }
  }
}
您可能感兴趣的文章:Android 软键盘弹出时把原来布局顶上去的解决方法Android 点击屏幕空白处收起输入法软键盘(手动打开)Android判断软键盘的状态和隐藏软键盘的简单实例Android判断软键盘弹出并隐藏的简单完美解决方法(推荐)Android开发软键盘遮挡登陆按钮的完美解决方案Android软键盘弹出时的界面控制方法Android 表情面板和软键盘切换时跳闪问题的解决方法


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯