The application I'm working on is a service which runs in the background in android.
The problem is that I need to determine the height of the notification bar for some functionality of the service.
I found a number of solutions for this, for regular activities -
a view inside an activity can determine its own height without the notification bar, based on checking its actual view size from its onSizeChanged event, after it is already drawn.
However, this is not applicable to a service which has no physical view drawn.
I would really appreciate any ideas on getting the size of the notification bar at a system level, perhaps?
Thanks so much!
Vitaliy
Have an activity tell your service the proper size to use. The status bar height, nor the screen size, is likely to change during the operation of your service.
Maybe you can retrieve an standard notification bar icon and measure it.
You'll have to use a system icon so you have more chances to be present on all Android customizations (Sense UI, MotoBlur, etc...)
Something like:
Drawable phoneCallIcon = getResources().getDrawable(android.R.drawable.stat_sys_phone_call);
int height = phoneCallIcon.getIntrinsicHeight();
Running this from my Nexus One gives 38 pixels height, which is accurate.
A safer approach to this 'hack' would be iterating through some of the standard notification icons.
It would be this way:
int notificationBarResources[] = {
android.R.drawable.stat_sys_phone_call,
android.R.drawable.stat_notify_call_mute,
android.R.drawable.stat_notify_sdcard,
android.R.drawable.stat_notify_sync,
android.R.drawable.stat_notify_missed_call,
android.R.drawable.stat_sys_headset,
android.R.drawable.stat_sys_warning };
int notificationBarHeight = -1;
for (int i = 0; i < notificationBarResources.length; i++) {
try {
Drawable phoneCallIcon = getResources().getDrawable(
android.R.drawable.stat_sys_phone_call);
if ((notificationBarHeight = phoneCallIcon.getIntrinsicHeight()) != -1) {
break;
}
} catch (Resources.NotFoundException e) {
// Nothing to do
}
}
I have the same problem. My view lays out images in the view constructor. The only time you know the actual view height is on the onMesure method by doing:
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
widthView = MeasureSpec.getSize(widthMeasureSpec);
heightView = MeasureSpec.getSize(heightMeasureSpec);
}
However I can't lay out the images inside this method (animation of the images will keep calling this method).
To know the actual height of the view I would need to do:
screenHeight - statusBarHeight
so I could use this value on the constructor of the view.
I can't believe it has to be so complex to provide a solution for such a simple requirement.
Related
Let's say we have a main_activity.xml layout that defines all dimensions in a relative manner -- constraints, percentages, and guidelines (that are percentages)... no "static" dp.
But in MainActivity.java, we programatically create some subviews, and we want to define their height/width dimensions as relative to existing views.
We do not know the dimensions or density of the device so, so nor do we know the (actual integer) dimensions of any view before run-time...
But we can say something like:
int heightDimensionForNewView = (int) (someAlreadyInflatedView.getHeight() / 7f)
But what if, under certain circumstances, these "new" views need to be displayed immediately at app start-time?
So, the question:
In the Android Activity life-cycle, when is the earliest point at which you can (somehow) safely query (something) for actual/finalized/guaranteed layout dimensions? And what is that something and somehow?
I haven't been able to find an override method such as "onContentViewInflated()" and there is no onCreateView() method like there is in Fragments.
I've also tried Logging from inside onStart() and onResume() but the dimension results are always "0," presumably because they haven't been inflated yet.
I know that any given View can get its own dimensions in onMeasure(), but then you would have make a static variable in MainActivity in order to assign it and use it from there... or some way of sending that information from the View back to the Activity.
What am I missing? I just want to be able to get the number somehow from inside MainActivity itself.
My suggestions are:
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int oldWidth = oldRight - oldLeft; // right exclusive, left inclusive
if( v.getWidth() != oldWidth ) {
// width has changed
}
}
});
and
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// View has laid out
// Remove the layout observer if you don't need it anymore
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
I have an Android application that extensively uses PopupWindows. I've found that when the layouts of the contents of the PopupWindow use WRAP_CONTENT the dialog will only grow to a specific width before it begins truncating the content. I've traced this down to a config.xml dimension:
<dimen name="config_prefDialogWidth">580dp</dimen>
This dimension is used to create a maximum width in the measureHierarchy method of ViewRootImpl when determining the dialog size. The following code is used to access the value:
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
The config_prefDialogWidth seems to have values for different device configurations, for example, the one I listed is for sw600dp. The one for default devices has 320dp as a value.
It would seem that this dimension is tuned for portrait orientation. Since my app forces landscape orientation this width is too small.
How do I override the config_prefDialogWidth dimension?
What you need is to make the width of your PopupWindow(wrap_content) larger than config_prefDialogWidth.
So here is my solution.
Choose one child View from the contentView of your PopupWindow.
A TextView, for example.
Override its onMeasure, add View.MEASURED_STATE_TOO_SMALL if necessary:
TextView textView = new TextView(context) {
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Text breaks into at least 2 lines, maybe the width is too small
if (getLineCount() > 1) {
int measuredWidth = getMeasuredWidth();
final int desiredMaxWidth = (getResources().getDisplayMetrics().widthPixels >> 2) * 3;
// Compare with the desired max width you want, for example: 3/4 * screenWidth
if (measuredWidth < disiredMaxWidth) {
// Tell ViewRootImpl that the width used to measure is too small
measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
// ViewRootImpl will remeasure contentView with screem width.
setMeasuredDimension(measuredWidth, getMeasuredHeight());
}
}
}
};
Generally speaking:
Override onMeasure of one child View
Check whether the measured width is too small
Ask for remeasurement if necessary (Using View.MEASURED_STATE_TOO_SMALL)
You cannot override nor change config_prefDialogWidth's value because this dimension is a internal resource. A workaround is you must create your own class. You can copy PopupWindows from its source code, and modify it.
My app shows a widget with a total of 8 buttons. I would like to give the user the possibility to style that widget and customize the size of the ImageViews which are below the buttons.
The aim is to leave the Buttons as they are, but to change the ImageView sizes dynamically.
For this the User gets to set an icon Size, which is stored in a SharedPreference as an Integer iconSize.
How can I change the size of an ImageView in a Widget?
For now, the ImageViews are created with the size set in the xml file. How can I achieve a redraw with another size?
I assume there is not much code to post here but I will be glad to do so if necessary, just tell me if you have an idea what code could help here.
What I don't want to do:
resize the entire widget
create tons of layout files to switch according to the iconSize
Some Code:
This is how I set the ImageView size in my activity as a preview of the widget. icons is an array of ImageViews, progress refers to a ProgressBar that is used to choose iconSize.
for (ImageView icon : icons) {
icon.requestLayout();
// convert into actual Pixels
progress = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX,
progress,
getResources().getDisplayMetrics()
);
// set width and height
icon.getLayoutParams().height = progress;
icon.getLayoutParams().width = progress;
sizeText.setText("" + progress);
}
Here is a little workaround I found:
simply use
views.setViewPadding(R.id.vieId, left, top, right, bottom);
(views = RemoteViews)
You just have to make some calculation, so that an iconSize of 100% (of the biggest possible size) equals 0 padding and 1% iconSize equals max padding.
It worked without, too but I think it can'T harm to add the
android:cropToPadding="true"
attribute to ImageViews if you use this method.
Edit:
I forgot to mention that at some point after setting the padding you should update the widget (I do it in onPause() when the user quits the application to look at the widget).
Using the setPadding() in an activity will also lead to nowhere without calling invalidate() on the View, to force a redraw/update of it.
Here more code:
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Map the values, 100 is the custom max of my seekBar
// I am adding 1 to because the seekBar goes from 0-99,
// but the size from 1% to 100%
int iconSize = 100 - (progress+1);
for (ImageView icon : icons) {
// set the padding
icon.setPadding(iconSize, iconSize, iconSize, iconSize);
// force the ImageView to redraw with the new padding, this
// serves as a live preview of the icons' sizes.
icon.invalidate();
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// you might want to do something here
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// map the value
int iconSize = 100 - (seekBar.getProgress()+1);
// save it in your SharedPreferences or somewhere else
Utils.setDefaultsInt(Con._ICONSIZE, iconSize, MainPage.this);
}
});
I'm using Actionbarsherlock and I want to place a PopupWindow right below the action bar. Using the showAtLocation() takes an x and y offset, so ideally the y offset would be the height of the action bar. But when I call
int abHeight = getSupportActionBar().getHeight();
it returns zero. I'm using a SherlockFragmentActivity
Here's the relevant code:
slidingLayout = inflater.inflate(R.layout.sliding_menu, null);
menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT, Position.LEFT);
menuDrawer.setContentView(R.layout.activity_main);
menuDrawer.setMenuView(slidingLayout.findViewById(R.id.sliding_menu));
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
int abHeight = getSupportActionBar().getHeight();
I've looked all over and can't find a similar question/answer, so has anyone experienced this before? Thanks.
EDIT: Jake's answer was right on. In order to get that attribute value I used this post.
You can read the height of the action bar from the actionBarSize theme attribute. This changes based on the device configuration so make sure you are always reading it when your activity is created or recreated.
in you style.XML add: <item name="#android:attr/actionBarSize">50px</item>
and then in your activity add the following code :
TypedArray actionbarSizeTypedArray = mContext.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize});
int h = (int) actionbarSizeTypedArray.getDimension(0, 0);
this is one kind ,I am trying to get other ways.Good luck!
Yeah!I find a way very simple:
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
{
int h=TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
more info,look this link
You can't get the height for views until they have been layed out. Try adding a ViewTreeObserver:
someView.getViewTreeObserver().addGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remember to remove it if you don't want it to fire every time
someView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int abHeight = getSupportActionBar().getHeight();
// Use the height as desired...
}
});
Refer to the docs starting at View.getViewTreeObserver().
I tried to do custom component. I extended View class and do some drawing in onDraw overrided method. Why I need to override onMeasure? If I didn't, everything seen to be right. May someone explain it? How should I write my onMeasure method? I've seen couple tutorials, but each one is a little bit different than the other. Sometimes they call super.onMeasure at the end, sometimes they use setMeasuredDimension and didn't call it. Where is a difference?
After all I want to use several exactly the same components. I added those components to my XML file, but I don't know how big they should be. I want to set its position and size later (why I need to set size in onMeasure if in onDraw when I draw it, is working as well) in custom component class. When exactly I need to do that?
onMeasure() is your opportunity to tell Android how big you want your custom view to be dependent the layout constraints provided by the parent; it is also your custom view's opportunity to learn what those layout constraints are (in case you want to behave differently in a match_parent situation than a wrap_content situation). These constraints are packaged up into the MeasureSpec values that are passed into the method. Here is a rough correlation of the mode values:
EXACTLY means the layout_width or layout_height value was set to a specific value. You should probably make your view this size. This can also get triggered when match_parent is used, to set the size exactly to the parent view (this is layout dependent in the framework).
AT_MOST typically means the layout_width or layout_height value was set to match_parent or wrap_content where a maximum size is needed (this is layout dependent in the framework), and the size of the parent dimension is the value. You should not be any larger than this size.
UNSPECIFIED typically means the layout_width or layout_height value was set to wrap_content with no restrictions. You can be whatever size you would like. Some layouts also use this callback to figure out your desired size before determine what specs to actually pass you again in a second measure request.
The contract that exists with onMeasure() is that setMeasuredDimension() MUST be called at the end with the size you would like the view to be. This method is called by all the framework implementations, including the default implementation found in View, which is why it is safe to call super instead if that fits your use case.
Granted, because the framework does apply a default implementation, it may not be necessary for you to override this method, but you may see clipping in cases where the view space is smaller than your content if you do not, and if you lay out your custom view with wrap_content in both directions, your view may not show up at all because the framework doesn't know how large it is!
Generally, if you are overriding View and not another existing widget, it is probably a good idea to provide an implementation, even if it is as simple as something like this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 100;
int desiredHeight = 100;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
actually, your answer is not complete as the values also depend on the wrapping container. In case of relative or linear layouts, the values behave like this:
EXACTLY match_parent is EXACTLY + size of the parent
AT_MOST wrap_content results in an AT_MOST MeasureSpec
UNSPECIFIED never triggered
In case of an horizontal scroll view, your code will work.
If you don't need to change something onMeasure - there's absolutely no need for you to override it.
Devunwired code (the selected and most voted answer here) is almost identical to what the SDK implementation already does for you (and I checked - it had done that since 2009).
You can check the onMeasure method here :
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Overriding SDK code to be replaced with the exact same code makes no sense.
This official doc's piece that claims "the default onMeasure() will always set a size of 100x100" - is wrong.