一、基本介绍
市场上基本的应用都会有登录,登录作为一个灰常重要的页面,给用户的第一感觉一定要简洁美观,最关键还要体验好(体验不好,分分钟都想卸载的冲动,哈哈哈...),很多应用登录按钮被键盘覆盖体验就不是很好,还要把键盘收起来,再点击按钮登录。下面主要针对按钮被覆盖的问题,解决方案就是当键盘弹出时,通过计算,向上移动指定的高度,使按钮不被键盘覆盖。举个栗子,简书的App就是下图这样的,微信的登录也类似。
二、实现原理
首先判断键盘是否能覆盖住按钮(或指定内容),假设覆盖了(覆盖按钮多少高度处理都是一样的),通过获取键盘的高度减去蓝色箭头的高度就是布局向上移动的距离,这样按钮就不会被覆盖。注意:因为弹出键盘的同时,布局向上移动,所以需要监听键盘事件。具体计算:
- 向上移动距离 = 键盘高度 - 蓝色箭头高度
- 键盘的高度:弹出键盘时,挤压根布局多少就是键盘的高度,当然还有其它的方法。
- 蓝色布局箭头的高度:屏幕的高度减去Titlebar至登录按钮的高度(如果有底部虚拟返回键,需要减去虚拟高度,如华为等手机);也可以通过父布局减去子布局的高度,主要看你怎么布局,方法有很多。
三、具体代码实现
-
布局文件activity_login:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_login_root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android:id="@+id/rel_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="16dp" android:paddingRight="16dp"> <ImageView android:id="@+id/img_login" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="40dp" android:src="@mipmap/ic_launcher" /> <LinearLayout android:id="@+id/ll_account" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/img_login" android:layout_marginBottom="12dp" android:layout_marginTop="12dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="账号 " android:textColor="@android:color/black" android:textSize="18sp" /> <EditText android:id="@+id/cet_phone" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="12dp" android:layout_weight="1" android:background="@null" android:hint="请输入账号" android:inputType="text" android:lines="1" android:textColorHint="#CCCCCC" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_below="@id/ll_account" android:background="#EAEAEA" /> <LinearLayout android:id="@+id/ll_code" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/ll_account" android:layout_marginBottom="12dp" android:layout_marginTop="12dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="密码 " android:textColor="@android:color/black" android:textSize="18sp" /> <EditText android:id="@+id/et_pwd" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="12dp" android:background="@null" android:hint="请输入密码" android:inputType="textPassword" android:lines="1" android:maxLength="16" android:textColorHint="#CCCCCC" /> </LinearLayout> <View android:id="@+id/line" android:layout_width="match_parent" android:layout_height="1dp" android:layout_below="@id/ll_code" android:background="#EAEAEA" /> <Button android:id="@+id/btn_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/line" android:layout_marginBottom="12dp" android:layout_marginTop="20dp" android:text="登录" android:textSize="18sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btn_login" android:layout_centerHorizontal="true" android:layout_marginBottom="12dp" android:text="忘记密码" android:textSize="16sp" /> </RelativeLayout> </LinearLayout>
-
LoginActivity:
package com.chao.sample;import android.graphics.Rect; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.LinearLayout; import android.widget.RelativeLayout; import butterknife.Bind; import butterknife.ButterKnife; /** * Created by Iven on 2016/12/25. */ public class LoginActivity extends AppCompatActivity { @Bind(R.id.rel_content) RelativeLayout relContent; @Bind(R.id.ll_login_root) LinearLayout llLoginRoot; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); ButterKnife.bind(this); keepLoginBtnNotOver(llLoginRoot, relContent); //触摸外部,键盘消失 llLoginRoot.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Utils.closeKeyboard(LoginActivity.this); return false; } }); } /** * 保持登录按钮始终不会被覆盖 * * @param root * @param subView */ private void keepLoginBtnNotOver(final View root, final View subView) { root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect rect = new Rect(); // 获取root在窗体的可视区域 root.getWindowVisibleDisplayFrame(rect); // 获取root在窗体的不可视区域高度(被其他View遮挡的区域高度) int rootInvisibleHeight = root.getRootView().getHeight() - rect.bottom; // 若不可视区域高度大于200,则键盘显示,其实相当于键盘的高度 if (rootInvisibleHeight > 200) { // 显示键盘时 int srollHeight = rootInvisibleHeight - (root.getHeight() - subView.getHeight()) - Utils.getNavigationBarHeight(root.getContext()); if (srollHeight > 0) {当键盘高度覆盖按钮时 root.scrollTo(0, srollHeight); } } else { // 隐藏键盘时 root.scrollTo(0, 0); } } }); } }
-
用到的工具类Utils:
package com.chao.sample; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.util.Log; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import java.lang.reflect.Method; /** * Created by Iven on 2016/12/25. */ public class Utils { private static final String TAG = Utils.class.getSimpleName(); /** * 关闭键盘 * * @param activity */ public static void closeKeyboard(Activity activity) { if (activity.getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) { if (activity.getCurrentFocus() != null) { InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } } /** * 判断是否有虚拟底部按钮 * * @return */ public static boolean checkDeviceHasNavigationBar(Context context) { boolean hasNavigationBar = false; Resources rs = context.getResources(); int id = rs.getIdentifier("config_showNavigationBar", "bool", "android"); if (id > 0) { hasNavigationBar = rs.getBoolean(id); } try { Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); Method m = systemPropertiesClass.getMethod("get", String.class); String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { hasNavigationBar = false; } else if ("0".equals(navBarOverride)) { hasNavigationBar = true; } } catch (Exception e) { Log.w(TAG, e); } return hasNavigationBar; } /** * 获取底部虚拟按键高度 * * @return */ public static int getNavigationBarHeight(Context context) { int navigationBarHeight = 0; Resources rs = context.getResources(); int id = rs.getIdentifier("navigation_bar_height", "dimen", "android"); if (id > 0 && checkDeviceHasNavigationBar(context)) { navigationBarHeight = rs.getDimensionPixelSize(id); } return navigationBarHeight; } }
最后实现效果图:
如有问题欢迎留言,感谢支持和关注。