Android監(jiān)聽(tīng)鍵盤(pán)狀態(tài)獲取鍵盤(pán)高度的實(shí)現(xiàn)方法
前言
Android暫時(shí)還沒(méi)有提供一個(gè)合適的API來(lái)獲取/監(jiān)聽(tīng)鍵盤(pán)的狀態(tài)和高度 , 而我們又經(jīng)常會(huì)有這個(gè)需求.
最近我的一個(gè)項(xiàng)目中,在ugc頁(yè)面需要在鍵盤(pán)頂部,緊貼著鍵盤(pán)顯示一個(gè)文字提示,當(dāng)鍵盤(pán)消失時(shí)就隱藏.
因此,我需要監(jiān)聽(tīng)軟鍵盤(pán)的打開(kāi)/關(guān)閉 , 以及獲取它的高度.
ViewTreeObserver
A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change…
Android框架提供了一個(gè)ViewTreeObserver類(lèi),它是一個(gè)View視圖樹(shù)的觀察者類(lèi)。ViewTreeObserver類(lèi)中定義了一系列的公共接口(public interface)。當(dāng)一個(gè)View attach到一個(gè)窗口上時(shí)就會(huì)創(chuàng)建一個(gè)ViewTreeObserver對(duì)象,這樣當(dāng)一個(gè)View的視圖樹(shù)發(fā)生改變時(shí),就會(huì)調(diào)用該對(duì)象的某個(gè)方法,將事件通知給每個(gè)注冊(cè)的監(jiān)聽(tīng)者。
OnGlobalLayoutListener是ViewTreeObserver中定義的眾多接口中的一個(gè),它用來(lái)監(jiān)聽(tīng)一個(gè)視圖樹(shù)中全局布局的改變或者視圖樹(shù)中的某個(gè)視圖的可視狀態(tài)的改變。當(dāng)軟鍵盤(pán)由隱藏變?yōu)轱@示,或由顯示變?yōu)殡[藏時(shí),都會(huì)調(diào)用當(dāng)前布局中所有存在的View中的ViewTreeObserver對(duì)象的dispatchOnGlobalLayout()方法,此方法中會(huì)遍歷所有已注冊(cè)的OnGlobalLayoutListener,執(zhí)行相應(yīng)的回調(diào)方法,將全局布局改變的消息通知給每個(gè)注冊(cè)的監(jiān)聽(tīng)者。
view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
getWindowVisibleDisplayFrame
Retrieve the overall visible display size in which the window this view is attached to has been positioned in.
getWindowVisibleDisplayFrame()會(huì)返回窗口的可見(jiàn)區(qū)域高度,通過(guò)和屏幕高度相減,就可以得到軟鍵盤(pán)的高度了。
完整示例代碼
package com.cari.cari.promo.diskon.util; import android.content.Context; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.ViewTreeObserver; import java.util.LinkedList; import java.util.List; public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener { public interface SoftKeyboardStateListener { void onSoftKeyboardOpened(int keyboardHeightInPx); void onSoftKeyboardClosed(); } private final List<SoftKeyboardStateListener> listeners = new LinkedList<>(); private final View activityRootView; private int lastSoftKeyboardHeightInPx; private boolean isSoftKeyboardOpened; private Context mContext; //使用時(shí)用這個(gè)構(gòu)造方法 public SoftKeyboardStateWatcher(View activityRootView, Context context) { this(activityRootView, false); this.mContext = context; } private SoftKeyboardStateWatcher(View activityRootView) { this(activityRootView, false); } private SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) { this.activityRootView = activityRootView; this.isSoftKeyboardOpened = isSoftKeyboardOpened; activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override public void onGlobalLayout() { final Rect r = new Rect(); activityRootView.getWindowVisibleDisplayFrame(r); final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top); if (!isSoftKeyboardOpened && heightDiff > dpToPx(mContext, 200)) { isSoftKeyboardOpened = true; notifyOnSoftKeyboardOpened(heightDiff); } else if (isSoftKeyboardOpened && heightDiff < dpToPx(mContext, 200)) { isSoftKeyboardOpened = false; notifyOnSoftKeyboardClosed(); } } public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) { this.isSoftKeyboardOpened = isSoftKeyboardOpened; } public boolean isSoftKeyboardOpened() { return isSoftKeyboardOpened; } /** * Default value is zero {@code 0}. * * @return last saved keyboard height in px */ public int getLastSoftKeyboardHeightInPx() { return lastSoftKeyboardHeightInPx; } public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) { listeners.add(listener); } public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) { listeners.remove(listener); } /** * @param keyboardHeightInPx 可能是包含狀態(tài)欄的高度和底部虛擬按鍵的高度 */ private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) { this.lastSoftKeyboardHeightInPx = keyboardHeightInPx; for (SoftKeyboardStateListener listener : listeners) { if (listener != null) { listener.onSoftKeyboardOpened(keyboardHeightInPx); } } } private void notifyOnSoftKeyboardClosed() { for (SoftKeyboardStateListener listener : listeners) { if (listener != null) { listener.onSoftKeyboardClosed(); } } } private static float dpToPx(Context context, float valueInDp) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics); } }
可以看到, 我建了一個(gè)自己的一個(gè)Listener , 通過(guò)這個(gè)listener實(shí)現(xiàn)我們想要的監(jiān)聽(tīng) , 然后在這里處理一些邏輯問(wèn)題.
主要代碼還是在onGlobalLayout中:
首先通過(guò)activityRootView.getWindowVisibleDisplayFrame(r)
檢索此視圖所附加的窗口所在的整個(gè)可見(jiàn)顯示大小 ,然后減去,已顯示的視圖的高度 ,(r.bottom - r.top)就是顯示的view的下坐標(biāo)和上坐標(biāo),差即為高度.
至此,我們得到了剩余的高度 . 這個(gè)高度可能就是鍵盤(pán)高度了, 為什么說(shuō)可能呢?因?yàn)檫€么有考慮到頂部的狀態(tài)欄和底部的虛擬導(dǎo)航欄. 當(dāng)然也可能不是鍵盤(pán).
然后我們根據(jù)這個(gè)高度和之前已知的鍵盤(pán)狀態(tài)來(lái)判斷是否為鍵盤(pán).
并回調(diào)給監(jiān)聽(tīng)者.
使用
ScrollView scrollView = findViewById(R.id.ugc_scrollview); final SoftKeyboardStateWatcher watcher = new SoftKeyboardStateWatcher(scrollView, this); watcher.addSoftKeyboardStateListener( new SoftKeyboardStateWatcher.SoftKeyboardStateListener() { @Override public void onSoftKeyboardOpened(int keyboardHeightInPx) { ConstraintLayout.LayoutParams layoutParamsVideo = (ConstraintLayout.LayoutParams) mError1000tv.getLayoutParams(); layoutParamsVideo.setMargins(0, 0, 0, keyboardHeightInPx - ScreenUtils.getStatusHeight(UGCEditActivity.this) - ScreenUtils.getBottomStatusHeight(UGCEditActivity.this)); } @Override public void onSoftKeyboardClosed() { mError1000tv.setVisibility(View.GONE); } } );
Scrollview是整個(gè)頁(yè)面的根布局, 我通過(guò)監(jiān)聽(tīng)它來(lái)實(shí)現(xiàn)對(duì)整個(gè)布局的監(jiān)聽(tīng).
mError1000tv就是我一開(kāi)始提到的要緊貼鍵盤(pán)頂部顯示的一個(gè)textview了.
我通過(guò)LayoutParams給它設(shè)置邊距 , 只設(shè)置了底部邊距 , 值為返回的"鍵盤(pán)高度"- 頂部狀態(tài)欄高度-虛擬導(dǎo)航欄的高度. 得到真實(shí)的鍵盤(pán)高度.
在onSoftKeyboardOpened和onSoftKeyboardClosed這兩個(gè)回調(diào)中, 處理自己的邏輯就好了.
然后放上我這邊屏幕工具類(lèi)ScreenUtils的代碼, 需要的可以復(fù)制下來(lái)
ScreenUtils
package com.cari.promo.diskon.util; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.Display; import android.view.View; import android.view.WindowManager; import java.lang.reflect.Method; public class ScreenUtils { private ScreenUtils() { /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } /** * 獲得屏幕高度 * * @param context * @return */ 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; } /** * 獲得屏幕寬度 * * @param context * @return */ 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; } /** * 獲得狀態(tài)欄的高度 * * @param context * @return */ 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; } /** * 獲取當(dāng)前屏幕截圖,包含狀態(tài)欄 * * @param activity * @return */ 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; } /** * 獲取當(dāng)前屏幕截圖,不包含狀態(tài)欄 * * @param activity * @return */ 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; } /** * 獲取 虛擬按鍵的高度 * * @param context 上下文 * @return 虛擬鍵高度 */ public static int getBottomStatusHeight(Context context) { int totalHeight = getAbsoluteHeight(context); int contentHeight = getScreenHeight(context); return totalHeight - contentHeight; } /** * 獲取屏幕原始尺寸高度,包括虛擬功能鍵高度 * * @param context 上下文 * @return The absolute height of the available display size in pixels. */ private static int getAbsoluteHeight(Context context) { int absoluteHeight = 0; WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = null; if (windowManager != null) { display = windowManager.getDefaultDisplay(); } DisplayMetrics displayMetrics = new DisplayMetrics(); @SuppressWarnings("rawtypes") Class c; try { c = Class.forName("android.view.Display"); @SuppressWarnings("unchecked") Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); method.invoke(display, displayMetrics); absoluteHeight = displayMetrics.heightPixels; } catch (Exception e) { e.printStackTrace(); } return absoluteHeight; } }
全文完.
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)我們的支持。
上一篇:沒(méi)有了
欄 目:Android
下一篇:Android中實(shí)現(xiàn)長(zhǎng)按照片彈出右鍵菜單功能的實(shí)例代碼
本文標(biāo)題:Android監(jiān)聽(tīng)鍵盤(pán)狀態(tài)獲取鍵盤(pán)高度的實(shí)現(xiàn)方法
本文地址:http://mengdiqiu.com.cn/a1/Android/8967.html
您可能感興趣的文章
- 01-10Android自定義View之繪制圓形頭像功能
- 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方法詳解
- 01-10android實(shí)現(xiàn)記住用戶名和密碼以及自動(dòng)登錄
- 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
- 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代碼
- 01-10android實(shí)現(xiàn)指紋識(shí)別功能
- 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳解
- 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
- 01-10android開(kāi)發(fā)環(huán)境中SDK文件夾下的所需內(nèi)容詳解
- 01-10android異步消息機(jī)制 源碼層面徹底解析(1)


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10Android自定義View之繪制圓形頭像功能
- 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方
- 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
- 01-10android實(shí)現(xiàn)記住用戶名和密碼以及自動(dòng)
- 01-10C++自定義API函數(shù)實(shí)現(xiàn)大數(shù)相乘算法
- 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代
- 01-10android實(shí)現(xiàn)指紋識(shí)別功能
- 01-10如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
- 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
- 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10delphi制作wav文件的方法
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載