Get dimension of screen area used by activity - android

How do I determine the dimensions (width and height) of an activity during onCreate. The dimension must not be that of the entire screen but only of area that is used by the activity. A few methods I tried posted on SO all end up giving the device's maximum screen sizes.

Have you tried (not exactly onCreate):
activityRootView = findViewById(R.id.yourRootView);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//activityRootView.getHeight();
//...
}
}
});
And ofcourse, your activitys main layout has to match parent or fill content

The entire user content goes to android.R.id.content so what you can do is
final Rect rectgle = new Rect();
LinearLayout mContent = (LinearLayout)findViewById(android.R.id.content).getParent();
mContent.getWindowVisibleDisplayFrame(rectgle);
// and use
//rectgle.top;
//rectgle.left
//rectgle.right
this includes action bar if you want to exclude actionbar also directly use
//findViewById(android.R.id.content);
which returns a FrameLayout

Related

Programmatically add items to scrollview top, but should not scroll to top

I have scrollview with child LinearLayout . I am adding data programmaticaly to it. When i add some data to top of linearlayout it automatically scrolls to top element. But i want something like , user reaches top -> scrolls upside to load previous data ->add data to linearlayout top but should not get focus, after addition complete , if user scrolls then and then only it should display .
How to achieve this?
Well I thought of a way and it works almost perfectly.
I have a LinearLayout (llCommunicationsLayout) inside a ScrollView (svCommunications) .
I inflate a new LinearLayout, I'm going to add views to the top of this new LinearLayout and then add this new layout to the LinearLayout inside the ScrollView.
This is the new layout:
final LinearLayout wrapperLayout = (LinearLayout) mInflater.inflate(R.layout.empty_layout, null);
I add my views to the 0'th position of this layout.
wrapperLayout.addView(view, 0);
After all the views are added into the wrapperLayout, I add the wrapperLayout into the llCommunicationsLayout (the one inside my ScrollView)
llCommunicationsLayout.addView(wrapperLayout, 0);
After this, I calculate their heights after the wrapperLayout is on screen (has a measurable height)
wrapperLayout.post(new Runnable() {
#Override
public void run() {
int wrapperHeight = wrapperLayout.getHeight();
int svHeight = svCommunications.getHeight();
int scrollHeight = Math.abs(svHeight - wrapperHeight);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int displayHeight = displaymetrics.heightPixels / 4;
svCommunications.scrollTo(0, (scrollHeight + displayHeight));
}
});
Here, I get both the newly added layout's and the ScrollView's heights.
I calculate their difference, add the 1/4 of the height of the screen of my device and scroll it, voila!
It's not perfect, but after the layouts are added, it no longer scrolls to the top of the screen. Experiment with the displayHeight for different results.
Hope this helps someone out.
You can grab the current view which is on top of your LinearLayout then add new content to your LinearLayout and then scroll back to view which was previously on top. The code would be something like:
public void addViewsOnTop(List<View> views) {
final View currentViewOnTop = (linearLayout.getChildCount() > 0) ? linearLayout.getChildAt(0) : null;
// Add Views. Note that views will appear in reverse order
for(View view : views) {
linearLayout.addView(view, 0);
}
// Scroll back to view which was on top
if(currentViewOnTop != null) {
new Handler().post(new Runnable() {
#Override
public void run() {
scrollView.scrollTo(0, currentViewOnTop.getBottom());
}
});
}
}
Solved....
Try this, worked for me ,
lv_chat.setAdapter(adapter);
lv_chat.setSelection(somePreviousPosition);

Get content view size in onCreate

I'm looking for a good way to measure the dimensions of the actual content area for an activity in Android.
Getting display always works. Simply go like this:
Display display = getWindowManager().getDefaultDisplay();
And you can get the pixel count for the entire screen. Of course this does not take into consideration the ActionBar, status bar, or any other views which will reduce the available size of the activity itself.
Once the activity is running, you can do this:
View content = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
To get the activity content only. But doing this in onCreate() will result in a view with width and height of 0, 0.
Is there a way to get these dimensions during onCreate? I imagine there ought to be a way to get the measurements of any status bars and just subtract that from the total display size, but I'm unable to find a way to do that. I think this would be the only way, because the content window method will always return a view with no width/height before it is drawn.
Thanks!
You can use a layout or pre-draw listener for this, depending on your goals. For example, in onCreate():
final View content = findViewById(android.R.id.content);
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Remove it here unless you want to get this callback for EVERY
//layout pass, which can get you into infinite loops if you ever
//modify the layout from within this method.
content.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//Now you can get the width and height from content
}
});
Update
as of API 16 removeGlobalOnLayoutListener is deprecated.
Change to:
content.getViewTreeObserver().removeOnGlobalLayoutListener(this)
(copied from my answer to a related question)
I use the following technique - post a runnable from onCreate() that will be executed when the view has been created:
contentView = findViewById(android.R.id.content);
contentView.post(new Runnable()
{
public void run()
{
contentHeight = contentView.getHeight();
}
});
This code will run on the main UI thread, after onCreate() has finished.
Answer with post is incorrect, because the size might not be recalculated.
Another important thing is that the view and all it ancestors must be visible. For that I use a property View.isShown.
Here is my kotlin function, that can be placed somewhere in utils:
fun View.onInitialized(onInit: () -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (isShown) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
onInit()
}
}
})
}
And the usage is:
myView.onInitialized {
Log.d(TAG, "width is: " + myView.width)
}
This appears to be a duplicate question. There is an elegant answer within this SO question:
getWidth() and getHeight() of View returns 0
Which (shamelessly copied) is to override onWindowFocusChanged(), which seems to fire just after onCreate(), and where the sizes are rendered:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//Here you can get the size!
}
If you want lo load a Bitmap into a ImageView within OnCreate(), you can use this example to do it:
public static void setImageLater(#NonNull final ImageView imageView,final Bitmap bitmap){
final ViewTreeObserver observer = imageView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
imageView.setImageBitmap(bitmap);
}
});
}
You can do any work wich needs sizes in onResume(using flag like alreadyDone not to repeat it every time an Activity goes foreground). In onCreate views are not displayed, so it's normal that sizes are zeros.

Getting window size before adding controls

I have a custom view with a public function that adds a control as a child of the view, and I want to call it from my activity. The problem is that I need to know the size of the view in the function in order to place the control. I can't override onMeasure to get the measures because my view inherits from another custom view in which this function is final. I tried overriding measureChildren, but it gets called too late (even after onResume on the activity in which the view is placed in). What can I do in order to have the size before the activity calls the function in the view?
One possibility is to measure your view back in the activity then set properties of the view for it's internal methods to use.
Using a global layout listener has always worked well for me. It has the advantage of being able to remeasure things if the layout is changed, e.g. if something is set to View.GONE or child views are added/removed.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null);
// 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() {
// at this point, the UI is fully displayed
}
}
);
setContentView(mainLayout);
http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html
If you want the the display dimensions in pixels you can use getSize:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;

When do we know when an Activity's views have been layed out

I have an Activity with a view hierarchy as follows:
<RelativeLayout>
<LinearLayout>
<GridLayout />
</LinearLayout>
</RelativeLayout>
The top RelativeLayout takes up the full screen while the LinearLayout takes up a subset of the screen. I'm trying to set the size of the children views of the GridLayout in such a way that with n rows and m columns the size of cells is directly related to the n,m values and the width and height of the LinearLayout.
So for example, I'm trying to get the cell width and height as follows:
int linearLayoutWidth = mLinearLayout.getMeasuredWidth();
int linearLayoutHeight = mLinearLayout.getMeasuredHeight();
...
int rows = mGridModel.getRows() + 1;
int columns = mGridModel.getColumns() + 1;
int cellWidth = (int) (linearLayoutWidth / columns);
int cellHeight = (int) (linearLayoutHeight / rows);
Unfortunately, mLinearLayout.getMeasuredWidth() does not return the correct size when the view is created. It always returns the actual device's screen width until the views have been fully laid out - in which case it is too late. I have already given my children cell views the size based on the initially completely incorrect values.
My question is - how do I know when the views have been FULLY laid out. I need to query for their correct or actual measured width/height.
setContentView() creates the views and theoretically lays them out. But they views are not guaranteed to be in their final correct locations/sizes yet.
You can add ViewObserver to any view, in this observer there is a callback method onGlobalLayout, invoked when Layout has been laid;
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//in here, place the code that requires you to know the dimensions.
//this will be called as the layout is finished, prior to displaying.
}
}
Extending jeet's answer, you can have your activity implement the listener, which leads to slightly nicer code.
public class MyActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
view.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
#Override
public void onGlobalLayout() {
...
}
}

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