-
-
Save grennis/2e3cd5f7a9238c59861015ce0a7c5584 to your computer and use it in GitHub Desktop.
public class SoftInputAssist { | |
private View rootView; | |
private ViewGroup contentContainer; | |
private ViewTreeObserver viewTreeObserver; | |
private ViewTreeObserver.OnGlobalLayoutListener listener = () -> possiblyResizeChildOfContent(); | |
private Rect contentAreaOfWindowBounds = new Rect(); | |
private FrameLayout.LayoutParams rootViewLayout; | |
private int usableHeightPrevious = 0; | |
public SoftInputAssist(Activity activity) { | |
contentContainer = (ViewGroup) activity.findViewById(android.R.id.content); | |
rootView = contentContainer.getChildAt(0); | |
rootViewLayout = (FrameLayout.LayoutParams) rootView.getLayoutParams(); | |
} | |
public void onPause() { | |
if (viewTreeObserver.isAlive()) { | |
viewTreeObserver.removeOnGlobalLayoutListener(listener); | |
} | |
} | |
public void onResume() { | |
if (viewTreeObserver == null || !viewTreeObserver.isAlive()) { | |
viewTreeObserver = rootView.getViewTreeObserver(); | |
} | |
viewTreeObserver.addOnGlobalLayoutListener(listener); | |
} | |
public void onDestroy() { | |
rootView = null; | |
contentContainer = null; | |
viewTreeObserver = null; | |
} | |
private void possiblyResizeChildOfContent() { | |
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds); | |
int usableHeightNow = contentAreaOfWindowBounds.height(); | |
if (usableHeightNow != usableHeightPrevious) { | |
rootViewLayout.height = usableHeightNow; | |
rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom); | |
rootView.requestLayout(); | |
usableHeightPrevious = usableHeightNow; | |
} | |
} | |
} | |
Anyone knows why it flickers when the keyboard is opened/closed? It's like the toolbar jumps down for a split second.
I'm using support library v28.0.0-alpha1.
How to use () -> possiblyResizeChildOfContent()
without lambda, because lambda need Java 8, but I am using Java 7!
@jpvs0101, I think, it is:
private ViewTreeObserver.OnGlobalLayoutListener listener;
public SoftInputAssist(Activity activity) {
...
listener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
};
}
Also in Activity
you should use (sorry, in Kotlin):
override fun onResume() {
super.onResume()
softInputAssist.onResume()
}
override fun onPause() {
softInputAssist.onPause()
super.onPause()
}
override fun onDestroy() {
softInputAssist.onDestroy()
super.onDestroy()
}
where softInputAssist = new SoftInputAssist(this);
.
In Kotlin (see also https://stackoverflow.com/a/42261118/2914140).
class SoftInputAssist(activity: Activity) {
private var rootView: View? = null
private var contentContainer: ViewGroup? = null
private var viewTreeObserver: ViewTreeObserver? = null
private val listener: () -> Unit
private val contentAreaOfWindowBounds = Rect()
private val rootViewLayout: FrameLayout.LayoutParams
private var usableHeightPrevious = 0
init {
contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
rootView = contentContainer!!.getChildAt(0)
rootViewLayout = rootView!!.layoutParams as FrameLayout.LayoutParams
listener = { possiblyResizeChildOfContent() }
}
fun onResume() {
if (viewTreeObserver == null || viewTreeObserver?.isAlive == false) {
viewTreeObserver = rootView?.viewTreeObserver
}
viewTreeObserver?.addOnGlobalLayoutListener(listener)
}
fun onPause() {
if (viewTreeObserver?.isAlive == true) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewTreeObserver?.removeOnGlobalLayoutListener(listener)
} else {
//noinspection deprecation
viewTreeObserver?.removeGlobalOnLayoutListener(listener)
}
}
}
fun onDestroy() {
rootView = null
contentContainer = null
viewTreeObserver = null
}
private fun possiblyResizeChildOfContent() {
contentContainer?.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
// Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.
rootView?.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top,
contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
rootView?.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
Anyone knows why it flickers when the keyboard is opened/closed? It's like the toolbar jumps down for a split second.
I'm using support library v28.0.0-alpha1.
Yaa, I see the flickering even with 27.1.1
Worked for me after I have replaced
int usableHeightNow = contentAreaOfWindowBounds.height();
with
int usableHeightNow = contentAreaOfWindowBounds.bottom;
otherwise I had a large gap at the bottom of the screen.
Not working.
Not working, I'm using ConstraintLayout
and it's doing nothing, no change at all.
This gist is over 3 years old and hacking around limitations in the framework at the time. I am not surprised to hear it doesn’t work properly anymore. I believe similar functionality is now available in androidx libs. I should probably just delete this.
For those, who found it not working, use solution in this comment: https://gist.github.com/grennis/2e3cd5f7a9238c59861015ce0a7c5584#gistcomment-2783151
fantastic. work still greatly in 2020. Two problem has remain. Flickering and bottom height. bottom height solve by int usableHeightNow = contentAreaOfWindowBounds.bottom; still has 10 dp gap in android pie but lower version it works.
This gist is over 3 years old and hacking around limitations in the framework at the time. I am not surprised to hear it doesn’t work properly anymore. I believe similar functionality is now available in androidx libs. I should probably just delete this.
For me with this solution, i see flicker on the action bar as well as it some what works as adjustResize, where it's bringing other views with input field above keyboard.
My layout is like this ->
Custom tool bar
Webview (containing the input field at the bottom of view)
Bottom bar(having some tab views as options)
I don't want the bottom bar to come above the keyboard when i click on input field within the webview, but with this solution bottom bar also jumps up the keyboard.
Note: I have used adjustPan with the activity as softInputMode, also if i use only adjustResize it does work very similar to what i see with adjustPan & this solution.
@grennis Can you please guide with the solution which androidx libs provides as you have mentioned?
@bhardwaj49 I think they mentioned it on the android developer backstage podcast, they said it will be a new framework feature but would likely have jetpack backport support.
The Activity can be set to adjustNothing in the AndroidManifest.xml. https://github.com/siebeprojects/samples-keyboardheight
This gist is over 3 years old and hacking around limitations in the framework at the time. I am not surprised to hear it doesn’t work properly anymore. I believe similar functionality is now available in androidx libs. I should probably just delete this.
For me with this solution, i see flicker on the action bar as well as it some what works as adjustResize, where it's bringing other views with input field above keyboard.
My layout is like this ->
Custom tool bar
Webview (containing the input field at the bottom of view)
Bottom bar(having some tab views as options)I don't want the bottom bar to come above the keyboard when i click on input field within the webview, but with this solution bottom bar also jumps up the keyboard.
Note: I have used adjustPan with the activity as softInputMode, also if i use only adjustResize it does work very similar to what i see with adjustPan & this solution.
@grennis Can you please guide with the solution which androidx libs provides as you have mentioned?
try replace
rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
to
rootView.layout(contentAreaOfWindowBounds.left - (int)contentContainer.getX(), contentAreaOfWindowBounds.top - (int)contentContainer.getY(), contentAreaOfWindowBounds.right - (int)contentContainer.getX(), contentAreaOfWindowBounds.bottom - (int)contentContainer.getY());
@mahbub-java @bhardwaj49 Were you able to find a final solution for the status bar flicker? Still experiencing the same issue here.
It's very frustrating have to do tricks because of framework's limitations. Any other solution?
How do we remove these window adjustments when moving to a new fragment? It works perfectly in the fragment where I have fullscreen mode but when I navigate back to the previous fragment which isnt full screen the bottom nav view is now hidden behind the device navigation. I guess whatever insets are being done in the full screen mode are carrying over to the prevouis fragment which doesnt require any window/inset modifications
I am able to implement this and the status bar is not flickering anymore, need to let it run in Handler when calling "possiblyResizeChildOfContent".
Not sure whether this works on anyone else, but give it a try.
The code below is in Kotlin.
class SoftInputAssist(activity: Activity) {
private var contentContainer: ViewGroup?
private var rootView: View?
private val rootViewLayout: FrameLayout.LayoutParams?
private val contentAreaOfWindowBounds: Rect = Rect()
private var viewTreeObserver: ViewTreeObserver? = null
private var usableHeightPrevious = 0
private val listener = OnGlobalLayoutListener {
possiblyResizeChildOfContent()
}
init {
contentContainer = activity.contentRootView as? ViewGroup
rootView = contentContainer?.getChildAt(0)
rootViewLayout = rootView?.layoutParams as? FrameLayout.LayoutParams
}
fun onPause() {
if (viewTreeObserver != null && viewTreeObserver!!.isAlive) {
viewTreeObserver?.removeOnGlobalLayoutListener(listener)
}
}
fun onResume() {
viewTreeObserver = rootView?.viewTreeObserver
if (viewTreeObserver != null) {
viewTreeObserver?.addOnGlobalLayoutListener(listener)
}
}
fun onDestroy() {
contentContainer = null
rootView = null
viewTreeObserver = null
}
private fun possiblyResizeChildOfContent() {
runOnMainThread {
contentContainer?.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow: Int = contentAreaOfWindowBounds.bottom
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout?.height = usableHeightNow
rootView?.layout(
contentAreaOfWindowBounds.left - (contentContainer?.x?.toInt() ?: 0),
contentAreaOfWindowBounds.top - (contentContainer?.y?.toInt() ?: 0),
contentAreaOfWindowBounds.right - (contentContainer?.x?.toInt() ?: 0),
contentAreaOfWindowBounds.bottom - (contentContainer?.y?.toInt() ?: 0)
)
rootView?.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
}
val Activity.contentRootView: View?
get() = window?.decorView?.findViewById(android.R.id.content) ?: findViewById(android.R.id.content)
private object ContextHandler {
val handler = Handler(Looper.getMainLooper())
}
fun runOnMainThread(action: () -> Unit) {
ContextHandler.handler.post {
action()
}
}
I have a view that is constanted to the container but when resizing the root view, that view disappears
it does not work to me i has error at line : private ViewTreeObserver.OnGlobalLayoutListener listener = () -> possiblyResizeChildOfContent();
please how can i resolve it . thanks
Thanks. It works perfectly.
it does not work to me i has error at line : private ViewTreeObserver.OnGlobalLayoutListener listener = () -> possiblyResizeChildOfContent(); please how can i resolve it . thanks
Use like this
private ViewTreeObserver.OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
};
This gist is over 3 years old and hacking around limitations in the framework at the time. I am not surprised to hear it doesn’t work properly anymore. I believe similar functionality is now available in androidx libs. I should probably just delete this.
Actually it works, it just flickers when keyboard size or visibility is changes.
I am able to implement this and the status bar is not flickering anymore, need to let it run in Handler when calling "possiblyResizeChildOfContent". Not sure whether this works on anyone else, but give it a try. The code below is in Kotlin.
class SoftInputAssist(activity: Activity) { private var contentContainer: ViewGroup? private var rootView: View? private val rootViewLayout: FrameLayout.LayoutParams? private val contentAreaOfWindowBounds: Rect = Rect() private var viewTreeObserver: ViewTreeObserver? = null private var usableHeightPrevious = 0 private val listener = OnGlobalLayoutListener { possiblyResizeChildOfContent() } init { contentContainer = activity.contentRootView as? ViewGroup rootView = contentContainer?.getChildAt(0) rootViewLayout = rootView?.layoutParams as? FrameLayout.LayoutParams } fun onPause() { if (viewTreeObserver != null && viewTreeObserver!!.isAlive) { viewTreeObserver?.removeOnGlobalLayoutListener(listener) } } fun onResume() { viewTreeObserver = rootView?.viewTreeObserver if (viewTreeObserver != null) { viewTreeObserver?.addOnGlobalLayoutListener(listener) } } fun onDestroy() { contentContainer = null rootView = null viewTreeObserver = null } private fun possiblyResizeChildOfContent() { runOnMainThread { contentContainer?.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds) val usableHeightNow: Int = contentAreaOfWindowBounds.bottom if (usableHeightNow != usableHeightPrevious) { rootViewLayout?.height = usableHeightNow rootView?.layout( contentAreaOfWindowBounds.left - (contentContainer?.x?.toInt() ?: 0), contentAreaOfWindowBounds.top - (contentContainer?.y?.toInt() ?: 0), contentAreaOfWindowBounds.right - (contentContainer?.x?.toInt() ?: 0), contentAreaOfWindowBounds.bottom - (contentContainer?.y?.toInt() ?: 0) ) rootView?.requestLayout() usableHeightPrevious = usableHeightNow } } } } val Activity.contentRootView: View? get() = window?.decorView?.findViewById(android.R.id.content) ?: findViewById(android.R.id.content) private object ContextHandler { val handler = Handler(Looper.getMainLooper()) } fun runOnMainThread(action: () -> Unit) { ContextHandler.handler.post { action() } }
For me this solution worked perfectly with windowSoftInputMode= adjustPan
and then adjusting my NestedScrollView's parent's top, start, end, and bottom constraints with the parent with 0dp
height
thanks.
Worked for me after I have replaced
int usableHeightNow = contentAreaOfWindowBounds.height();
with
int usableHeightNow = contentAreaOfWindowBounds.bottom;
otherwise I had a large gap at the bottom of the screen.
Thanks Bro it was a problem for me. But a little bit problem while launching the activity, splash like problem. If you found an answer please email me. [email protected]. I'll be waiting to your answer.
Can this be used in fragment...
Worked excellently.