版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!java
演示獲取軟鍵盤高度並保存,而後根據輸入框的原有位置是否被軟鍵盤擋住了,若是被擋住了則將總體頁面上移必定的高度,當軟鍵盤隱藏的時候再下移回來的功能。android
KeyboardUtil:顯示、隱藏軟鍵盤,以及保存軟鍵盤的高度值;git
KeyboardSharedPreferences:SharedPreferences存儲工具類;github
ViewAnimationUtil:上移、下移的動畫效果;app
首先,獲取軟鍵盤的高度值並保存【當點擊輸入框,彈出軟鍵盤的時候會進行保存,具體邏輯見代碼】ide
//計算總體view須要移動的高度值(總高度 - 可見區域高度 + top(標題欄高度) = 隱藏區域高度(軟鍵盤高度值)) ((RelativeLayout)rootLayout).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { //當保存的高度值小於300的時候執行 // 【當APP第一次打開的時候,這裏的代碼也會執行,那麼此時keyboardHeight==0,那麼會繼續執行下面代碼,可是keyboardHeight計算後的值通常會小於300,因此此時不能保存keyboardHeight!!!】 // 【當觸摸輸入框的時候,無論輸入框在軟鍵盤上方仍是下方,此時keyboardHeight計算後的值>300】【也就是彈出系統軟鍵盤後總體view向上移動的距離(rect.bottom值變小了),也就能夠理解爲系統軟鍵盤的高度】 if(keyboardHeight < 300) { Rect rect = new Rect(); rootLayout.getWindowVisibleDisplayFrame(rect);//rect指可見區域 Log.e(TAG, "{onGlobalLayout}rootLayout.getRootView().getHeight()=" + rootLayout.getRootView().getHeight());//【移動前的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}rect.bottom=" + rect.bottom);//【移動後的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}rect.top=" + rect.top);//【標題欄的高度值】 keyboardHeight = rootLayout.getRootView().getHeight() - rect.bottom + rect.top; Log.e(TAG, "{onGlobalLayout}keyboardHeight=" + keyboardHeight);// if (keyboardHeight > 300) { KeyboardUtil.saveKeyboardHeight(MainActivity.this, keyboardHeight); } }else {//方案一 Rect rect = new Rect(); rootLayout.getWindowVisibleDisplayFrame(rect);//rect指可見區域 Log.e(TAG, "{onGlobalLayout}rect.bottom=" + rect.bottom);//【移動後的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}keyboardHeight=" + keyboardHeight);// if(rect.bottom != keyboardHeight){//表明軟鍵盤隱藏了,當軟鍵盤顯示的時候,rect.bottom == keyboardHeight downEditRect(); } } } });
而後,輸入框添加觸摸事件監聽,用於上移的動畫工具
//輸入框觸摸的監聽事件 edt_user.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //【注意:edt_user.getBottom()是指系統軟鍵盤彈出來後的輸入框的bottom值,,缺乏頂部標題欄的區域高度,並且連續點擊後值不變】 Log.i(TAG, "{initViews}edt_user.getBottom()=" + edt_user.getBottom()); //計算相對於Windows的座標 int[] locationW = new int[2]; edt_user.getLocationInWindow(locationW); Log.i(TAG, "{onTouch}locationW=" + locationW[0] +";" + locationW[1]); //計算相對於Screen的座標 int[] locationS = new int[2]; edt_user.getLocationOnScreen(locationS); Log.i(TAG, "{onTouch}locationS=" + locationS[0] +";" + locationS[1]); Log.i(TAG, "{onTouch}edt_user.getMeasuredHeight()=" + edt_user.getMeasuredHeight());//輸入框的高度 int edtBottom = locationW[1] + edt_user.getMeasuredHeight();//輸入框的底部的Y座標值== topY + Height; showEditRect(edt_user,edtBottom); return false; } });
最後,監聽軟鍵盤隱藏、總體頁面添加點擊事件,實現下移動畫【監聽軟鍵盤隱藏是在上面的addOnGlobalLayoutListener方法中實現的】佈局
//整個界面區域的觸摸事件 rootLayout.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { Log.v(TAG, "{initialize}rootLayout=onTouch"); //隱藏自定義的軟鍵盤區域 hideEditRect(); return true; } });
注意事項:post
一、 導入類文件後須要change包名以及從新import R文件路徑動畫
二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋
package com.why.project.keyboardutildemo.keyboard; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.util.Log; import android.view.View; import android.view.inputmethod.InputMethodManager; import com.why.project.keyboardutildemo.R; /** * Used 軟鍵盤的操做類(顯示、隱藏軟件盤、保存軟鍵盤的高度值——用於控制輸入框的上升) */ public class KeyboardUtil { /**最後一次保存的鍵盤高度*/ private static int LAST_SAVE_KEYBOARD_HEIGHT = 0; /**輸入法軟鍵盤區域的最大高度*/ private static int MAX_PANEL_HEIGHT = 0; /**輸入法軟鍵盤區域的最小高度*/ private static int MIN_PANEL_HEIGHT = 0; /**顯示軟鍵盤*/ public static void showKeyboard(View view) { if(view != null){ view.requestFocus(); InputMethodManager inputManager = (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (inputManager != null) { inputManager.showSoftInput(view, 0); } } } /**隱藏軟鍵盤 * 第一個參數中的windowToken應當是以前請求顯示軟鍵盤的View的windowToken,也就是執行showSoftInput()時第一個參數中的View的windowToken。 * 可是實際狀況是,用任意一個當前佈局中的已經加載的View的windowToken均可以隱藏軟鍵盤,哪怕這個View被設置爲INVISIBLE或GONE。 * 所以,若是不知道以前是誰請求顯示的軟鍵盤,能夠隨便傳入一個當前佈局中存在的View的windowToken。 * 特別的,能夠傳入一個Activity的頂層View的windowToken,即getWindow().getDecorView().getWindowToken(),來隱藏當前Activity中顯示的軟鍵盤, * 而不用管以前調用showSoftInput()的到底是哪一個View。*/ public static void hideKeyboard(Activity mActivity) { InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(mActivity.getWindow().getDecorView().getWindowToken(), 0); } } /**隱藏軟鍵盤*/ public static void hideKeyboard(View view) { InputMethodManager imm = (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); view.clearFocus(); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } /**是否正在顯示軟鍵盤*/ public static boolean isShowKeyboard(Context context, View view) { boolean bool = false; InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm.hideSoftInputFromWindow(view.getWindowToken(), 0)) { imm.showSoftInput(view, 0); bool = true; } return bool; } /**保存軟鍵盤的高度值*/ public static boolean saveKeyboardHeight(Context context, int keyboardHeight) { Log.d("KeyboardUtil", "keyboardHeight="+keyboardHeight); if(keyboardHeight <= 300 || LAST_SAVE_KEYBOARD_HEIGHT == keyboardHeight) { return false; } LAST_SAVE_KEYBOARD_HEIGHT = keyboardHeight; return KeyboardSharedPreferences.save(context, keyboardHeight); } /**獲取軟鍵盤的高度值*/ public static int getKeyboardHeight(Context context) { if(LAST_SAVE_KEYBOARD_HEIGHT == 0) { LAST_SAVE_KEYBOARD_HEIGHT = KeyboardSharedPreferences.get(context, getMinPanelHeight(context.getResources())); } return LAST_SAVE_KEYBOARD_HEIGHT; } /**獲取面板的最大高度值-自定義的*/ private static int getMaxPanelHeight(Resources res) { if(MAX_PANEL_HEIGHT == 0) { MAX_PANEL_HEIGHT = res.getDimensionPixelSize(R.dimen.keyboard_content_panel_max_height); } return MAX_PANEL_HEIGHT; } /**獲取面板的最小高度值-自定義的*/ private static int getMinPanelHeight(Resources res) { if(MIN_PANEL_HEIGHT == 0) { MIN_PANEL_HEIGHT = res.getDimensionPixelSize(R.dimen.keyboard_content_panel_min_height); } return MIN_PANEL_HEIGHT; } }
package com.why.project.keyboardutildemo.keyboard; import android.content.Context; import android.content.SharedPreferences; /** * Used SharedPreferences存儲軟鍵盤的高度值 */ public class KeyboardSharedPreferences { /**存儲文件名*/ private static final String FILE_NAME = "KeyboardSharedPreferences"; /**存儲Key值*/ private static final String KEY_KEYBORD_HEIGHT = "keyboardHeight"; private static volatile SharedPreferences SP; /**實例化SharedPreferences*/ private static SharedPreferences with(Context context) { if(SP == null) { synchronized(KeyboardSharedPreferences.class) { if(SP == null) { SP = context.getSharedPreferences(FILE_NAME, 0); } } } return SP; } /**存儲軟鍵盤的高度*/ public static boolean save(Context context, int keyboardHeight) { return with(context).edit().putInt(KEY_KEYBORD_HEIGHT, keyboardHeight).commit(); } /**讀取存儲軟鍵盤的高度(帶默認值)*/ public static int get(Context context, int defaultHeight) { return with(context).getInt(KEY_KEYBORD_HEIGHT, defaultHeight); } }
package com.why.project.keyboardutildemo.keyboard; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; /** * Used 界面上升降低動畫效果工具類(全) */ public class ViewAnimationUtil { private static ViewAnimationListener mViewAnimationListener = null; public ViewAnimationUtil() { } /**區域的上升動畫效果*/ public static void editAreaAnimator(View view, float translationFromY, float translationToY, float scalingFromRatio, float scalingToRatio, final boolean doEnd) { ObjectAnimator animTY = ObjectAnimator.ofFloat(view, "translationY", new float[] {translationFromY, translationToY}); ObjectAnimator animTSX = ObjectAnimator.ofFloat(view, "scaleX", new float[] {scalingFromRatio, scalingToRatio}); ObjectAnimator animTSY = ObjectAnimator.ofFloat(view, "scaleY", new float[] {scalingFromRatio, scalingToRatio}); AnimatorSet editAreaSet = new AnimatorSet(); int EDIT_DURATION = 500; editAreaSet.setDuration((long)EDIT_DURATION); editAreaSet.playTogether(new Animator[] {animTY, animTSX, animTSY}); editAreaSet.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if((doEnd) && mViewAnimationListener != null) { mViewAnimationListener.endAnimation(); } } }); editAreaSet.start(); } public static void setViewAnimationListener(ViewAnimationListener mmViewAnimationListener) { mViewAnimationListener = mmViewAnimationListener; } public static abstract interface ViewAnimationListener { public abstract void endAnimation(); } }
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <!-- *********KeyboardUtil相關********* --> <dimen name="keyboard_content_panel_max_height">120dp</dimen> <dimen name="keyboard_content_panel_min_height">0dp</dimen> </resources>
input_box_send.9.png
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layoutroot" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- 當之有一個EditText或者AutoCompleteTextView的時候,進入畫面時是默認獲得焦點的。 要想去除焦點,能夠在auto以前加一個0像素的layout,並設置他先獲得焦點。 --> <LinearLayout android:layout_width="0px" android:layout_height="0px" android:focusable="true" android:focusableInTouchMode="true"/> <LinearLayout android:id="@+id/centerLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_alignParentBottom="true" android:layout_marginBottom="200dp"> <EditText android:id="@+id/edt_user" android:layout_width="match_parent" android:layout_height="48dp" android:inputType="text" android:hint="請輸入用戶名" android:lines="1" android:background="@drawable/input_box_send" android:layout_margin="5dp" /> <EditText android:id="@+id/edt_send" android:layout_width="match_parent" android:layout_height="48dp" android:hint="請輸入密碼" android:lines="1" android:imeOptions="actionGo" android:inputType="textPassword" android:background="@drawable/input_box_send" android:layout_margin="5dp" /> </LinearLayout> </RelativeLayout>
其中第一個輸入框的原有位置在彈起的軟鍵盤上方,第二個輸入框的原有位置在彈起的軟鍵盤下方。
package com.why.project.keyboardutildemo; import android.graphics.Rect; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.EditText; import android.widget.RelativeLayout; import com.why.project.keyboardutildemo.keyboard.KeyboardUtil; import com.why.project.keyboardutildemo.keyboard.ViewAnimationUtil; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private RelativeLayout rootLayout;//總體View private EditText edt_send;//輸入框view private EditText edt_user;//輸入框view /**區域是否上升了*/ private boolean editAreaIsUp = false; /**軟鍵盤的高度值*/ private int keyboardHeight = 0; /**須要上升的高度值*/ private int textBottom = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); } private void initViews() { rootLayout = (RelativeLayout) findViewById(R.id.layoutroot); edt_send = (EditText) findViewById(R.id.edt_send); edt_user = (EditText) findViewById(R.id.edt_user); //計算總體view須要移動的高度值(總高度 - 可見區域高度 + top(標題欄高度) = 隱藏區域高度(軟鍵盤高度值)) ((RelativeLayout)rootLayout).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { //當保存的高度值小於300的時候執行 // 【當APP第一次打開的時候,這裏的代碼也會執行,那麼此時keyboardHeight==0,那麼會繼續執行下面代碼,可是keyboardHeight計算後的值通常會小於300,因此此時不能保存keyboardHeight!!!】 // 【當觸摸輸入框的時候,無論輸入框在軟鍵盤上方仍是下方,此時keyboardHeight計算後的值>300】【也就是彈出系統軟鍵盤後總體view向上移動的距離(rect.bottom值變小了),也就能夠理解爲系統軟鍵盤的高度】 if(keyboardHeight < 300) { Rect rect = new Rect(); rootLayout.getWindowVisibleDisplayFrame(rect);//rect指可見區域 Log.e(TAG, "{onGlobalLayout}rootLayout.getRootView().getHeight()=" + rootLayout.getRootView().getHeight());//【移動前的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}rect.bottom=" + rect.bottom);//【移動後的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}rect.top=" + rect.top);//【標題欄的高度值】 keyboardHeight = rootLayout.getRootView().getHeight() - rect.bottom + rect.top; Log.e(TAG, "{onGlobalLayout}keyboardHeight=" + keyboardHeight);// if (keyboardHeight > 300) { KeyboardUtil.saveKeyboardHeight(MainActivity.this, keyboardHeight); } }else {//方案一 Rect rect = new Rect(); rootLayout.getWindowVisibleDisplayFrame(rect);//rect指可見區域 Log.e(TAG, "{onGlobalLayout}rect.bottom=" + rect.bottom);//【移動後的rootLayout的bottom】 Log.e(TAG, "{onGlobalLayout}keyboardHeight=" + keyboardHeight);// if(rect.bottom != keyboardHeight){//表明軟鍵盤隱藏了,當軟鍵盤顯示的時候,rect.bottom == keyboardHeight downEditRect(); } } } }); //整個界面區域的觸摸事件 rootLayout.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { Log.v(TAG, "{initialize}rootLayout=onTouch"); //隱藏自定義的軟鍵盤區域 hideEditRect(); return true; } }); //輸入框觸摸的監聽事件 edt_user.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //【注意:edt_user.getBottom()是指系統軟鍵盤彈出來後的輸入框的bottom值,,缺乏頂部標題欄的區域高度,並且連續點擊後值不變】 Log.i(TAG, "{initViews}edt_user.getBottom()=" + edt_user.getBottom()); //計算相對於Windows的座標 int[] locationW = new int[2]; edt_user.getLocationInWindow(locationW); Log.i(TAG, "{onTouch}locationW=" + locationW[0] +";" + locationW[1]); //計算相對於Screen的座標 int[] locationS = new int[2]; edt_user.getLocationOnScreen(locationS); Log.i(TAG, "{onTouch}locationS=" + locationS[0] +";" + locationS[1]); Log.i(TAG, "{onTouch}edt_user.getMeasuredHeight()=" + edt_user.getMeasuredHeight());//輸入框的高度 int edtBottom = locationW[1] + edt_user.getMeasuredHeight();//輸入框的底部的Y座標值== topY + Height; showEditRect(edt_user,edtBottom); return false; } }); //輸入框觸摸的監聽事件 edt_send.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //【注意:edt_send.getBottom()是指系統軟鍵盤彈出來後的輸入框的bottom值,缺乏頂部標題欄的區域高度,並且連續點擊後值不變】 Log.i(TAG, "{initViews}edt_send.getBottom()=" + edt_send.getBottom()); //計算相對於Windows的座標 int[] locationW = new int[2]; edt_send.getLocationInWindow(locationW); Log.i(TAG, "{initViews}locationW1=" + locationW[0] +";" + locationW[1]); //計算相對於Screen的座標 int[] locationS = new int[2]; edt_send.getLocationOnScreen(locationS); Log.i(TAG, "{initViews}locationS1=" + locationS[0] +";" + locationS[1]); int edtBottom = locationW[1] + edt_send.getMeasuredHeight();//輸入框的底部的Y座標值== topY + Height; showEditRect(edt_send,edtBottom); return false; } }); } /** * 顯示自定義的軟鍵盤區域 * @param view - 輸入框view * @param bottom 輸入框的bottom【彈出系統軟鍵盤後的值】*/ public void showEditRect(final View view, final int bottom) { //實現編輯區域的上升動畫效果 view.postDelayed(new Runnable() { public void run() { Log.w(TAG, "(showEditRect)bottom="+bottom); Log.w(TAG, "(showEditRect)keyboardHeight="+keyboardHeight); if(keyboardHeight != 0 && bottom - keyboardHeight > 0){//爲何須要判斷bottom - keyboardHeight > 0??由於當已經彈出軟鍵盤後繼續點擊輸入框的時候,就不須要在上移了,而能夠經過bottom值變小了來解決繼續上移的問題。 textBottom = view.getMeasuredHeight(); Log.w(TAG, "(showEditRect)textBottom="+textBottom); makeEditAreaUpAndSmall(((float)textBottom)); } } }, 300); } /**隱藏自定義軟鍵盤區域*/ private void hideEditRect(){ KeyboardUtil.hideKeyboard(this); downEditRect(); } /**下移*/ private void downEditRect(){ if(textBottom > 0) { makeEditAreaOriginal((float)textBottom); textBottom = 0; } } /**編輯區域上升的動畫效果:指定高度*/ public void makeEditAreaUpAndSmall(float to) { if (!this.editAreaIsUp) { ViewAnimationUtil.editAreaAnimator(rootLayout, 0.0F, -to, 1.0F, 1.0F,false); this.editAreaIsUp = true; } } /**編輯區域回到原始的動畫效果*/ public void makeEditAreaOriginal(float from) { if(this.editAreaIsUp) { ViewAnimationUtil.editAreaAnimator(rootLayout, -from, 0.0F, 1.0F, 1.0F,false); this.editAreaIsUp = false; } } }
無
暫時空缺