The previous version of my question was too wordy. People couldn't understand it, so the following is a complete rewrite. See the edit history if you are interested in the old version.
A RelativeLayout parent sends MeasureSpecs to its child view's onMeasure method in order to see how big the child would like to be. This occurs in several passes.
My custom view
I have a custom view. As the view's content increases, the view's height increases. When the view reaches the maximum height that the parent will allow, the view's width increases for any additional content (as long as wrap_content was selected for the width). Thus, the width of the custom view is directly dependant on what parent says the maximum hight must be.
An (inharmonious) parent child conversation
onMeasure pass 1
The RelativeLayout parent tells my view, "You can be any width up to 900 and any height up to 600. What do you say?"
My view says, "Well, at that height, I can fit everything with a width of 100. So I'll take a width of 100 and a height of 600."
onMeasure pass 2
The RelativeLayout parent tells my view, "You told me last time that you wanted a width of 100, so let's set that as an exact width. Now, based on that width, what kind of height would you like? Anything up to 500 is OK."
"Hey!" my view replies. "If you're only giving me a maximum hight of 500, then 100 is too narrow. I need a width of 200 for that height. But fine, have it your way. I won't break the rules (yet...). I'll take a width of 100 and a height of 500."
Final result
The RelativeLayout parent assigns the view a final size of 100 for the width and 500 for the height. This is of course too narrow for the view and part of the content gets clipped.
"Sigh," thinks my view. "Why won't my parent let me be wider? There is plenty of room. Maybe someone on Stack Overflow can give me some advice."
Update: Modified code to fix some things.
First, let me say that you asked a great question and laid out the problem very well (twice!) Here is my go at a solution:
It seems that there is a lot going on with onMeasure that, on the surface, doesn't make a lot of sense. Since that is the case, we will let onMeasure run as it will and at the end pass judgment on the View's bounds in onLayoutby setting mStickyWidth to the new minimum width we will accept. In onPreDraw, using a ViewTreeObserver.OnPreDrawListener, we will force another layout (requestLayout). From the documentation (emphasis added):
boolean onPreDraw ()
Callback method to be invoked when the view tree is about to be drawn. At this point, all views in the tree have been measured and
given a frame. Clients can use this to adjust their scroll bounds or
even to request a new layout before drawing occurs.
The new minimum width set in onLayout will now be enforced by onMeasure which is now smarter about what is possible.
I have tested this with your example code and it seems to work OK. It will need much more testing. There may be other ways to do this, but that is the gist of the approach.
CustomView.java
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
public class CustomView extends View
implements ViewTreeObserver.OnPreDrawListener {
private int mStickyWidth = STICKY_WIDTH_UNDEFINED;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
logMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
int desiredHeight = 10000; // some value that is too high for the screen
int desiredWidth;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
// Height
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(desiredHeight, heightSize);
} else {
height = desiredHeight;
}
// Width
if (mStickyWidth != STICKY_WIDTH_UNDEFINED) {
// This is the second time through layout and we are trying renogitiate a greater
// width (mStickyWidth) without breaking the contract with the View.
desiredWidth = mStickyWidth;
} else if (height > BREAK_HEIGHT) { // a number between onMeasure's two final height requirements
desiredWidth = ARBITRARY_WIDTH_LESSER; // arbitrary number
} else {
desiredWidth = ARBITRARY_WIDTH_GREATER; // arbitrary number
}
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(desiredWidth, widthSize);
} else {
width = desiredWidth;
}
Log.d(TAG, "setMeasuredDimension(" + width + ", " + height + ")");
setMeasuredDimension(width, height);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int w = right - left;
int h = bottom - top;
super.onLayout(changed, left, top, right, bottom);
// Here we need to determine if the width has been unnecessarily constrained.
// We will try for a re-fit only once. If the sticky width is defined, we have
// already tried to re-fit once, so we are not going to have another go at it since it
// will (probably) have the same result.
if (h <= BREAK_HEIGHT && (w < ARBITRARY_WIDTH_GREATER)
&& (mStickyWidth == STICKY_WIDTH_UNDEFINED)) {
mStickyWidth = ARBITRARY_WIDTH_GREATER;
getViewTreeObserver().addOnPreDrawListener(this);
} else {
mStickyWidth = STICKY_WIDTH_UNDEFINED;
}
Log.d(TAG, ">>>>onLayout: w=" + w + " h=" + h + " mStickyWidth=" + mStickyWidth);
}
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
if (mStickyWidth == STICKY_WIDTH_UNDEFINED) { // Happy with the selected width.
return true;
}
Log.d(TAG, ">>>>onPreDraw() requesting new layout");
requestLayout();
return false;
}
protected void logMeasureSpecs(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
String measureSpecHeight;
String measureSpecWidth;
if (heightMode == MeasureSpec.EXACTLY) {
measureSpecHeight = "EXACTLY";
} else if (heightMode == MeasureSpec.AT_MOST) {
measureSpecHeight = "AT_MOST";
} else {
measureSpecHeight = "UNSPECIFIED";
}
if (widthMode == MeasureSpec.EXACTLY) {
measureSpecWidth = "EXACTLY";
} else if (widthMode == MeasureSpec.AT_MOST) {
measureSpecWidth = "AT_MOST";
} else {
measureSpecWidth = "UNSPECIFIED";
}
Log.d(TAG, "Width: " + measureSpecWidth + ", " + widthSize + " Height: "
+ measureSpecHeight + ", " + heightSize);
}
private static final String TAG = "CustomView";
private static final int STICKY_WIDTH_UNDEFINED = -1;
private static final int BREAK_HEIGHT = 1950;
private static final int ARBITRARY_WIDTH_LESSER = 200;
private static final int ARBITRARY_WIDTH_GREATER = 800;
}
To make custom layout you need to read and understand this article https://developer.android.com/guide/topics/ui/how-android-draws.html
It isn't difficult to implement behaviour you want. You just need to override onMeasure and onLayout in your custom view.
In onMeasure you will get max possible height of your custom view and call measure() for childs in cycle. After child measurement get desired height from each child and calculate is child fit in current column or not, if not increase custom view wide for new column.
In onLayout you must call layout() for all child views to set them positions within the parent. This positions you have calculated in onMeasure before.
I'm having everything the same as in this sample in https://github.com/googlesamples/android-vision/tree/master/visionSamples/multi-tracker except my activity layout is this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/topLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:weightSum="100"
android:orientation="horizontal">
<be.citylife.communitypurchaseapp.view.camera.CameraSourcePreview
android:id="#+id/preview"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="60">
<be.citylife.communitypurchaseapp.view.camera.GraphicOverlay
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</be.citylife.communitypurchaseapp.view.camera.CameraSourcePreview>
<FrameLayout
android:id="#+id/sideContainer"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="40"/>
</LinearLayout>
My tablet is in landscape and I want that the cameraPreviewSource is always left and fills the whole screen in the height and then right off it I'm having a fragment that fills the rest.
This layout works except my previewsource doesn't fill the whole height. It has a black banner on it. Even my width is actually smaller than I want you can see this on the screenshot:
http://i61.tinypic.com/vctmw0.png
I played with the CameraSourcePreview with the width and height in the onLayout function but it doesn't help. I know on the preview that it does fill the screen to the bottom of the screen but on the tablet it isn't.
lp.
Anyone an idea how to solve this?
EDIT:
I think it has something to do with this:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int width = 320;
int height = 240;
if (mCameraSource != null) {
Size size = mCameraSource.getPreviewSize();
if (size != null) {
width = size.getWidth();
height = size.getHeight();
}
}
// Swap width and height sizes when in portrait, since it will be rotated 90 degrees
if (isPortraitMode()) {
int tmp = width;
width = height;
height = tmp;
}
final int layoutWidth = right - left;
final int layoutHeight = bottom - top;
// Computes height and width for potentially doing fit width.
int childWidth = layoutWidth;
int childHeight = (int)(((float) layoutWidth / (float) width) * height);
// If height is too tall using fit width, does fit height instead.
if (childHeight > layoutHeight) {
childHeight = layoutHeight;
childWidth = (int)(((float) layoutHeight / (float) height) * width);
}
for (int i = 0; i < getChildCount(); ++i) {
getChildAt(i).layout(0, 0, childWidth, childHeight);
}
try {
startIfReady();
} catch (IOException e) {
Log.e(TAG, "Could not start camera source.", e);
}
}
That's the onlayout method off the CameraSourcePreview.
Comment or remove below lines from CameraSourcePreview and it should be fine. I was having same issue like you and it is solved now.
if (childHeight > layoutHeight) {
childHeight = layoutHeight;
childWidth = (int)(((float) layoutHeight / (float) height) * width);
}
that should put it into fullscreen mode :D there are a bunch of other modes you can select from. if this doesnt work, remove the automatically generated
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/** gets called when a Menu.onClick happens
*
* #param item the ID of the clicked Item
*/
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
if (id == R.id.action_settings) {
//TODO
}
return super.onOptionsItemSelected(item);
}
}
Likely has something to do with the aspect ratio of the camera and how it draws into its container.
If the camera preview you're using maintains aspect ratio and fits the preview to the container, then you will definitely get black bars. This is because most cameras' sensors produce images that are designed to fit within a space relative to 1920x1080px (or a 16:9 aspect ratio box).
What you need, is for the extra space on the sides to be hidden and for the preview to fill based on height. That is, if you don't mind some of your image to be hidden from the user when previewing. It might not be possible to do this directly with the view you're using, but it should be relatively simple if you place your object into another layout container.
Hope this helps!
I am using objectAnimator for animating a button from bottom to top in Android. Now i am using the below code
ObjectAnimator transAnimation = ObjectAnimator.ofFloat(button,"translationY",0,440);
transAnimation.setDuration(440);
transAnimation.start();
I have also tried with sample code below. But still the problem exists
ObjectAnimator transAnimation = ObjectAnimator.ofFloat(loginLayout, "translationY",0f,0.8f);
transAnimation.setDuration(480);
transAnimation.start();
It works fine in large screen devices. But when it comes to small screen devices it goes off the screen. I want to stay it in the top of the screen irrespective of different screen sizes. I think i have to give values in percentage (say 0% to 100% or 0 to 100%p). So my question is how to give values in percentage in objectAnimator in Android. I also noticed one thing that this objectAnimator is introduced only in HoneyComb. So is there any backward compatibility libraries for running it in low versions. Could anyone guide me to find a solution for this.
I also tried extending View and writing getter and setter for the property in offset(). Still it does not move fully to top of screen. Here is the code I have used.
#SuppressLint("NewApi") public float getXFraction()
{
int width = context.getWindowManager().getDefaultDisplay().getHeight();
return (width == 0) ? 0 : (getY());
}
#SuppressLint("NewApi") public void setXFraction(float xFraction) {
int width = context.getWindowManager().getDefaultDisplay().getWidth();
setY((width > 0) ? (width) : 0);
}
Thanks inAdvance
To use ObjectAnimator on pre-Honeycomb devices add NineOldAndroids library to your project and change your imports to use com.nineoldandroids.animation.ObjectAnimator.
To use percentage values in ObjectAnimator instead of getXFraction and setXFraction you have to add getYFraction and setYFraction methods like this
public float getYFraction() {
final WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int height = wm.getDefaultDisplay().getHeight();
return (height == 0) ? 0 : getY() / (float) height;
}
public void setYFraction(float yFraction) {
final WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int height = wm.getDefaultDisplay().getHeight();
setY((height > 0) ? (yFraction * height) : 0);
}
And then you can create xml file project-folder/res/animator/move_bottom.xml like this
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="yFraction"
android:valueFrom="0"
android:valueTo="0.8"
android:valueType="floatType" />
Or create animation in code
final LoginLayout loginLayout = (LoginLayout) findViewById(R.id.login_layout);
final ObjectAnimator oa = ObjectAnimator.ofFloat(loginLayout, "yFraction", 0f, 0.8f);
oa.start();
If you want to create animation from outside of screen with ObjectAnimator. You can do this with Android display size.
#Override
public void onCreate(Bundle savedInstanceState) {
...
sampleImage = BitmapFactory.decodeResource(getResources(), R.drawable.sample);
myImage = (ImageView) findViewById(R.id.image_view);
myImage.setImageBitmap(sampleImage);
//In case API Level is 14 above.
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
float displayWidth = size.x;
//In case API Level is 13 below.
//WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
//Display display = wm.getDefaultDisplay();
//displayWidth = display.getWidth();
animator = ObjectAnimator.ofFloat(myImage, "translationX", displayWidth, 0);
animator.setDuration(500);
}
Sorry, if my suggestion missed the point.
Try using a custom property for ObjectAnimator, instead of creating custom view with custom property, you can create a custom property that accepts a 'fraction':
Property<View, Float> property = new Property<View, Float>(Float.TYPE, "translation_x_fraction") {
#Override public Float get(View view) {
return view.getWidth() <= 0 ? 0 : view.getTranslationX() / view.getWidth();
}
#Override public void set(View view, Float value) {
view.setTranslationX(view.getWidth() * value);
}
}
ObjectAnimator.ofFloat(view, property, 0f, 1f);
I have done this way, I spent too much time but it will save your time.
Arguments:
View (Your support View)
Parent width (Root of your View)
Child width (On which you want to apply Animation)
% (How much percentage you want to move your object or view)
Do not afraid with Arguments, I am explaining here by example.
XML and JAVA code:
activity_main.xml:
<LinearLayout
android:id="#+id/llProgressParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_gravity="center"
android:orientation="horizontal"
android:weightSum="1" >
<View
android:id="#+id/viewSuppot"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0" >
</View>
<ImageView
android:id="#+id/ivProgressChild"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/ic_launcher" />
</LinearLayout>
MainActivity.java:
private LinearLayout llProgressParent;
private View viewSuppot;
private ImageView ivProgressChild;
private int childWidth, parentWidth;
private int percentage = 75;
on onCreate():
viewSuppot = findViewById(R.id.viewSuppot);
llProgressParent = (LinearLayout)findViewById(R.id.llProgressParent);
llProgressParent.post(new Runnable() {
#Override
public void run() {
parentWidth = llProgressParent.getMeasuredWidth();
}
});
ivProgressChild = (ImageView)findViewById(R.id.ivProgressChild);
ivProgressChild.post(new Runnable() {
#Override
public void run() {
childWidth = ivProgressChild.getMeasuredWidth();
}
});
Method:
private void animateViewByPercentageOrProgrss(final View view, int parentWidth, int childWidth, int percentage){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ValueAnimator anim = ValueAnimator.ofInt(view.getMeasuredWidth(), (int) (((float) percentage * parentWidth) / 100)-(childWidth/2));
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = val;
view.setLayoutParams(layoutParams);
}
});
anim.setDuration(2000);
anim.start();
}
How to call Method:
animateViewByPercentageOrProgrss(viewSuppot, parentWidth, childWidth, percentage);
Its Done.
how about this
Get Y dimen of parent layout then dividing it by 2 and then doing an animation on it ....
int a = parentlayout.getY();
view.animate().setTranslationY(a/2).start();
this one works as you want ....
its my first answer lemme know if it worked or not ..
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.