I want to get width and height of my layout after it had been drawn.
My code is actually called in the createView() method. But I want to wait until the layout is drawn before execute this code :
Log.i(TAG, "Height: "+myButton.getHeight()+" - width : "+myButton.getWidth());
// result is Height: 0 - width : 0
Is there any event launched after onCreateView() call ?
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() {
//Remove the listener before proceeding
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// measure your views here
}
}
);
setContentView(mainLayout);
Depending on your usage, you might want to remove the listener when you're done with it as shown.
createView does not exits. I suppose you mean Fragment.onCreateView
inside onCreateView,
onCreateView(...) {
View view = infalter.inflate...
final View button = view.findViewById
view.post(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Height: "+button.getHeight()+" - width : "+button.getWidth());
}
});
return view;
}
I think onWindowFocusChanged(boolean hasFocus) is what you are looking for.
Keep in mind that it is called whenever the window focus changes.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.i(TAG, "Height: "+myButton.getHeight()+" - width : "+myButton.getWidth());
}
Related
I need to set the width of a view based on the width of another view at run time.
I want to set the width in the onCreate() method,
textView1.setWidth(textView2.getWidth());
Since the width of view is still not fully constructed, when above code is executed, getWidth() returns zero.
I attempted the same code in the onPostCreate() method, but the problem persists.
Output of the following is 0 , 0.
protected void onPostCreate(BundlesavedInstanceState)
{
super.onPostCreate(savedInstanceState);
TextView textView = (TextView) findViewById(R.id.qwert);
string = string + " , " + Integer.toString(textView.getWidth());
Toast.makeText(this,string,Toast.LENGTH_LONG).show();
}
I thought onPostCreate() method will be called when all the views are completely rendered (inflated to their respective height and width).
Why is the onPostCreate() method not behaving as expected?
OnGlobalLayoutListener may give you what you need.
View myView=findViewById(R.id.myView);
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//At this point the layout is complete and the
//dimensions of myView and any child views are known.
}
});
You need to add some delay before getting width of view.
you can get width by doing like this in onCreate()
textView.post(new Runnable()
{
#Override
public void run()
{
int viewWidth = textView.getWidth();
}
});
I'm interested in what is the right and first possible moment to get size of first item of RecyclerView?
I've tried to use:
recyclerView.setLayoutManager(new GridLayoutManager(context, 2));
recyclerView.setAdapter(new MyDymmyGridRecyclerAdapter(context));
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
View firstRecyclerViewItem = recyclerView.getLayoutManager().findViewByPosition(0);
// firstRecyclerViewItem is null here
}
});
but it returns null at this moment.
If you're using OnGlobalLayoutListener you should remember onGlobalLayout can be called multiple times. Some of those calls can happen even before Layout is ready (and by ready I mean the moment when you can get dimensions of a View by calling view.getHeight() or view.getWidth()). So the proper way of implementing your approach would be:
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = recyclerView.getWidth();
int height = recyclerView.getHeight();
if (width > 0 && height > 0) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
View firstRecyclerViewItem = recyclerView.getLayoutManager().findViewByPosition(0);
}
});
Apart from that you still need to be sure that at the time of findViewByPosition(0) call:
Your RecyclerView's Adapter has at least one data element.
View at position 0 is currently visible in RecyclerView
Tell me if that fixes your issue, if not there is still another way of doing what you need.
In my extended RecyclerView I override onChildAttachedToWindow like this
#Override
public void onChildAttachedToWindow(View child) {
super.onChildAttachedToWindow(child);
if (!mIsChildHeightSet) {
// only need height of one child as they are all the same height
child.measure(0, 0);
// do stuff with child.getMeasuredHeight()
mIsChildHeightSet = true;
}
}
I had this type of problem. I want to perform click by default on the first visible position of the recylerview. I wrote the code for that on onResume but it did not work. I solved my problem by writting the code in onWindowFocusChanged method
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(isCalledForTheFirstTime)
{
LinearLayoutManager manager= (LinearLayoutManager) rcViewHeader.getLayoutManager();
int pos= manager.findFirstCompletelyVisibleItemPosition();
View view=manager.findViewByPosition(pos);
if(view!=null)
{
view.performClick();
}
// change the vaule so that it would not be call in case a pop up appear or disappear
isCalledForTheFirstTime=false;
}
}
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
}
});
I am working on a fragment and I want to get the dimension of a layout contained in the xml fragment layout.
When I try the code
RelativeLayout myLayout = view.findViewById(R.id.myLayout);
myLayout.getHeight();
it returns 0.
I need these dimensions to put inside myLayout other objects.
I try to use:
myLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
mHeight = myLayout.getHeight();
mWidth= myLayout.getWidth();
System.out.println("width: "+mWidth+" height: "+mHeight);
}
});
but this code is invoke a lot of time and I don't know exactly when it is execute.
I need these dimensions into public void onActivityCreated () method. Is it possible?
There's a cleaner solution to this, just use the View.post() method on your fragment's root view, and you can call getMeasuredHeight()/getMeasuredWidth() and get the actual values.
E.g.
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.myfragment, container, false);
root.post(new Runnable() {
#Override
public void run() {
// for instance
int height = root.getMeasuredHeight();
}
});
return root;
}
Very neat and tidy and no messy mucking about with the ViewTreeObserver.
I've only tested this with android-23 devices, but the API has had this method since level 1.
Anyway, WFM.
The addOnGlobalLayoutListener will be called whenever small change of the view happened. So you need to remove this listener from the view.
Simple usage:
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
if (Build.VERSION.SDK_INT < 16) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
I would suggest you to check :
if (myLayout.getHeight()>0 && myLayout.getWidth()>0) {
// Do some code...
removeOnGlobalLayoutListener(..)
}
In onActivityCreated, the view hasn't been measured yet.
So no, you can't get the measurements from there. onGlobalLayout is a good place to do it. Layouts happen directly after measurement.
You can remove your listener afterwards, if you only want to do it once. Note that layouts can change size though (soft keyboards appearing, for example)
I have home activity on which I have 3 buttons : Move To A , Move To B and Move To C.
On click of these buttons, I navigate to inner activity where I have 3 layouts within a HorizontalScrollView, say A, B and C which in turn consists of several views.
Now what I want is , inner activity should navigate to one of the 3 layouts based on clicked button from Home activity i.e.
If Move To A on home activity is pressed, inner activity should navigate to Layout A and so on...
Possible solution might be to use ScrollTo() but it isn't efficient with variable screen sizes.
Any help appreciated.
Edit
This doesn't work:
RelativeLayout LayoutA = (RelativeLayout) findViewById(R.id.LayoutA);
HorizontalScrollView main = (HorizontalScrollView) findViewById(R.id.scrollview);
main.scrollTo(PoCLayout.getLeft(), 0);
Even this doesn't work:
main.scrollTo(500, 0);
If main.scrollTo(500,0) does not work, try :
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(500, 0);
}
}
Edit : the working solution in the listener of the button :
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(layoutX.getLeft(), 0);
}
}
I'm not sure it will work, but you can try to get the position of your layout A, B or C calling the method getLeft() and then use scrollTo().
Example to scroll to layout A : scrollTo(a.getLeft(), 0);
You can do a little Math here
Use View.getLeft() and View.getRight() then divide calculated width per 2 to scroll to the View center
Use :
final HorizontalScrollView scrollView= (HorizontalScrollView)
int speed=1000;
findViewById(R.id.main_scrollView);
final View view = findViewById(R.id.your_view);
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ObjectAnimator.ofInt(scrollView, "scrollY", (int) view.getX()).setDuration(speed).start();
}
});
main.post(new Runnable() {
#Override
public void run() {
main.scrollTo(500,500);
}
}