I have a Java app (API 23). In it I have a MainActivity and an AccessibilityService. In the AccessibilityService:
#Override
public void onServiceConnected() {
oView = new LinearLayout(this);
oView.setBackgroundColor(0x88ff0000); // The translucent red color
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width,
height,
xPos, yPos,
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT
);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(oView, params);
}
I need to adjust the width, height, xPos and yPos so it covers the whole screen including the status bar and navigation bar. That means I have to find the dimensions for the status bar, screen, and navigation bar (I can work out the values I need once I have those).
I found Height of statusbar? - best answer seemed to be
ViewCompat.setOnApplyWindowInsetsListener(rootView, new OnApplyWindowInsetsListener() {
#Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
//THIS is the value you want.
int statusBarHeight = insets.getSystemWindowInsetTop();
int navigationBar = insets.getSystemWindowInsetBottom();
// Let the view handle insets as it likes.
return ViewCompat.onApplyWindowInsets(v, insets);
}
});
But what rootView can I use, since there's no guarantee my Activity will be visible? And do I have to move the creation of the view from onServiceConnected() to inside onApplyWindowInsets()?
Is this really the best way to do this?
Follow up questions to bear in mind for the above - depending on the above and whether it answers them too, I will ask here or in a separate question:
What happens when the screen rotates? What if the status bar and/or navigation bar disappear (for example, if it goes fullscreen)? Seems like I need to handle that, re-read the heights, and re-create the view. Any thoughts or guidance on how to do that would be welcomed.
=== Update - Progress so far ===
I thought I only needed screen size plus info about the status bar (size, location), but Navigation Bar is sometimes on the left side of the screen so I need that, and Keyboard might affect things too.
I can get screen size with
Display myDisplay = wm.getDefaultDisplay();
Display.Mode mode = myDisplay.getMode();
int height = mode.getPhysicalHeight();
int width = mode.getPhysicalWidth();
I'm still working on the various location and sizes. I tried creating a view and then reading insets but it gave me the wrong answers. So unless a better answer arises I will probably have to use
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
But that can give wrong answers in some cases if Status bar is hidden, and it doesn't tell me where on the screen it is. I'm still working on that; got a few things to try, so we'll see.
Handling rotation in a service can be done using onConfigurationChanged() - see How do I use a service to monitor Orientation change in Android
Hope that helps someone - please, desperately searching for those final pieces.
Update: There's a problem when the screen is rotated upside-down. See Find placement of Android Navigation Bar without Insets (inside Service)
I'm currently working on a solution with my own research combined with https://stackoverflow.com/a/55348825/1910690
But I really wish I could just use Insets, or something like that.
==============
I tried a bunch of things that seemed to fail because I didn't have an activity; it's possible if I had poked around some more I would have made it work with Insets, and I still hope for an answer using them as they are more official and allow for notches etc.
In the meantime I finally pieced various answers together to make the following. It would probably work for a normal Service as well as an AccessibilityService.
The point is that at the end of onGlobalLayout() inside CreateLayoutHelperWnd() you have the complete information on screen size, orientation, status bar and navigation bar visibility and size, and you know that either you've just started your service or something changed. I found that it gets called twice sometimes even though nothing changed, probably because sometimes it does 'hide status+nav bar' and then 'change orientation' (same results for both), and sometimes it does it the other way around (different results) so I have wrapped the results in a class, and I compare the new results with the results from the last time and only do something if a result changed (that's why I use so many global variables, because those got moved later to the special class).
Please note that this is my first serious foray into Android and Java, so apologies if it's badly done - comments welcomed. It also doesn't handle error checking (eg StatusBar not found). Also note that I have not tested this extensively across multiple devices, I have no idea what it does with folding devices, multiple displays, or even split-screen; but I tried to account for the issues I saw in the various solutions (eg reading Status Bar height sometimes gives the wrong answer when it's closed on some devices, so I try to check if it is open or closed). I'll update if needed as I test more.
In the AccessibilityService:
// global variables
int statusBarHeight = -1;
int navigationBarHeight = -1;
int screenWidth = -1;
int screenHeight = -1;
View layoutHelperWnd;
#Override
public void onServiceConnected() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
CreateLayoutHelperWnd(wm);
}
public void onUnbind() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
DestroyLayoutHelperWnd(wm);
}
private void DestroyLayoutHelperWnd(WindowManager wm) {
wm.removeView(layoutHelperWnd);
}
private void CreateLayoutHelperWnd(WindowManager wm) {
final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.type = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
p.gravity = Gravity.RIGHT | Gravity.TOP;
p.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
p.width = WindowManager.LayoutParams.MATCH_PARENT;
p.height = WindowManager.LayoutParams.MATCH_PARENT;
p.format = PixelFormat.TRANSPARENT;
layoutHelperWnd = new View(this); //View helperWnd;
wm.addView(layoutHelperWnd, p);
final ViewTreeObserver vto = layoutHelperWnd.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// catches orientation change and fullscreen/not fullscreen change
// read basic screen setup - not sure if needed every time, but can't hurt
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
ReadScreenDimensions(wm); // sets up screenWidth and screenHeight
statusBarHeight = GetStatusBarHeight();
navigationBarHeight = GetNavigationBarHeight();
int windowHeight = layoutHelperWnd.getHeight();
int windowWidth = layoutHelperWnd.getWidth();
Boolean isFullScreen, isStatusBar, isNavigationBar;
isFullScreen = isStatusBar = isNavigationBar = false;
Configuration config = getResources().getConfiguration();
if (config.orientation == ORIENTATION_LANDSCAPE) {
// landscape - screenWidth is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on right
if (screenWidth != windowHeight)
isStatusBar = true;
if (screenHeight != windowWidth)
isNavigationBar = true;
}
else {
// portrait, square, unknown - screenHeight is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on bottom
if (screenHeight != windowHeight) {
int difference = screenHeight - windowHeight;
if (difference == statusBarHeight)
isStatusBar = true;
else if (difference == navigationBarHeight)
isNavigationBar = true;
else {
isStatusBar = true;
isNavigationBar = true;
}
}
}
if (!isStatusBar && !isNavigationBar)
isFullScreen = true;
Log.v(TAG,"Screen change:\nScreen W,H: (" + screenWidth + ", " + screenHeight + ")\nOrientation: " + (config.orientation == ORIENTATION_LANDSCAPE ? "Landscape" : config.orientation == ORIENTATION_PORTRAIT ? "Portrait" : "Unknown") +
"\nWindow W,H: (" + windowWidth + ", " + windowHeight + ")\n" + "Status bar H: " + statusBarHeight + ", present: " + isStatusBar + "\nNavigation bar H: " + navigationBarHeight + ", present: " + isNavigationBar + "\nFullScreen: " + isFullScreen);
// do any updates required to the service's assets here
}
});
}
public void ReadScreenDimensions(WindowManager wm) {
Display myDisplay = wm.getDefaultDisplay();
Display.Mode mode = myDisplay.getMode();
screenHeight = mode.getPhysicalHeight();
screenWidth = mode.getPhysicalWidth();
}
public int GetStatusBarHeight() {
// returns 0 for no result found
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int GetNavigationBarHeight() {
// returns 0 for no result found
int result = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return getResources().getDimensionPixelSize(resourceId);
}
return result;
}
You will also need to add to your manifest (for the app, not the service):
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
and in your main activity call isSystemAlertPermissionGranted() and then do something meaningful depending on what is returned (possibly waiting until onActivityResult() in some cases). Er, I basically grabbed this wholesale from various places and have not changed it much or at all. :)
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 1234;
public boolean isSystemAlertPermissionGranted() {
// if this doesn't work, try https://stackoverflow.com/questions/46208897/android-permission-denied-for-window-type-2038-using-type-application-overlay
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
return false;
}
else
{
Log.v(TAG,"Permission is granted");
return true;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG, "Permission is granted");
return true;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
// Check if the app get the permission
if (Settings.canDrawOverlays(this)) {
// Run your logic with newly-granted permission.
} else {
// Permission not granted. Change your logic accordingly.
// App can re-request permission anytime.
}
}
}
This is how we're getting screen height:
getResources().getDisplayMetrics().heightPixels;
The problem we're having is that with the Galaxy S8's hidden nav bar, the value returned is the height minus the nav bar size (even though the nav bar is hidden by the user). How do we get the full usable height?
example: screen height is 2220 pixels but the value returned is 2076
The answer needs to be able to work on Galaxy S8 with or without the nav bar hidden as well as other devices
thank you so much in advance
Try to use this :
Point displaySize = new Point();
activity.getWindowManager().getDefaultDisplay().getRealSize(displaySize);
Point is a class from android.graphics package.
From docs : https://developer.android.com/reference/android/view/Display.html#getRealSize(android.graphics.Point)
Gets the real size of the display without subtracting any window decor
or applying any compatibility scale factors.
The size is adjusted based on the current rotation of the display.
The real size may be smaller than the physical size of the screen when
the window manager is emulating a smaller display (using adb shell wm
size).
Edit :
I checked this code on LG G4 and it works fine :
//get the full height of device
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getRealSize(displaySize);
Resources resources = getResources();
int navBarId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
int fullHeight = displaySize.y;
//get nav bar size
int navbarSize = resources.getDimensionPixelSize(navBarId);
int availableSize = fullHeight;
//check is navbar is visible
boolean navBarVisible = (getWindow().getDecorView().getSystemUiVisibility() &
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
if(navBarVisible){
availableSize -= navbarSize;
}
Also you can use this code :
view.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
#Override
public void onSystemUiVisibilityChange(int i) {
Log.d("Nav bar visible : ", String.valueOf((i & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)==0));
}
});
to check when nav bar is hiding.
I had the same problem and could solve this with the decorview:
//Get the correct screen size even if the device has a hideable navigation bar (e.g. the Samsung Galaxy S8)
View decorView = getWindow().getDecorView(); //if you use this in a fragment, use getActivity before getWindow()
Rect r = new Rect();
decorView.getWindowVisibleDisplayFrame(r);
int screenHeight = r.bottom; // =2220 on S8 with hidden NavBar and =2076 with enabled NavBar
int screenWidth = r.right; // =1080 on S8
In short I want a preview similar to Google Plays, where you can slide up and the top image/video preview fades back and the rest of the view is moved up.
I've got a view that I want to be hiden like the video in this example, the action bar can remain stationary at all times, but I need the bottom part to be dragable.
I can't seem to find out which layout that is, or how it is done, all I managed to find were unrelated such as ViewPager. My current min sdk version is 18, the compile version is 21.
The following is the code I used in the app I am working
You will have to use the OnScrollChanged function in your ScrollView. ActionBar doesn't let you set the opacity , so set a background drawable on the actionbar and you can change its opacity based on the amount of scroll in the scrollview. I have given an example workflow
The function sets gives the appropriate alpha for the view locationImage based on its position WRT window .
this.getScrollY() gives you how much the scrollView has scrolled
public void OnScrollChanged(int l, int t, int oldl, int oldt) {
// Code ...
locationImage.setAlpha(getAlphaForView(locationImageInitialLocation- this.getScrollY()));
}
private float getAlphaForView(int position) {
int diff = 0;
float minAlpha = 0.4f, maxAlpha = 1.f;
float alpha = minAlpha; // min alpha
if (position > screenHeight)
alpha = minAlpha;
else if (position + locationImageHeight < screenHeight)
alpha = maxAlpha;
else {
diff = screenHeight - position;
alpha += ((diff * 1f) / locationImageHeight)* (maxAlpha - minAlpha); // 1f and 0.4f are maximum and min
// alpha
// this will return a number betn 0f and 0.6f
}
// System.out.println(alpha+" "+screenHeight +" "+locationImageInitialLocation+" "+position+" "+diff);
return alpha;
}
You can download an example working sample at https://github.com/ramanadv/fadingActionBar
Credit: CommandSpace
Is there a way to determine the dimension (width) of the menu bar of devices without hard menu buttons? (like the archos devices).
I need to know the usable dimension of the screen...
This method is very useful in order to set the layout padding in Android KitKat (4.4). Using this, you can avoid the soft buttons bar overlapping over your layout.
The getRealMetrics method is only available with API 17 and +, but I'm only using the following method I wrote for devices on API 19+
#SuppressLint("NewApi")
private int getSoftbuttonsbarHeight() {
// getRealMetrics is only available with API 17 and +
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return 0;
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int usableHeight = metrics.heightPixels;
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
int realHeight = metrics.heightPixels;
return realHeight > usableHeight ? realHeight - usableHeight : 0;
}
Tested upon Nexus 5 & Nexus 7 2013.
A very late answer but for anyone that encounters this problem. A simple way to get the amount of usable space offsetting any soft keys/navigation bar or even the status bar, is to use getWindowVisibleDisplayFrame(Rect) from the Apps DecorView. This will load a Rect object with the dimension of the display that is actually usable.
Since you cannot directly get a reference to the Activiy's DecorView, you need to first get the Activity's window using the accessor method, getWindow(). This will return the window object that holds the Activity and other screen elements. To get the decorView that contains the activity as well as the screen decorations i.e. the status bar, navigation bar/soft button bar, call accessor method getDecorView(). Once your have a reference to the DecorView call getWindowVisibleDisplayFrame(Rect) and you will have a loaded Rect object.
Starting Andorid 3.2, the height of system status bar is not included in DisplayMetrics's height, you have to use undocumented APIs (Display.getRawWidth() and Display.getRawHeight()) to get the physical screen width or height.
Method mGetRawW = Display.class.getMethod("getRawWidth");
Method mGetRawH = Display.class.getMethod("getRawHeight");
int nW = (Integer)mGetRawW.invoke(dp);
int nH = (Integer)mGetRawH.invoke(dp);
UPDATED: For API 13-16, you have to use the above code to get real width/height. For API 17+, you can now use the new public API, Display.getRealSize()
for me, in android note 10, I need to get navigation bar height by dimen:
fun getSoftNavigationBarSize(resources: Resources): Int {
var result = 0
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
This question already has answers here:
Height of statusbar?
(14 answers)
Closed 2 years ago.
What's the height of the status bar in Android? Is it always the same?
From my measurements it seems that it's 25dp, but I'm not sure if it has the same height on all platforms.
(I want to know this to properly implement a fade transition from an activity that doesn't have status bar to one that does)
this question was answered before...
Height of statusbar?
Update::
Current method:
ok, the height of the status bar depends on the screen size, for example in a device
with 240 X 320 screen size the status bar height is 20px, for a device with 320 X 480 screen size the status bar height is 25px, for a device with 480 x 800 the status bar height must be 38px
so i recommend to use this script to get the status bar height
Rect rectangle = new Rect();
Window window = getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectangle);
int statusBarHeight = rectangle.top;
int contentViewTop =
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int titleBarHeight= contentViewTop - statusBarHeight;
Log.i("*** Elenasys :: ", "StatusBar Height= " + statusBarHeight + " , TitleBar Height = " + titleBarHeight);
(old Method) to get the Height of the status bar on the onCreate() method of your Activity, use this method:
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Out of all the code samples I've used to get the height of the status bar, the only one that actually appears to work in the onCreate method of an Activity is this:
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Apparently the actual height of the status bar is kept as an Android resource. The above code can be added to a ContextWrapper class (e.g. an Activity).
Found at http://mrtn.me/blog/2012/03/17/get-the-height-of-the-status-bar-in-android/
Hardcoding the size or using reflection to get the value of status_bar_height is considered bad practice. Chris Banes talked about this in at the Droidcon New York. The recommended way of getting the status bar size is via the OnApplyWindowInsetsListener:
myView.setOnApplyWindowInsetsListener { view, insets -> {
val statusBarSize = insets.systemWindowInsetTop
return insets
}
This was added in API 20 and is also backported via ViewAppCompat.
On MDPI devices, the status bar is 25px. We can use this as the base and multiply it by the density (rounded up) to get the status bar height on any device:
int statusBarHeight = Math.ceil(25 * context.getResources().getDisplayMetrics().density);
For reference: ldpi=.75, mdpi=1, hdpi=1.5, xhdpi=2
I've merged some solutions together:
public static int getStatusBarHeight(final Context context) {
final Resources resources = context.getResources();
final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
return resources.getDimensionPixelSize(resourceId);
else
return (int) Math.ceil((VERSION.SDK_INT >= VERSION_CODES.M ? 24 : 25) * resources.getDisplayMetrics().density);
}
another alternative:
final View view = findViewById(android.R.id.content);
runJustBeforeBeingDrawn(view, new Runnable() {
#Override
public void run() {
int statusBarHeight = getResources().getDisplayMetrics().heightPixels - view.getMeasuredHeight();
}
});
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
According to Material Guidance; height of status bar is 24 dp.
If you want get status bar height in pixels you can use below method:
private static int statusBarHeight(android.content.res.Resources res) {
return (int) (24 * res.getDisplayMetrics().density);
}
which can be called from activity with:
statusBarHeight(getResources());
The default height used to be 25dp. With Android Marshmallow (API 23) the height was reduced to 24dp.
Update: Please be aware that since the age of notches and punch-whole-cameras began, using a static height for the status bar no longer works. Please use window insets instead!
this also work with the refrence link
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Official height is 24dp,
as is stated officially by Google on Android Design webpage.
I have the same problem of having to get the status bar height in an onCreate. This works for me.
private static final int LOW_DPI_STATUS_BAR_HEIGHT = 19;
private static final int MEDIUM_DPI_STATUS_BAR_HEIGHT = 25;
private static final int HIGH_DPI_STATUS_BAR_HEIGHT = 38;
Inside the onCreate:
DisplayMetrics displayMetrics = new DisplayMetrics();
((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics);
int statusBarHeight;
switch (displayMetrics.densityDpi) {
case DisplayMetrics.DENSITY_HIGH:
statusBarHeight = HIGH_DPI_STATUS_BAR_HEIGHT;
break;
case DisplayMetrics.DENSITY_MEDIUM:
statusBarHeight = MEDIUM_DPI_STATUS_BAR_HEIGHT;
break;
case DisplayMetrics.DENSITY_LOW:
statusBarHeight = LOW_DPI_STATUS_BAR_HEIGHT;
break;
default:
statusBarHeight = MEDIUM_DPI_STATUS_BAR_HEIGHT;
}
See:
http://developer.android.com/reference/android/util/DisplayMetrics.html
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
Kotlin version that combines two best solutions
fun getStatusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId)
else Rect().apply { window.decorView.getWindowVisibleDisplayFrame(this) }.top
}
Takes status_bar_height value if present
If status_bar_height is not present, calculates the status bar height from Window decor
Yes when i try it with View it provides the result of 25px.
Here is the whole code :
public class SpinActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout lySpin = new LinearLayout(this);
lySpin.setOrientation(LinearLayout.VERTICAL);
lySpin.post(new Runnable()
{
public void run()
{
Rect rect = new Rect();
Window window = getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int contentViewTop =
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int titleBarHeight = contentViewTop - statusBarHeight;
System.out.println("TitleBarHeight: " + titleBarHeight
+ ", StatusBarHeight: " + statusBarHeight);
}
}
}
}
240x320 - 20px
320x480 - 25px
480x800+ - 38px
Try this:
Rect rect = new Rect();
Window win = this.getWindow();
win.getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int contentViewTop = win.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int titleBarHeight = contentViewTop - statusBarHeight;
Log.d("ID-ANDROID-CONTENT", "titleBarHeight = " + titleBarHeight );
it didn't work for me in the onCreate method for the activity, but did when I put it in an onClickListener and gave me a measurement of 25
the height of the status bar is 24dp in android 6.0
<!-- Height of the status bar -->
<dimen name="status_bar_height">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen>
you can find the answer in the source code: frameworks\base\core\res\res\values\dimens.xml
To solve this, I used a combination approach.
This is necessary as on tablets the system bar already subtracts it's pixels when display.getHeight() is called.
So I first check if a system bar is present, and then Ben Claytons approach, which works fine on phones.
public int getStatusBarHeight() {
int statusBarHeight = 0;
if (!hasOnScreenSystemBar()) {
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
}
return statusBarHeight;
}
private boolean hasOnScreenSystemBar() {
Display display = getWindowManager().getDefaultDisplay();
int rawDisplayHeight = 0;
try {
Method getRawHeight = Display.class.getMethod("getRawHeight");
rawDisplayHeight = (Integer) getRawHeight.invoke(display);
} catch (Exception ex) {
}
int UIRequestedHeight = display.getHeight();
return rawDisplayHeight - UIRequestedHeight > 0;
}
Thanks to #Niklas +1 this is the correct way to do it.
public class MyActivity extends Activity implements android.support.v4.View.OnApplyWindowInsetsListener {
Rect windowInsets;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
View rootview = findViewById(android.R.id.content);
android.support.v4.View.ViewCompat.setOnApplyWindowInsetsListener(rootview, this);
}
android.support.v4.View.WindowInsetsCompat android.support.v4.View.OnApplyWindowInsetsListener.OnApplyWindowInsets(View v, android.support.v4.View.WindowInsetsCompat insets)
{
windowInsets = new Rect();
windowInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
//StatusBarHeight = insets.getSystemWindowInsetTop();
//Refresh/Adjust view accordingly
return insets;
}
}
Please excuse me if the code isn't 100% correct, converted it from Xamarin C# but this is the just of it. Works with Notches, etc.
Toggled Fullscreen Solution:
This solution may look like a workaround, but it actually accounts for whether your app is fullscreen (aka hiding the status bar) or not:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point(); display.getSize(size);
int barheight = size.y - findViewById(R.id.rootView).getHeight();
This way, if your app is currently fullscreen, barheight will equal 0.
Personally I had to use this to correct absolute TouchEvent coordinates to account for the status bar as so:
#Override
public boolean onTouch(View view,MotionEvent event) {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point(); display.getSize(size);
int YCoord = (int)event.getRawY() - size.y + rootView.getHeight());
}
And that will get the absolute y-coordinate whether the app be fullscreen or not.
Enjoy
The reason why the top answer does not work for some people is because you cannot get the dimensions of a view until it is ready to render. Use an OnGlobalLayoutListener to get said dimensions when you actually can:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ViewGroup decorView = (ViewGroup) this.getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= 16) {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
// Nice one, Google
decorView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
rect.top; // This is the height of the status bar
}
}
}
This is the most reliable method.
On Android 4.1 and higher, you can set your application's content to appear behind the status bar, so that the content doesn't resize as the status bar hides and shows. To do this, use SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN. You may also need to use SYSTEM_UI_FLAG_LAYOUT_STABLE to help your app maintain a stable layout.
When you use this approach, it becomes your responsibility to ensure that critical parts of your app's UI (for example, the built-in controls in a Maps application) don't end up getting covered by system bars. This could make your app unusable. In most cases you can handle this by adding the android:fitsSystemWindows attribute to your XML layout file, set to true. This adjusts the padding of the parent ViewGroup to leave space for the system windows. This is sufficient for most applications.
In some cases, however, you may need to modify the default padding to get the desired layout for your app. To directly manipulate how your content lays out relative to the system bars (which occupy a space known as the window's "content insets"), override fitSystemWindows(Rect insets). The fitSystemWindows() method is called by the view hierarchy when the content insets for a window have changed, to allow the window to adjust its content accordingly. By overriding this method you can handle the insets (and hence your app's layout) however you want.
form:
https://developer.android.com/training/system-ui/status.html#behind
If you know exactly the size VS height
like
for example in a device with 320 X 480 screen size the status bar height is 25px, for a device with 480 x 800 the status bar height must be 38px
then you can just get the width of your view / the screen size you can just use an if else statement to get the height of status bar
Since multi-window mode is available now, your app may not have statusbar on top.
Following solution handle all the cases automatically for you.
android:fitsSystemWindows="true"
or programatically
findViewById(R.id.your_root_view).setFitsSystemWindows(true);
you may also get root view by
findViewById(android.R.id.content).getRootView();
or
getWindow().getDecorView().findViewById(android.R.id.content)
For more details on getting root-view refer - https://stackoverflow.com/a/4488149/9640177
This issue recently became relevant for me because of the notch in my Pixel 3XL. I really liked android developer's solution, but I wanted to be able to get the status bar height at will, since it was specifically necessary for a full screen animation that I needed to play. The function below enabled a reliable query:
private val DEFAULT_INSET = 96
fun getInsets(view: View?): Int {
var inset = DEFAULT_INSET
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//Safe because only P supports notches
inset = view?.rootWindowInsets?.stableInsetTop ?: DEFAULT_INSET
}
return inset
}
fun blurView(rootView: View?, a: SpacesActivity?) {
val screenBitmap = getBitmapFromView(rootView!!)
val heightDifference = getInsets(rootView)
val croppedMap = Bitmap.createBitmap(
screenBitmap, 0, heightDifference,
screenBitmap.width,
screenBitmap.height - heightDifference)
val blurredScreen = blurBitmap(croppedMap)
if (blurredScreen != null) {
val myDrawable = BitmapDrawable(a!!.resources, blurredScreen)
a.errorHudFrameLayout?.background = myDrawable
a.appBarLayout?.visibility = View.INVISIBLE
}
}
And then in the activity class:
fun blurView() {
this.runOnUiThread {
Helper.blurView(this)
}
}
You will of course want to make pass a weak reference of the activity to the static Helper class method parameter, but for the sake of brevity I refrained in this example. The blurBitmapand errorHudFrameLayout are omitted for the same reason, since they don't directly pertain to obtaining the height of the status bar.