Related
I am currently developing an application in which I use a heavily modified Split Action Bar. Here is a link to the app's current state:
You'll notice a transparent action bar up top, with a custom view inflated into it, with a hacked together split action bar on bottom. The bottom view is actually a single action item with a custom view inflated into it and showAlways=true.
Currently I only support SDK v15+ and I don't really plan on changing that, but with the Lollipop AppCompat library that just released, I decided to implement it, so I could get some of that awesomeness in my app.
I've changed my theme to Theme.AppCompat.Light, and my MainActivity now extends ActionBarActivity instead of Activity.
All references to getActionBar have now been switched to getSupportActionBar, and with only those changes, this is what my activity now looks like:
You'll notice I got a UI dump from the Device Monitor, and it's shoving the bottom action bar into a weird space and calling that the action bar, and getting rid of my top custom view.
Here is my code for setting up my action bar:
public void initializeActionBar(){
View customNav = LayoutInflater.from(this).inflate(R.layout.action_bar_top, null);
actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent_fifty_percent));
final PopupWindow window = addPopupWindow();
actionBarOptions = (ImageView)customNav.findViewById(R.id.options);
actionBarOptions.setVisibility(View.GONE);
actionBarOptions.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.vertical_ellipsis, app.scaleByDensity(48)));
actionBarOptions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
window.showAsDropDown(actionBarOptions, 0, 0);
}
});
TextView title = (TextView) customNav.findViewById(R.id.screen_title);
Typeface font1 = Typeface.createFromAsset(getAssets(), "Merriweather-Italic.ttf");
title.setText("Parsley");
title.setTypeface(font1);
actionBar.setCustomView(customNav);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayUseLogoEnabled(false);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(this).inflate(R.menu.test, menu);
LinearLayout fullMenu = (LinearLayout) menu.findItem(R.id.full_menu).getActionView();
ViewGroup.LayoutParams params;
icon1 = (ImageView) fullMenu.findViewById(R.id.action_item1);
params = icon1.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
icon1.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.shopping_list_icon, app.scaleByDensity(32)));
icon2 = (ImageView) fullMenu.findViewById(R.id.action_item2);
icon3 = (ImageView) fullMenu.findViewById(R.id.action_item3);
icon4 = (ImageView) fullMenu.findViewById(R.id.action_item4);
icon2.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.recipe_box_icon, app.scaleByDensity(32)));
icon3.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_search, app.scaleByDensity(32)));
icon4.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_add, app.scaleByDensity(32)));
params = icon2.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon3.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon4.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
if (!firstLoad) {
setBottomActionBarActive();
setActiveTab(0);
}
optionsLoaded = true;
return true;
}
initializeActionBar() is called from onCreate in my activity. Any ideas what I'm doing wrong?
Toolbar should be used. In your case it's one toolbar at the top, and one at the bottom. Check android team blog, they have nice integration guide.
If you just want your bottom action bar back, you can simply change back to appcompat v7:20 ,and it works for me. The problem is split action bar is no longer being supported in appcomat v7:21.
While user482277's solution may work for instances with a more traditional split action bar, utilizing action items, navigation drawer, etc, it didn't quite work for me. What I ended up doing was building a pair of custom (compound really) views to emulate both the top and bottom action bar. I found this situation to work much better, especially with backwards compatibility. I don't have to worry about earlier versions supporting action bar, because at the end of the day, it's just a pair of classes that extend LinearLayout. In addition, I don't have to worry about different screen sizes (particularly tablets) not supporting the split version.
I have a spinner which currently obscures some text below the spinner when opened. I need to limit the maximum drop-down length of the spinner, either through java code or through XML, so that it does not obscure this text.
The current design is the left example while the desired design is on the right.
How do I go about limiting how far the spinner drops down to when opened? At present, it drops down to fill the entire portion of screen below it.
One way to achieve this is to use ActionBarSherlock's IcsSpinner. I made the needed modifications to limit the size of the spinner and that seems to work nicely.
Make the following modifications to IcsListPopupWindow.
Add an instance variable:
private int mDropDownVerticalOffsetBottom;
Add a method to set this variable:
public void setVerticalOffsetBottom(int offsetBottom) {
mDropDownVerticalOffsetBottom = offsetBottom;
}
Alter the call to getMaxAvailableHeight to (mDropDownVerticalOffsetBottom was added):
final int maxHeight = /*mPopup.*/getMaxAvailableHeight(
mDropDownAnchorView, mDropDownVerticalOffset, mDropDownVerticalOffsetBottom, ignoreBottomDecorations);
Change the method's signature to include that variable:
private int getMaxAvailableHeight(View anchor, int yOffset, int yOffsetBottom, boolean ignoreBottomDecorations) {
And consider that offset when computing the distance to the bottom:
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset - yOffsetBottom;
Now modify IcsSpinner.java to implement the setter method for the offset:
private DropdownPopup mPopup; // <- needs to be a DropdownPopup instead of a SpinnerPopup
public void setVerticalOffsetBottom(int offsetBottom) {
mPopup.setVerticalOffsetBottom(offsetBottom);
}
Now "all" you need to do is to set the offset to the correct value. Here's how I did it (I tested it and it worked on two test devices):
final View root = findViewById(R.id.root_layout);
final View view = findViewById(R.id.view_not_to_be_obscured);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int[] locations = new int[2];
root.getLocationOnScreen(locations);
int y1 = locations[1];
int h1 = root.getHeight();
view.getLocationOnScreen(locations);
int y2 = locations[1];
int offset = y1 + h1 - y2;
// here we initialize the spinner
}
});
The assumption is that root_layout fills the whole window excluding all decorating elements.
The final step is to create the spinner itself:
// actionDropDownStyle is an attribute set in the theme to e.g. #style/Widget.Sherlock.Spinner.DropDown.ActionBar or #style/Widget.Sherlock.Light.Spinner.DropDown.ActionBar for light theme
IcsSpinner spinner = new IcsSpinner(context, null, R.attr.actionDropDownStyle);
// yes here we set the offset!
spinner.setVerticalOffsetBottom(offset);
spinner.setPadding(spinner.getPaddingLeft(), 0, spinner.getPaddingRight(), 0);
spinner.setId(R.id.some_id);
spinner.setAdapter(yourAdapter); // you can use a simple ArrayAdapter or whatever you need
// create ICS LinearLayout
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
IcsLinearLayout linearLayout = (IcsLinearLayout) inflater.inflate(R.layout.abs__action_bar_tab_bar_view, null);
linearLayout .setPadding(listNavLayout.getPaddingLeft(), 0, listNavLayout.getPaddingRight(), 0);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
linearLayout .addView(spinner, params);
Now that might look complicated but as someone else mentioned, you'll not be able to achieve this without your own spinner implementation and since ActionBarSherlock comes with one, why not use it? It's certainly less work than writing your own one. If you don't use the library for the ActionBar strip away all resource files you don't need and use Proguard to strip away all unused classes.
You could probably achieve the same using AppCompat (see here: https://github.com/android/platform_frameworks_support/tree/master/v7/appcompat/src/android/support).
I've searched quite a bit and it seems that I can't find a solution to this anywhere. You may have to go a slightly different route by recreating the functionality of a spinner, but using buttons and dialogs intead.
Assuming you're using an ArrayAdapter for the spinner, try this:
Replace your Spinner with a button that has a down arrow icon.
Create a custom Dialog or AlertDialog and populate a ListView with the ArrayAdapter.
For the onClick method, push the value of the selected option and populate a static variable and the text of the button with the chosen value. This way, you can make the size of the dialog box any size you want and the excess choices will always be scrollable.
I know it's not ideal, but I haven't found any way to change the height of the "dropdown" portion of a spinner.
Sorry if this wasn't what you were hoping for.
EDIT: Someone else asked the exact same question here before and it turns out they went with the exact method I just described. You can see the sample code they used here: Check out the question/code
in Kotlin, This what i've done to set dropdwon spinner height.
Replace "spinner_empresas" with your spinner id.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.setOnClickListener(this)
binding?.spinnerEmpresas?.adapter = ArrayAdapter(this,
android.R.layout.simple_spinner_item, arrayOf("items"))
val popup: Field =
AppCompatSpinner::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val popupWindow = popup[spinner_empresas] as
androidx.appcompat.widget.ListPopupWindow
popupWindow.height = 700
}
}
you can use relfection.
Spinner spinner = (Spinner) findViewById(R.id.spinner);
try {
Field popup = Spinner.class.getDeclaredField("mPopup");
popup.setAccessible(true);
// Get private mPopup member variable and try cast to ListPopupWindow
android.widget.ListPopupWindow popupWindow = (android.widget.ListPopupWindow) popup.get(spinner);
// Set popupWindow height to 500px
popupWindow.setHeight(500);
}
catch (NoClassDefFoundError | ClassCastException | NoSuchFieldException | IllegalAccessException e) {
// silently fail...
}
But if you are using androidx.appCompat 1.1.0 then you have to use ->
I can't use API reflection on androidx.appcompat:appcompat:1.1.0
Regards
I think you have to use custom spinner for your requirement .There is not any inbuilt method or xml property that can help.
See this example for help .
https://github.com/ankitthakkar/DropDownSpinner
Hope this will work.
Note: This is changed answer.
Is there a way to know the size of the keyboard that is shown in the screen?
I am using Cocos2dx for programming, but I want to know the height of the keyboard shown in screen in the part of Android or the part of Cocos, it does not matter.
I know that Keyboard has a getHeight() method but I don't want to create new keyboards, i want to use the default one.
We did it with this
myLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
parent.getWindowVisibleDisplayFrame(r);
int screenHeight = parent.getRootView().getHeight();
int heightDifference = screenHeight - (r.bottom - r.top);
Log.d("Keyboard Size", "Size: " + heightDifference);
}
});
We only resize views with the keyboard, so we could use this.
Rect r = new Rect();
View rootview = this.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
Result of this is the amount of space your application uses on screen (works even when activity is not resized). Obviously remaining screen space will be used by the keyboard ( if its visible)
Found id up here: https://github.com/freshplanet/ANE-KeyboardSize/blob/master/android/src/com/freshplanet/ane/KeyboardSize/getKeyboardY.java
if your activity is not fullscreen, using code below:
content.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
if (keyBoardHeight <= 100) {
Rect r = new Rect();
content.getWindowVisibleDisplayFrame(r);
int screenHeight = content.getRootView()
.getHeight();
int heightDifference = screenHeight
- (r.bottom - r.top);
int resourceId = getResources()
.getIdentifier("status_bar_height",
"dimen", "android");
if (resourceId > 0) {
heightDifference -= getResources()
.getDimensionPixelSize(resourceId);
}
if (heightDifference > 100) {
keyBoardHeight = heightDifference;
}
Log.d("Keyboard Size", "Size: " + heightDifference);
}
// boolean visible = heightDiff > screenHeight / 3;
}
});
If you want to calculate the Virtual Keyboard height while your activity does not change in size (adjustPan) then you can use this sample:
https://github.com/siebeprojects/samples-keyboardheight
It uses a hidden window in order to calculate the height difference between the window and the root view of the activity.
You can't tell. No, really: you simply can't tell.
The keyboard does not need to be any particular shape. It does not have to be placed at the bottom of the screen (many of the most popular options are not), it does not have to keep its current size when you change text fields (almost none do depending on the flags). It does not even have to be rectangular. It may also just take over the entire screen.
I know this is an old post, but I noticed that the chosen solution for me did not work on all devices. There seemed to be a discrepancy and so I implemented this and it seems to be a catch all:
final int[] discrepancy = new int[1];
discrepancy[0] = 0;
// this gets the height of the keyboard
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
View rootview = activity.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
int screen_height = rootview.getRootView().getHeight();
int keyboard_height = screen_height - (r.bottom + r.top) - discrepancy[0];
if (discrepancy[0] == 0) {
discrepancy[0] = keyboard_height;
if (keyboard_height == 0) discrepancy[0] = 1;
}
int margin_bottom = keyboard_height + Helper.getDp(10, activity);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) carousel_container.getLayoutParams();
params.setMargins(0, 0, 0, margin_bottom);
//boolean visible = heightDiff > screenHeight / 3;
}
});
When the listener is first called it measures the screen without a keyboard and if there is a discrepancy I account for it the next time around. If there is no discrepancy I set the discrepancy to 1 just so it is no longer 0.
After 2020, if your min SDK large or equal then 21, you can check the visibility and height of IME by below functions:
fun isKeyboardVisible(attachedView: View): Boolean {
val insets = ViewCompat.getRootWindowInsets(attachedView)
return insets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
}
fun getKeyboardHeight(attachedView: View): Int {
val insets = ViewCompat.getRootWindowInsets(attachedView)
return insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
}
Ref: Animating your keyboard (part 1). New WindowInsets APIs for checking theā¦ | by Chris Banes | Android Developers | Medium
in cocos2d-x we have got CCEditBox.
Inside Extensions->GUI->CCEditBox, you can find the class CCEditBox.
The beauty is that it hides the keyboard of tapping somewhere else on the scene. and automatically moves the keyboard up incase your edit box was placed too low on the scene.
If you are using cocos2d-x v2.1.3 then you can navigate to sample Project by going to
samples->cpp->TestCpp->Classes->ExtensionTest->EditBoxTest.
I'm just going to use it instead of CCTextField from now on. just came across it yesterday :)
After hours of searching I found a solution if you want to set windowSoftInput="adjustPan"
Here is the code snippet:
final View root = findViewById(android.R.id.content);
root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
Rect r = new Rect();
{
root.getWindowVisibleDisplayFrame(r);
}
#Override
public void onGlobalLayout() {
Rect r2 = new Rect();
root.getWindowVisibleDisplayFrame(r2);
int keyboardHeight = r.height() - r2.height();
if (keyboardHeight > 100) {
root.scrollTo(0, keyboardHeight);
}
else {
root.scrollTo(0, 0);
}
}
});
In this code, after I found the keyboard height I scroll the view up to not covered by the keyboard which is the main reason for finding the keyboard height.
According to the docs :
void getWindowVisibleDisplayFrame(Rect outRect) : Retrieve the overall visible display size in which the window this view is attached to has been positioned in.
The ROOT_VIEW of an android display screen can be visualized as being a single screen view with VISIBLE DISPLAY FRAME which displays your activity's view.
This VISIBLE DISPLAY FRAME is adjusted when SOFT KEYBOARD is displayed or hidden from the screen.
NOTE : Please look at the two images by clicking on the links given below for better understanding
So the ROOT VIEW of a display screen can be visualized as :
RootView of display screen
The adjustment of VISIBLE DISPLAY FRAME with the opening and closing of SOFT KEYBOARD can be visualized as :
VISIBLE_DISPLAY_SCREEN adjustment
This adjustment of the VISUAL DISPLAY FRAME can be very well used to find out the height of the keyboard as :
(when the soft keyboard is open)
SOFT_KEYBOARD_HEIGHT = ROOT_VIEW_HEIGHT - (VISUAL_DISPLAY_FRAME_HEIGHT + EXTRA_SCREEN_HEIGHT)
The code to achieve the above is :
int mExtraScreenHeight=-1, mKeyboardHeight=-1;
boolean mKeyboardOpen;
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int rootViewHeight, visibleDisplayFrameHeight, fakeHeight;
/* (rootViewHeight - visibleDisplayFrameHeight) is not the real height of the keyboard
it is the fake height as it also consist of extra screen height
so FAKE_HEIGHT = KEYBOARD_HEIGHT + EXTRA_SCREEN_HEIGHT
To get keyboard height extra screen height must be removed from fake height
*/
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
rootViewHeight = rootView.getRootView().getHeight();
visibleDisplayFrameHeight = rect.height();
fakeHeight = rootViewHeight-visibleDisplayFrameHeight;
if (mExtraScreenHeight == -1){
mExtraScreenHeight=fakeHeight;
}
/* Suppose the soft keyboard is open then the VISIBLE_DISPLAY_FRAME is in reduced size
due to the space taken up by extra screen and the keyboard but when the soft keyboard closes
then KEYBOARD_HEIGHT=0 and thus FAKE_HEIGHT = EXTRA_SCREEN_HEIGHT
*/
else if (fakeHeight <= mExtraScreenHeight){
mExtraScreenHeight=fakeHeight;
mKeypadOpen=false;
}
else if (fakeHeight > mExtraScreenHeight){
mKeypadHeight=fakeHeight-mExtraScreenHeight;
mKeypadOpen=true;
}
}
});
NOTE : The onGlobalLayout() function will be called only when the global layout changes like when the soft keyboard opens. So the soft keyboard must be open at least once to get the soft keyboard height.
It worked for me ;)
Sorry for not being able to comment, two or three of the answers helped me solve my issue and they were related to using the AddOnGlobalLayoutListener and then determining the remaining height before and after a keyboard showed up.
The solution I used was based off of Rudy_TM's answer.
HOWEVER, one thing that I had to find was that in order for that method to work, you must have the following line somewhere
Window.SetSoftInputMode(SoftInput.AdjustResize);
Before I had SoftInput.AdjustNothing (or something like that) and it would not work. Now it works perfect. Thanks for the answers!
Complete answer & worked perfectly for me:
Rect r = new Rect();
View rootview = this.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
int keyboardHeight = rootview.getHeight() - r.bottom;
I have a view made up of TableLayout, TableRow and TextView. I want it to look like a grid. I need to get the height and width of this grid. The methods getHeight() and getWidth() always return 0. This happens when I format the grid dynamically and also when I use an XML version.
How to retrieve the dimensions for a view?
Here is my test program I used in Debug to check the results:
import android.app.Activity;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TextView;
public class appwig extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
int vh = 0;
int vw = 0;
//Test-1 used the xml layout (which is displayed on the screen):
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight(); //<- getHeight returned 0, Why?
vw = tl.getWidth(); //<- getWidth returned 0, Why?
//Test-2 used a simple dynamically generated view:
TextView tv = new TextView(this);
tv.setHeight(20);
tv.setWidth(20);
vh = tv.getHeight(); //<- getHeight returned 0, Why?
vw = tv.getWidth(); //<- getWidth returned 0, Why?
} //eof method
} //eof class
I believe the OP is long gone, but in case this answer is able to help future searchers, I thought I'd post a solution that I have found. I have added this code into my onCreate() method:
EDITED: 07/05/11 to include code from comments:
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
LayerDrawable ld = (LayerDrawable)tv.getBackground();
ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
ViewTreeObserver obs = tv.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
}
});
First I get a final reference to my TextView (to access in the onGlobalLayout() method). Next, I get the ViewTreeObserver from my TextView, and add an OnGlobalLayoutListener, overriding onGLobalLayout (there does not seem to be a superclass method to invoke here...) and adding my code which requires knowing the measurements of the view into this listener. All works as expected for me, so I hope that this is able to help.
I'll just add an alternative solution, override your activity's onWindowFocusChanged method and you will be able to get the values of getHeight(), getWidth() from there.
#Override
public void onWindowFocusChanged (boolean hasFocus) {
// the height will be set at this point
int height = myEverySoTallView.getMeasuredHeight();
}
You are trying to get width and height of an elements, that weren't drawn yet.
If you use debug and stop at some point, you'll see, that your device screen is still empty, that's because your elements weren't drawn yet, so you can't get width and height of something, that doesn't yet exist.
And, I might be wrong, but setWidth() is not always respected, Layout lays out it's children and decides how to measure them (calling child.measure()), so If you set setWidth(), you are not guaranteed to get this width after element will be drawn.
What you need, is to use getMeasuredWidth() (the most recent measure of your View) somewhere after the view was actually drawn.
Look into Activity lifecycle for finding the best moment.
http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
I believe a good practice is to use OnGlobalLayoutListener like this:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!mMeasured) {
// Here your view is already layed out and measured for the first time
mMeasured = true; // Some optional flag to mark, that we already got the sizes
}
}
});
You can place this code directly in onCreate(), and it will be invoked when views will be laid out.
Use the View's post method like this
post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
}
});
I tried to use onGlobalLayout() to do some custom formatting of a TextView, but as #George Bailey noticed, onGlobalLayout() is indeed called twice: once on the initial layout path, and second time after modifying the text.
View.onSizeChanged() works better for me because if I modify the text there, the method is called only once (during the layout pass). This required sub-classing of TextView, but on API Level 11+ View. addOnLayoutChangeListener() can be used to avoid sub-classing.
One more thing, in order to get correct width of the view in View.onSizeChanged(), the layout_width should be set to match_parent, not wrap_content.
Are you trying to get sizes in a constructor, or any other method that is run BEFORE you get the actual picture?
You won't be getting any dimensions before all components are actually measured (since your xml doesn't know about your display size, parents positions and whatever)
Try getting values after onSizeChanged() (though it can be called with zero), or just simply waiting when you'll get an actual image.
As F.X. mentioned, you can use an OnLayoutChangeListener to the view that you want to track itself
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) {
// Make changes
}
});
You can remove the listener in the callback if you only want the initial layout.
I guess this is what you need to look at: use onSizeChanged() of your view. Here is an EXTENDED code snippet on how to use onSizeChanged() to get your layout's or view's height and width dynamically http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html
ViewTreeObserver and onWindowFocusChanged() are not so necessary at all.
If you inflate the TextView as layout and/or put some content in it and set LayoutParams then you can use getMeasuredHeight() and getMeasuredWidth().
BUT you have to be careful with LinearLayouts (maybe also other ViewGroups). The issue there is, that you can get the width and height after onWindowFocusChanged() but if you try to add some views in it, then you can't get that information until everything have been drawn. I was trying to add multiple TextViews to LinearLayouts to mimic a FlowLayout (wrapping style) and so couldn't use Listeners. Once the process is started, it should continue synchronously. So in such case, you might want to keep the width in a variable to use it later, as during adding views to layout, you might need it.
Even though the proposed solution works, it might not be the best solution for every case because based on the documentation for ViewTreeObserver.OnGlobalLayoutListener
Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.
which means it gets called many times and not always the view is measured (it has its height and width determined)
An alternative is to use ViewTreeObserver.OnPreDrawListener which gets called only when the view is ready to be drawn and has all of its measurements.
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {
#Override
public void onPreDraw() {
tv.getViewTreeObserver().removeOnPreDrawListener(this);
// Your view will have valid height and width at this point
tv.getHeight();
tv.getWidth();
}
});
Height and width are zero because view has not been created by the time you are requesting it's height and width . One simplest solution is
view.post(new Runnable() {
#Override
public void run() {
view.getHeight(); //height is ready
view.getWidth(); //width is ready
}
});
This method is good as compared to other methods as it is short and crisp.
You should rather look at View lifecycle: http://developer.android.com/reference/android/view/View.html Generally you should not know width and height for sure until your activity comes to onResume state.
You can use a broadcast that is called in OnResume ()
For example:
int vh = 0;
int vw = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight();
vw = tl.getWidth();
}
}, new IntentFilter("Test"));
}
protected void onResume() {
super.onResume();
Intent it = new Intent("Test");
sendBroadcast(it);
}
You can not get the height of a view in OnCreate (), onStart (), or even in onResume () for the reason that kcoppock responded
Simple Response: This worked for me with no Problem.
It seems the key is to ensure that the View has focus before you getHeight etc. Do this by using the hasFocus() method, then using getHeight() method in that order. Just 3 lines of code required.
ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1);
myImageButton1.hasFocus();
int myButtonHeight = myImageButton1.getHeight();
Log.d("Button Height: ", ""+myButtonHeight );//Not required
Hope it helps.
Use getMeasuredWidth() and getMeasuredHeight() for your view.
Developer guide: View
CORRECTION:
I found out that the above solution is terrible. Especially when your phone is slow.
And here, I found another solution:
calculate out the px value of the element, including the margins and paddings:
dp to px:
https://stackoverflow.com/a/6327095/1982712
or dimens.xml to px:
https://stackoverflow.com/a/16276351/1982712
sp to px:
https://stackoverflow.com/a/9219417/1982712 (reverse the solution)
or dimens to px:
https://stackoverflow.com/a/16276351/1982712
and that's it.
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.