The status bar and the screen moves up completely when the keyboard popsUp in xamarin forms android and the EditText field is in the bottom of the screen. I tried using
WindowSoftInputMode = SoftInput.AdjustPan
and
WindowSoftInputMode = SoftInput.AdjustResize
But unfortunately both are not working,i also clubbed both
From a blog post i read putting
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
and
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
{
Window.DecorView.SystemUiVisibility = 0;
var statusBarHeightInfo = typeof(FormsAppCompatActivity).GetField("_statusBarHeight", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
statusBarHeightInfo.SetValue(this, 0);
Window.SetStatusBarColor(new Android.Graphics.Color(0,0,0, 255)); // Change color as required.
}
after launch application is an alternative, but unfortunately this also failed. Any other option available?
Its a bug in Xamarin. I used following code in mainActivity
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
{
Window.DecorView.SystemUiVisibility = 0;
var statusBarHeightInfo = typeof(FormsAppCompatActivity).GetField("_statusBarHeight", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
statusBarHeightInfo.SetValue(this, 50);
}
And used
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
The problem was it wont work if you forcefully hide the title bar
Forms.SetTitleBarVisibility(AndroidTitleBarVisibility.Never);
I commented out this code and the issue was solved.
But due to resize property i faced lot of issue since i have designed screens with Grid and star value which caused many unwanted issues.
So i am not going to use this method sadly. :(
Related
My app worked fine for lots of devices. But since upgrading to Android 12 on my own Pixel the following happens when calling showSoftInput or just when tapping the AppCompatEditText in a Bottomsheet.
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager;
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
Logcat warning (nothing happens in the app):
Ignoring showSoftInput() as view=androidx.appcompat.widget.AppCompatEditText{b5311a0 VFED..CL. .F.P..ID 84,0-996,118 #7f0900a7 app:id/et_bottomsheet aid=1073741827} is not served.
I tried lots of things like requesting focus, showSoftInput with SHOW_FORCE but nothing worked.
Starting from Android 11 (API 30) you can manually force the ime/keyboard to show with inset's API show()
myAppCompatEditText.windowInsetsController.show(WindowInsetsCompat.Type.ime())
And hide it with:
myAppCompatEditText.windowInsetsController.hide(WindowInsetsCompat.Type.ime())
To targed APIs below API 30, This is backported using the Compat version:
WindowInsetsControllerCompat(window, myAppCompatEditText)
.show(WindowInsetsCompat.Type.ime())
WindowInsetsControllerCompat(window, myAppCompatEditText)
.hide(WindowInsetsCompat.Type.ime())
Solution:
The problem seems to be that the keyboard couldn't gain window focus?
Anyways, the parts that were causing problems were:
window?.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
window?.decorView?.systemUiVisibility = fullscreenFlags
and
private const val fullscreenFlags = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
I removed them for now on SDK 33+, which kind of breaks the hiding of navigation elements that I had before but it's the only way I could fix this quickly.
Now everything seems to work.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) doThose()
Per the android docs, we can make an activity go full-screen by adding the following block of code in the onCreate method (inside setContent{...}, specifically, if you are using Compose)
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
However, this seems to only hide the information displayed on the statusbar, replacing it with just a black stripe.
Now, my question is - How can we modify the color of this stripe so that the UI of the app seems to extend to the entire display?
I am not sure where to start but I've heard accompanist MAY have something related to this, but I thought it better to post this question here, so that if anyone already knows a way around, they may share since it will be helpful to the community.
Other than that, solutions that do not involve accompanist are also welcome, and may be even preferred.
For reference, here's the output as of now
Notice the black bar at the top? That's the target.
copy and paste it in onCreate method after result you can understand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window: Window = window
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
val decorView: View = window.getDecorView()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
window.setStatusBarColor(Color.TRANSPARENT)
}
I have a Xamarin.Forms app being built for iOS and Android.
I'm having some difficulty in Android updating the icon colors when setting the status bar color. I have this working for API levels below 30 using the following code:
var isLight = false;
Window currentWindow = Platform.CurrentActivity.Window;
if (Color.FromHex(hexColor).Luminosity > 0.5)
{
isLight = true;
}
currentWindow.SetStatusBarColor(androidColor);
currentWindow.DecorView.SystemUiVisibility = isLight ? (StatusBarVisibility)(SystemUiFlags.LightStatusBar) : 0;
From what I can tell, DecorView.SystemUiVisibility is deprecated in API 30, and is supposed to be replaced with window.insetsController
What I can't figure out is if/where this API is exposed in Xamarin for me to use.
I looked at this SO question:
How to change the status bar color without a navigation page
and following the last answer, I attempted to use:
var lightStatusBars = isLight ? WindowInsetsControllerAppearance.LightStatusBars : 0;
currentWindow.InsetsController?.SetSystemBarsAppearance((int)lightStatusBars, (int)lightStatusBars);
but it will not build, saying Window doesn't have InsetsController
Has anyone figured this out? I definitely need to support the latest Android and this feature is killing me
Thanks in advance!
Your code looks correct. Change target framework to Android 11.0 (R). InsetsController was added in API level 30. Due to this you may receive build error.
public void UpdateStatusBarColor(String color)
{
Window.SetStatusBarColor(Color.ParseColor(color));
if (Build.VERSION.SdkInt >= BuildVersionCodes.R)
{
Window?.InsetsController?.SetSystemBarsAppearance((int)WindowInsetsControllerAppearance.LightStatusBars, (int)WindowInsetsControllerAppearance.LightStatusBars);
}
else
{
#pragma warning disable CS0618
Window.DecorView.SystemUiVisibility = (StatusBarVisibility)SystemUiFlags.LightStatusBar;
#pragma warning restore CS0618
}
}
Can you please try this in MainActivity.cs
$ Window.SetStatusBarColor(Android.Graphics.Color.Argb(255, 114, 75, 203));
I'm trying to update my App to Android 11. Many Screens of my App were Designed with App Content behind the StatusBar. I Updated my gradle to Android 11 and started updating the Window code to get the No Limit behavior also for Android 11 Devices.
I achived my desired result for pre Android 11 Devices with the folowing Code in my Activitiys onCreate method:
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
I tried to get the same no limit behavior for Android 11 by using w.setDecorFitsSystemWindows(false);
I tried using it instead of using the flags, using it with flags and passing true and false, setting it before and after setting the flags but i always see a white status and system navigation bar instead of my Apps content behind them.
What i tried:
Window window = getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false); //also tried with true
} else {
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
//or
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false); //also tried with true
}
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
//or
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false); //also tried with true
}
My App still is in Java Code, i tried window?.setDecorFitsSystemWindows(false) in another app which uses Kotlin code and it worked without any troubles.
Does anyone have an idea what i'm missing or doing wrong here?
My App still is in Java Code, i tried window?.setDecorFitsSystemWindows(false) in another app which uses Kotlin code and it worked without any troubles.
Kotlin internally uses Java, so it does not matter whether you code in Java or Kotlin in terms of that insets API.
Probably, one of the view groups, which you use in your view hierarchy, consumes insets and does not propagate them to the child views. This was my case - my app is using DebugDrawer and window.setDecorFitsSystemWindows(false); didn't have any effect on the layout in my case. There is even the solution for that case. Maybe you could use it too, if you have a similar problem. It could be helpful to check the view hierarchy in the new Layout Inspector of Android Studio. Pay attention to the views that have attribute fitsSystemWindows = true. Also, that answer could be helpful.
For android 11 along with flags we also need to add below styles in the app theme
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
I have an Activity with a RecyclerView in a data binding layout. RecyclerView takes up the whole screen, and looking at making the UX go full screen, drawn under the status and nav bars.
I'm calling setSystemUiVisibility in activity's onCreate as below.
window.decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
)
Now the RecyclerView is drawn under the system bars, so I want to make sure it has enough padding so the items don't overlap with the system UI.
I found 2 ways of doing this, via a BindingAdapter.
Option 1
var statusBar = 0
var resourceId = view.resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
statusBar = view.resources.getDimensionPixelSize(resourceId)
}
var navBar = 0
resourceId = view.resources.getIdentifier("navigation_bar_height", "dimen", "android")
if (resourceId > 0) {
navBar = view.resources.getDimensionPixelSize(resourceId)
}
view.setPadding(0, statusBar, 0, navBar)
Option 2
var insets = view.rootWindowInsets.stableInsets
view.setPadding(0, insets.top, 0, insets.bottom)
I prefer the first, because it (with limited testing on emulators seems to) work on API 21, 28 and 29.
Option 2 only works on API 29, and also seems to get null on view.rootWindowInsets if/when the view is not attached. (So I guess I have to add a listener and wait for it to be attached before doing this)
So my question is, is there a down side to Option 1? Can I use it over the new API in 29? Is there any scenarios that Option 1 would not work?
(I think Option 1 might not work well on tablets where both nav and systems bars are on the bottom, so extra padding will be applied to the wrong side.)
A little bit late to the party, but here is the way that I've been doing, someone might need it.
For Android M and above, you can call View#rootWindowInsets directly, otherwise rely on Java's Reflection to access the private field mStableInsets
fun getStableInsets(view: View): Rect {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val windowInsets = view.rootWindowInsets
if (windowInsets != null) {
Rect(windowInsets.stableInsetLeft, windowInsets.stableInsetTop,
windowInsets.stableInsetRight, windowInsets.stableInsetBottom)
} else {
// TODO: Edge case, you might want to return a default value here
Rect(defaultInsetLeft, defaultInsetTop, defaultInsetRight, defaultInsetBottom)
}
} else {
val attachInfoField = View::class.java.getDeclaredField("mAttachInfo")
attachInfoField.isAccessible = true
val attachInfo = attachInfoField.get(view);
if (attachInfo != null) {
val stableInsetsField = attachInfo.javaClass.getDeclaredField("mStableInsets")
stableInsetsField.isAccessible = true
Rect(stableInsetsField.get(attachInfo) as Rect)
} else {
// TODO: Edge case, you might want to return a default value here
Rect(defaultInsetLeft, defaultInsetTop, defaultInsetRight, defaultInsetBottom)
}
}
}
Update:
stableInsetBottom .etc. are now deprecated with message
Use {#link #getInsetsIgnoringVisibility(int)} with {#link Type#systemBars()}
* instead.
Unfortunately systemBars() was graylisted in API 29 and is blacklisted in API 30 plus using this seems to work on API 30 emulator, however (some) real devices even running API 29 throws.
Below is logcat from Galaxy S20 FE
Accessing hidden method Landroid/view/WindowInsets$Type;->systemBars()I (blacklist, linking, denied)
2021-01-17 01:45:18.348 23013-23013/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: test.app.package, PID: 23013
java.lang.NoSuchMethodError: No static method systemBars()I in class Landroid/view/WindowInsets$Type; or its super classes (declaration of 'android.view.WindowInsets$Type' appears in /system/framework/framework.jar!classes3.dex)
No answer for this it seems. Please put an answer if you find anything not covered below.
Using Option 1 I noticed on devices that do OEM specific gesture navigation atleast, when those gesture modes are active, above will still return full navigation bar height even though no visible navigation bar is present. So above will still pad the UI when it shouldn't.
Option 2 keeps returning null for insets until the view is attached so if you're doing this on a BindingAdapter, it won't work. It needs to be called after the view is attached.
My current solution is as below.
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
view.doOnAttach {
var bottom = it.rootWindowInsets?.stableInsetBottom?: 0
var top = it.rootWindowInsets?.stableInsetTop?: 0
view.setPadding(0, top, 0, bottom)
}
}
else {
// use option1, old devices don't have custom OEM specific gesture navigation.
// or.. just don't support versions below Android M ¯\_(ツ)_/¯
}
Caveats
Some OEMs (well atleast OnePlus) decided not to restart some activities, especially ones that are paused, when the navigation mode changed. So if the user decides to switch away from your app, change the navigation mode and return, your app may still overlap navigation bar until the activity is restarted.