Final width of Imageview - android

When I set an ImageView bitmap on create of an Activity. At what point can I read the final width of the ImageView?
What seems to happen is the bitmap is set in the onCreate() method and immediately is assigned the width of 965 which I can see when entering the onLayout method of the ImageView, then more of the layout/creation occurs and the ImageView width changes to 909 (I guess this is due to the adjustment to the screen size?).
What I want to do is read the final value of 909 to set the width of another view programmatically.
I've tried the addOnGlobalLayoutListener of the ImageView to try to listen of the completion of the layout, however reading the getMeasuredWidth() method of the ImageView still returns the 965 value.
What is the final event called once the layout calculations are completing which will allow me to set the width of another view during the creation.

You need to wait for Activity window to be attached and then get the ImageView width. To do that, you can override onWindowFocusChanged() method of the Activity:
#Override
public void onWindowFocusChanged(boolean hasFocus){
int imageWidth = yourImageView.getWidth();
}
As a second suggestion:
You can also add a OnPreDrawListener and use his onPreDraw() method
At this point, all views in the tree have been measured and given a
frame
ViewTreeObserver vto = yourImageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int imageWidth = yourImageView.getMeasuredWidth();
return true;
}
});

You can access the final width in the onStart() once the onCreate() has finished.

Related

Android ImageView some kind of size changed event?

I have an ImageView which has layout_width of fill_parent. I want to get the width of this ImageView in order to calculate the size of the thumbnail. The problem is that imageView.getWidth() and imageView.getHeight() both return 0 because the layout hasn't been sat yet.
My question is, does the ImageView has some kind of SizeChanged event or something of the kind? How do I react as soon as the ImageView width has been established?
Update:
I solved my problem this way. As I actually only needed the width of the ImageView which is set to fill_parent, what I actually needed was the device window size:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int targetW = metrics.widthPixels;
You can hook into a number of View tree events using a ViewTreeObserver. Specifically, either an OnGlobalLayoutListener or an OnPreDrawListener should work for you depending on what you are doing with the size.
Here's an example of using a ViewTreeObserverto get a View's width once everything has been laid out:
final ImageView myImageView = (ImageView) findViewById(R.id.image);
final ViewTreeObserver observer = myImageView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int height = myImageView.getHeight();
// Do something with the height
// Remove the layout listener so we don't waste time on future passes
observer.removeOnGlobalLayoutListener(this);
}
});
According to this post's answer:
ImageView.getWidth() returns 0
You will need to call it from the following function:
public void onWindowFocusChanged(boolean hasFocus)
This is because the window has not yet been attached and as you have noticed your imageview will have no dimensions yet.

How do I change the dimensions of a View programmatically?

I just want an empty view with certain dimensions.
I declare the reference variable here:
public static View sv;
I instantiate the view in the onCreate method of my activity:
sv = new View(this);
and then attempt to set the dimensions right below it:
sv.setLayoutParams(new FrameLayout.LayoutParams(100, 100));
but when I call sv.getWidth() and sv.getHeight(), they still return 0
The height and width only will have value after of the OnCreate() and OnResume(). Try get height and width after this methods.
UPDATED
You can use a layout listener for this, depending on your goals. For example, in onCreate():
final View view = findViewById(android.R.id.view);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
//Now you can change the dimensions
}
});

When can one obtain view dimensions in an activity?

In my app, I need to set the width of a view based on the width of another view. However, Activity's onCreate() method does not seem to be a good place to do so. The view's width via getWidth() returns 0. Other methods onStart() and onResume() also behave similarly.
I am wondering if there is any method on an Activity that is called after a view has been initialized? Or, is there is another way I can achieve my objective.
Try onGlobalLayoutListener. Get the instance of your main root view, and then just use addOnGlobalLayoutListener() method.
You will receive a callback when your views are already created and measured on Screen:
http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html
Declare onGlobalLayoutListener in onCreate method:
View firstView = (View) findViewById(R.id.firstView);
View secondView = (View) findViewById(R.id.secondView);
firstView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Ensure you call it only once :
firstView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = firstView.getWidth();
int height = firstView.getHeight();
// set dimensions of another view with that dimensions
secondView.setWidth(width);
secondView.setHeight(height);
}
});

When android calculate width and height of views

I create my hierarchy of views by code, and then I do setContentView()in the Activity with my root view like argument.
I need know width and height of one view in runtime but if i do getWidth() or getHeight(), i get 0. If i wait a few seconds i get the correct width or height.
I only want to know in what moment android calculate width / height of views. My code isn't outstanding
Thanks!
This is because in onCreate() the layouts haven't been calculated yet. So you need to add a GlobalLayoutListener to know when layouts has been calculated and placed in screen.
final View view = findViewById(R.id.root);
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeGlobalOnLayoutListener(this);
}
});
Where root is the root layout (LinearLayout, RelativeLayout i.e). Assign your root layout with the #+id/root.
You don't say where you are trying to measure the view but my guess is it's in onCreate() or onResume()>
Try this in your onCreate().
// set a global layout listener which will be called when the layout pass is completed and the view is drawn
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
// measure your views here
}
}
Here, mainLayout is a reference to the root view group of the layout.
This will also be called if the layout is resized after it's first drawn (views added in code, orientation changes etc) so you will always get the correct values.
You can use some View class. add this :
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super(w,h,oldw,oldh);
if(oldw == 0 && w !=0){
...
// w and h is what you want
}
For clarity, according to this Android documentation
When an Activity receives focus, it will be requested to draw its
layout.
...and also measure view dimensions.
Focus receives after onResume and lose it after onPause method.

How to retrieve the dimensions of a view?

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.

Categories

Resources