English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Preface
After developing for a long time, it is inevitable to encounter various pitfalls.
On the road of Android development, the pit of 'the soft keyboard blocks the input box' can be said to be a long-standing giant pit —— Let's take a look slowly.
Introduction
The most basic situation, as shown in the figure: there is an EditText at the bottom of the page. If no processing is done, it may block the EditText when the soft keyboard pops up.
The handling of this situation is actually very simple, you just need to set the activity in the AndroidManifest file: the value of android:windowSoftInputMode to adjustPan or adjustResize, like this:
<activity> android:name=".MainActivity" android:windowSoftInputMode="adjustPan" > ... </activity>
Generally speaking, they can solve the problem, of course, the effect of adjustPan and adjustResize is slightly different.
adjustPan is to shift the entire interface upwards, so that the input box is exposed without changing the layout of the interface;
adjustResize is to recalculate the size of the interface after the soft keyboard pops up, which is equivalent to displaying content with less interface area, so the input box is naturally included.
↑↑↑ OK, this is just an introduction, basically all Android engineers on Earth can handle it.
Don't hurry, see below~
Try adding WebView? The pit comes……
In the previous tutorial, the soft keyboard was triggered to pop up by the native EditText. In the H5When Hybrid has almost become the standard configuration of App, we often encounter the following situations: the soft keyboard is triggered to pop up by web elements in the WebView.
Situation Description
At this point, the situation will become more complex:
Firstly, when the page is not in full-screen mode, setting adjustPan for the activity will fail.
Secondly, when the page is in full-screen mode, both adjustPan and adjustResize will fail.
—— To explain, the full-screen mode here refers to the page being full-screen, including the Application or activity using the Fullscreen theme, using 'status color shading', 'immersive status bar', 'Immersive Mode', etc.—— in short, basically, as long as the App takes control of the status bar, this kind of problem will occur.
The following table can simply list the specific situations.
Why is it said to be a pit? “issue 5497
The situation in the above table is not what Google expects, the ideal situation would be that they all work normally——so this is actually a BUG in the Android system itself.
Why did the article say it's a pit at the beginning?
—— because this BUG has been from Android1.x era (2009Year) was reported, and it has been until the current Android7.0 (2016Year) has not been fixed yet.../(\^o^\)/
It can be said that this is not only a pit, but also an official pit~
“issue 5497”,Details page:9758; Issue 5497 - android -WebView adjustResize windowSoftInputMode breaks when activity is fullscreen - Android Open Source Project - Issue Tracker - Google Project Hosting
Of course, no matter who dug the pit, it is ultimately the developer who has to solve it.
After encountering the pit, there are two ways to pass through: dodge or fill.
Dodge posture
As shown in the previous text, the conditions for the pit are: the activity with WebView uses full-screen mode or adjustPan mode.
So the posture of dodging the pit is very simple -
If there is a WebView in the activity, do not use full-screen mode, and set its windowSoftInputMode value to adjustResize.
How about, isn't it simple?
But there are always times when you need to have full-screen mode and WebView at the same time. At this point, dodging the pit is not enough, and we need a new way to fill the gap. Fortunately, the wisdom of developers is infinite, and this pit has appeared for so many years, and still some solutions have been found.
AndroidBug5497Workaround
In my opinion, the best solution is this:AndroidBug5497WorkaroundIt only needs a magical AndroidBug5497Workaround class.
As the name suggests, it is specifically designed to deal with5497The steps to use it are also extremely simple:
Place AndroidBug5497Copy the Workaround class to the project.
Add an AndroidBug in the onCreate method of the activity that needs to fill the gap.5497Just call Workaround.assistActivity(this).
After testing, it is basically available on all Android versions, and the effect is basically the same as setting adjustResize.
See a comparison image:
From an activity page using WebView in full-screen mode of our factory App, from left to right are: style without soft keyboard, effect of soft keyboard blocking the input box, and using AndroidBug5497The final effect after Workaround.
What is its principle?
This cool AndroidBug5497The Workaround class is not very complex at all, just a few dozen lines of code, let's post it here first:
public class AndroidBug5497Workaround { // For more information, see https://code.google.com/p/android/issues/detail?id=5497 // To use this class, simply invoke assistActivity() on an Activity that already has its content view set. public static void assistActivity (Activity activity) { new AndroidBug5497Workaround(activity); } private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private AndroidBug5497Workaround(Activity activity) { FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { possiblyResizeChildOfContent(); } }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard/4)) { // The keyboard probably just became visible frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } // The keyboard probably just became hidden frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } } private int computeUsableHeight() { Rect r = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(r); return (r.bottom - r.top);// In full-screen mode: return r.bottom } }
The code roughly does the following things:
1.Find the root View of activity
Let's take a look at the entry code:
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0);
Among them, the View pointed by android.R.id.content in the first line is the root View that developers can control on all Activity interfaces of Android.
If the Activity is in full-screen mode, then android.R.id.content is full of the entire screen area.
If the Activity is a normal non-fullscreen mode, then android.R.id.content is full of all the areas except the status bar.
Other cases, such as Activity being a popup, or7.0 and later split-screen styles, android.R.id.content is also the range of pop-up windows or the half screen of split-screen——these cases are less common, so let's not consider them for now.
We often use setContentView(View view)/setContent(int layRes) is actually putting the specified View or layRes into android.R.id.content, making it its child View.
So, then, the second line content.getChildAt(0) gets mChildOfContent, which is actually used to get the View we put in with setContentView.
2.Set up a Listener to monitor changes in the View tree
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener({ //Simplified syntax possiblyResizeChildOfContent(); });
View.getViewTreeObserver() can obtain a ViewTreeObserver object——this object is an observer, specifically used to listen to some changes in the current View tree. The addOnGlobalLayoutListener registered here will trigger a notification callback when the global layout (GlobalLayout) of the current View tree changes, or when the visibility of the Views within it changes.
——'Soft keyboard popping up' is a source that triggers this event. (Soft keyboard popping up will change GlobalLayout)
That is to say, now we can listen to the event of 'soft keyboard popping up'.
3After the interface changes, get the 'available height'
After the soft keyboard pops up, the next thing is to get the available height of the changed interface (which can be used by developers to display content).
Let's take a look at the code:
private int computeUsableHeight() { Rect rect = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(rect); // rect.top is actually the height of the status bar. If it is a full-screen theme, you can directly return rect.bottom. return (rect.bottom - rect.top); }
The code View.getWindowVisibleDisplayFrame(Rect rect) can obtain the Rect - which is the rectangular area of the interface minus the title bar and the part blocked by the soft keyboard, as shown in the figure, the area in the red box.
Illustration of Rect area
It can also be seen that:
The value of rect.top is actually the height of the status bar. (In fact, it is often used as a method to obtain the height of the status bar)
Screen height-rect.bottom is the height of the soft keyboard. (The method to obtain the height of the soft keyboard also appeared)
At this point, there is:
In full screen mode, the usable height = rect.bottom
In non-full screen mode, the usable height = rect.bottom - rect.top
4.The final step is to reset the height
The usable height we calculate is the interface height that can be seen visually at the moment. However, the actual height of the current interface is longer than the usable height by the distance of the soft keyboard.
So, the final step is to set the interface height to the usable height - the job is done.
private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard/4)) { // The keyboard probably just became visible frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } // The keyboard probably just became hidden frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } }
The above code adds a "heightDifference > (usableHeightSansKeyboard/4judgment, this is to eliminate unnecessary interference. Because there are many reasons that can trigger the OnGlobalLayout event, not just the change of the soft keyboard pop-up, including various changes in the hiding and showing of various subViews, which have a limited impact on the interface height. With this judgment, only when the change in the interface height exceeds1/4of the screen height, the height will be reset, which can basically ensure that the code only responds to the pop-up of the soft keyboard.
Summary
Summing up, it is like this:
Normal Activity (without WebView), directly use adjustpan or adjustResize
If it includes WebView:
a) If it is not in full-screen mode, you can use adjustResize
b) If it is in full-screen mode, use AndroidBug5497Workaround for processing.
The above-mentioned is the ultimate solution to the problem of the Android soft keyboard blocking the input box introduced by the editor to everyone. I hope it will be helpful to everyone. If you have any questions, please leave a message, and the editor will reply to everyone in time. I also want to express my heartfelt thanks to everyone for their support of the Yell Tutorial website!
Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been edited by humans, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (when sending an email, please replace # with @ to report, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.)