最近做项目里面遇到了表情栏与手机键盘切换的时候会有因为键盘开启或者关闭时间问题导致的高度闪烁问题,然后百度一番,站在各位巨人的肩膀上,简单理一下实现类似微信表情栏切换的效果:
1.在键盘弹出的时候,对键盘高度进行记录,并将此高度设置给表情栏,使得键盘高度与表情栏高度相同,并将当前的SoftInputMode进行设置为SOFT_INPUT_ADJUST_NOTHING,然后把表情栏进行显示,当前键盘显示覆盖在表情界面之上。
2.显示表情栏布局的时候,关闭键盘直接调用键盘关闭方法即可
3.在edittext失去焦点即小键盘关闭的时候,将SoftInputMode设置回SOFT_INPUT_ADJUST_RESIZE,这样下次键盘弹出就是正常效果。
要解决的话首先要获取到键盘的高度,很多博客都有解决方案,这里我选择使用
https://www.jianshu.com/p/4293f90e7f1f
这个文章的方案,个人觉得比较简单并且通用性强。
获取到高度以后,动态设置表情栏的高度然后顺着思路进行代码编写就行了。代码如下:
布局代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<wjzc.com.dragview.ResizeFrameLayout
android:id="@+id/resize_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"></wjzc.com.dragview.ResizeFrameLayout>
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"/>
<Button
android:id="@+id/btn"
android:text="haha"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<Button
android:id="@+id/btn2"
android:text="hehe"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:visibility="gone"
android:id="@+id/ll_layout"
android:background="#ff00"
android:layout_width="match_parent"
android:layout_height="500dp">
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:visibility="gone"
android:id="@+id/hehe_layout"
android:background="#fff0"
android:layout_width="match_parent"
android:layout_height="500dp">
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv"
android:layout_above="@id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Activity代码:
public class SeccondActivity extends Activity implements View.OnClickListener {
private Button btn,hehe;
private EditText et;
private LinearLayout ll_layout,hehe_layout;
private TextView tv;
private Context mContext;
private ResizeFrameLayout resize_layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mContext=this;
init();
}
private void init() {
resize_layout=findViewById(R.id.resize_layout);
btn = findViewById(R.id.btn);
btn.setOnClickListener(this);
hehe = findViewById(R.id.btn2);
hehe.setOnClickListener(this);
tv = findViewById(R.id.tv);
tv.setOnClickListener(this);
ll_layout=findViewById(R.id.ll_layout);
hehe_layout=findViewById(R.id.hehe_layout);
resize_layout.setKeyboardListener(new KeyboardListener() {
@Override
public void onKeyboardShown(int height) {
Log.e("键盘高度", "onKeyboardShown: "+height );
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) ll_layout.getLayoutParams();
layoutParams.height=height;
ll_layout.setLayoutParams(layoutParams);
hehe_layout.setLayoutParams(layoutParams);
}
@Override
public void onKeyboardHidden(int height) {
Log.e("键盘高度", "onKeyboardHidden: "+height );
}
});
et=findViewById(R.id.et);
et.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (ll_layout.getVisibility()==View.VISIBLE || hehe_layout.getVisibility()==View.VISIBLE ){
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
KeyboardUtils.showSoftInput(mContext,et);
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn2:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
KeyboardUtils.hideSoftInput(SeccondActivity.this,et);
if (ll_layout.getVisibility()==View.VISIBLE){
ll_layout.setVisibility(View.GONE);
}
if (hehe_layout.getVisibility()!=View.VISIBLE){
hehe_layout.setVisibility(View.VISIBLE);
}
break;
case R.id.btn:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
KeyboardUtils.hideSoftInput(SeccondActivity.this,et);
if (hehe_layout.getVisibility()==View.VISIBLE){
hehe_layout.setVisibility(View.GONE);
}
if (ll_layout.getVisibility()!=View.VISIBLE){
ll_layout.setVisibility(View.VISIBLE);
}
break;
case R.id.tv:
hehe_layout.setVisibility(View.GONE);
ll_layout.setVisibility(View.GONE);
KeyboardUtils.hideSoftInput(SeccondActivity.this,et);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
break;
}
}
}
还有两个相关工具类:
public class KeyboardUtils { private KeyboardUtils() { throw new UnsupportedOperationException("u can't fuck me..."); } /** * 避免输入法面板遮挡 * <p>在manifest.xml中activity中设置</p> * <p>android:windowSoftInputMode="stateVisible|adjustResize"</p> */ /** * 动态隐藏软键盘 * * @param activity activity */ public static void hideSoftInput(Activity activity) { View view = activity.getWindow().peekDecorView(); if (view != null) { InputMethodManager inputmanger = (InputMethodManager) activity .getSystemService(Context.INPUT_METHOD_SERVICE); inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0); } } /** * 动态隐藏软键盘 * * @param context 上下文 * @param edit 输入框 */ public static void hideSoftInput(Context context, EditText edit) { edit.setFocusable(false); InputMethodManager inputmanger = (InputMethodManager) context .getSystemService(Context.INPUT_METHOD_SERVICE); inputmanger.hideSoftInputFromWindow(edit.getWindowToken(), 0); } /** * 点击屏幕空白区域隐藏软键盘(方法2) * <p>根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘</p> * <p>需重写dispatchTouchEvent</p> * <p>参照以下注释代码</p> */ /** * 动态显示软键盘 * * @param context 上下文 * @param edit 输入框 */ public static void showSoftInput(Context context, EditText edit) { edit.setFocusable(true); edit.setFocusableInTouchMode(true); edit.requestFocus(); InputMethodManager inputManager = (InputMethodManager) context .getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(edit, 0); } /** * 切换键盘显示与否状态 * * @param context 上下文 * @param edit 输入框 */ public static void toggleSoftInput(Context context, EditText edit) { edit.setFocusable(true); edit.setFocusableInTouchMode(true); edit.requestFocus(); InputMethodManager inputManager = (InputMethodManager) context .getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } }
public class ScreenUtils { private ScreenUtils() { throw new UnsupportedOperationException("cannot be instantiated"); } /** * 获得屏幕宽度 */ public static int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; } /** * 获得屏幕高度---2396 */ public static int getScreenHeight(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.heightPixels; } private volatile static boolean mHasCheckAllScreen; private volatile static boolean mIsAllScreenDevice; private static final int PORTRAIT = 0; private static final int LANDSCAPE = 1; @NonNull private volatile static Point[] mRealSizes = new Point[2]; /** * 获得状态栏高度 */ public static int getStatusHeight(Context context) { int statusHeight = -1; try { Class<?> clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); int height = Integer.parseInt(clazz.getField("status_bar_height") .get(object).toString()); statusHeight = context.getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } return statusHeight; } /** * 获取当前屏幕截图,包含状态栏 */ public static Bitmap snapShotWithStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, 0, width, height); view.destroyDrawingCache(); return bp; } /** * 获取当前屏幕截图,不包含状状态栏 */ public static Bitmap snapShotWithoutStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); Rect frame = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight); view.destroyDrawingCache(); return bp; } public static float dpToPx(Context context, float dp) { if (context == null) { return -1; } return dp * context.getResources().getDisplayMetrics().density; } public static float spTopx(Context context, float sp) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (sp * fontScale + 0.5f); } //获取虚拟按键的高度 public static int getNavigationBarHeight(Context context) { int result = 0; if (hasNavBar(context)) { Resources res = context.getResources(); int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { result = res.getDimensionPixelSize(resourceId); } } return result; } /** * 检查是否存在虚拟按键栏 * * @param context * @return */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public static boolean hasNavBar(Context context) { Resources res = context.getResources(); int resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android"); if (resourceId != 0) { boolean hasNav = res.getBoolean(resourceId); // check override flag String sNavBarOverride = getNavBarOverride(); if ("1".equals(sNavBarOverride)) { hasNav = false; } else if ("0".equals(sNavBarOverride)) { hasNav = true; } return hasNav; } else { // fallback return !ViewConfiguration.get(context).hasPermanentMenuKey(); } } /** * 判断虚拟按键栏是否重写 * * @return */ private static String getNavBarOverride() { String sNavBarOverride = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { Class c = Class.forName("android.os.SystemProperties"); Method m = c.getDeclaredMethod("get", String.class); m.setAccessible(true); sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys"); } catch (Throwable e) { } } return sNavBarOverride; } /** * 判断设备是否存在NavigationBar(虚拟导航栏) * * @return true 存在, false 不存在 */ public static boolean deviceHasNavigationBar() { boolean haveNav = false; try { //1.通过WindowManagerGlobal获取windowManagerService // 反射方法:IWindowManager windowManagerService = WindowManagerGlobal.getWindowManagerService(); Class<?> windowManagerGlobalClass = Class.forName("android.view.WindowManagerGlobal"); Method getWmServiceMethod = windowManagerGlobalClass.getDeclaredMethod("getWindowManagerService"); getWmServiceMethod.setAccessible(true); //getWindowManagerService是静态方法,所以invoke null Object iWindowManager = getWmServiceMethod.invoke(null); //2.获取windowMangerService的hasNavigationBar方法返回值 // 反射方法:haveNav = windowManagerService.hasNavigationBar(); Class<?> iWindowManagerClass = iWindowManager.getClass(); Method hasNavBarMethod = iWindowManagerClass.getDeclaredMethod("hasNavigationBar"); hasNavBarMethod.setAccessible(true); haveNav = (Boolean) hasNavBarMethod.invoke(iWindowManager); } catch (Exception e) { e.printStackTrace(); } return haveNav; } /** * 判断全面屏是否启用虚拟键盘 */ private static final String NAVIGATION = "navigationBarBackground"; public static boolean isNavigationBarExist(@NonNull Activity activity) { ViewGroup vp = (ViewGroup) activity.getWindow().getDecorView(); if (vp != null) { for (int i = 0; i < vp.getChildCount(); i++) { vp.getChildAt(i).getContext().getPackageName(); if (vp.getChildAt(i).getId()!=-1&& NAVIGATION.equals(activity.getResources().getResourceEntryName(vp.getChildAt(i).getId()))) { return true; } } } return false; } public static int getNavBarH(Activity activity){ if(ScreenUtils.isNavigationBarExist(activity)){ return ScreenUtils.getNavigationBarHeight(activity); }else{ return 0; } } }