Android 軟件盤彈出可能會遮擋住界面上的某些控件猖凛。當(dāng) windowSoftInputMode 為 adjustPan 時舀锨,一般不會擋住 EditText大审,但是假如 EditText 下面是一個登錄按鈕,那么這個按鈕就可能被擋住许布,但有時我們希望用戶輸完密碼可以直接點擊登錄按鈕替废,而不用把軟鍵盤收起來箍铭。這時就需要用到 adjustResize,這種模式能夠獲取到軟鍵盤的高度椎镣,這樣我們就能夠精確的對界面進(jìn)行控制诈火。
在閱讀本章之前,你應(yīng)該了解 windowSoftInputMode 的一些屬性状答,特別是 adjustResize柄瑰,如果還不熟悉,建議先閱讀Android 軟鍵盤之 windowSoftInputMode 分析再回過頭來繼續(xù)往下看剪况。
QQ 登錄界面很好的解決了軟鍵盤遮擋問題,當(dāng)然在大屏手機(jī)上軟鍵盤并不會擋住登錄按鈕蒲跨。今天我們也來模仿一個 QQ 登錄界面译断,最終效果如下:
監(jiān)聽軟鍵盤彈出及收起事件
step1. 指定 windowSoftInputMode="adjustResize"
在 AndroidManifest.xml 中相應(yīng)的 Activity 設(shè)置 android:windowSoftInputMode="adjustResize",也可以在 java 代碼中設(shè)置或悲。
step2. 監(jiān)聽 contentView 寬高(layout) 變化
獲取 ViewTreeObserver 并監(jiān)聽 OnGlobalLayoutListener, 當(dāng)viewTree的布局發(fā)生變化時onGlobalLayout將會回調(diào)孙咪。我們新建一個KeyboardHelper輔助類。
KeyboardHelper.java
public class KeyboardHelper {
private Activity activity;
private OnKeyboardStatusChangeListener onKeyboardStatusChangeListener;
private int windowBottom = -1;
private int keyboardHeight = 0;
public KeyboardHelper(Activity activity) {
this.activity = activity;
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
if (activity.getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
public void onCreate() {
View content = activity.findViewById(android.R.id.content);
// content.addOnLayoutChangeListener(listener); 這個方法有時會出現(xiàn)一些問題
content.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
public void onDestroy() {
View content = activity.findViewById(android.R.id.content);
ViewUtils.removeOnGlobalLayoutListener(content, onGlobalLayoutListener);
}
private OnGlobalLayoutListener onGlobalLayoutListener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
Log.d("KeyboardHelper", "onGlobalLayout: " + rect + ", " + windowBottom);
int newBottom = rect.bottom;
if (windowBottom != -1 && windowBottom != newBottom) {
if (newBottom < windowBottom) {
// keyboard pop
keyboardHeight = windowBottom - newBottom;
if (onKeyboardStatusChangeListener != null) {
onKeyboardStatusChangeListener.onKeyboardPop(keyboardHeight);
}
} else {
// keyboard close
if (onKeyboardStatusChangeListener != null) {
onKeyboardStatusChangeListener.onKeyboardClose(keyboardHeight);
}
}
}
windowBottom = newBottom;
}
};
public void setOnKeyboardStatusChangeListener(
OnKeyboardStatusChangeListener onKeyboardStatusChangeListener) {
this.onKeyboardStatusChangeListener = onKeyboardStatusChangeListener;
}
public interface OnKeyboardStatusChangeListener {
void onKeyboardPop(int keyboardHeight);
void onKeyboardClose(int keyboardHeight);
}
}
ViewUtils.java
public class ViewUtils {
public static void removeOnGlobalLayoutListener(View view, ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener) {
if (Build.VERSION.SDK_INT < 16) {
view.getViewTreeObserver().removeGlobalOnLayoutListener(onGlobalLayoutListener);
} else {
view.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
}
代碼比較簡單巡语,無非是獲取window的可視坐標(biāo)翎蹈,通過比較bottom的值來判斷軟鍵盤是彈出還是收起。
實現(xiàn) QQ 登錄界面
有了這個 KeyBoardHelper男公,那么要實現(xiàn)和 QQ 登錄界面一樣的效果就不難了荤堪。我們甚至不需要任何自定義控件。思路是在軟鍵盤彈出時,把登錄按鈕以上的布局往上移澄阳,只要為其設(shè)置一個負(fù)值的 topMargin 即可拥知。
如圖所示:
MainActivity 代碼如下:
public class MainActivity extends Activity {
private int bottomHeight;
private KeyboardHelper keyboardHelper;
private View layoutBottom;
private View layoutContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qq_login);
layoutContent = findViewById(R.id.layout_content);
layoutBottom = findViewById(R.id.layout_bottom);
keyboardHelper = new KeyboardHelper(this);
keyboardHelper.onCreate();
keyboardHelper.setOnKeyboardStatusChangeListener(onKeyBoardStatusChangeListener);
layoutBottom.post(new Runnable() {
@Override
public void run() {
bottomHeight = layoutBottom.getHeight();
}
});
}
private OnKeyboardStatusChangeListener onKeyBoardStatusChangeListener = new OnKeyboardStatusChangeListener() {
@Override
public void onKeyboardPop(int keyboardHeight) {
final int height = keyboardHeight;
if (bottomHeight > height) {
layoutBottom.setVisibility(View.GONE);
} else {
int offset = bottomHeight - height;
final ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) layoutContent
.getLayoutParams();
lp.topMargin = offset;
layoutContent.setLayoutParams(lp);
}
}
@Override
public void onKeyboardClose(int keyboardHeight) {
if (View.VISIBLE != layoutBottom.getVisibility()) {
layoutBottom.postDelayed(new Runnable() {
@Override
public void run() {
layoutBottom.setVisibility(View.VISIBLE);
}
}, 300);
}
final ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) layoutContent
.getLayoutParams();
if (lp.topMargin != 0) {
lp.topMargin = 0;
layoutContent.setLayoutParams(lp);
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
keyboardHelper.onDestroy();
}
}
布局文件:activity_qq_login.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="50dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="18dp"
android:contentDescription="marginTop68dp"
android:scaleType="centerInside"
android:src="@drawable/qq_ava"
android:visibility="visible" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:background="#f3f0f0"
android:paddingBottom="100dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="100dp"
android:text="這里為了演示增加一個TextView使登陸按鈕在鍵盤底部"
android:textColor="#333333"
android:textSize="13sp" />
<LinearLayout
android:id="@+id/layout_ed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:divider="@drawable/divider"
android:orientation="vertical"
android:showDividers="middle">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="QQ號/手機(jī)號/郵箱"
android:padding="10dp"
android:textColor="#000000"
android:textColorHint="#d2d2d2"
android:textCursorDrawable="@null" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="密碼"
android:padding="10dp"
android:textColor="#000000"
android:textColorHint="#d2d2d2"
android:textCursorDrawable="@null" />
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:layout_marginTop="14dp"
android:background="@drawable/btn_login"
android:text="登陸"
android:textSize="17sp" />
<RelativeLayout
android:id="@+id/layout_bottom"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_cannot_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="14dp"
android:text="無法登陸?"
android:textColor="@color/action_bar_bg"
android:textSize="14sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="14dp"
android:text="新用戶"
android:textColor="@color/action_bar_bg"
android:textSize="14sp" />
</RelativeLayout>
</LinearLayout>
<include
android:id="@+id/appbar"
layout="@layout/appbar"
android:layout_width="match_parent"
android:layout_height="50dp" />
</FrameLayout>