In a fragment's view, I'd like to adjust the layout params of one of the child views based on the width of the containing view, whose layout parameters are set to match_parent. What I need to do is to call getWidth/getHeight for the parent and then set the child view's ViewGroup.LayoutParameters, etc.
The problem is that getWidth will only return a valid value after the layout has been drawn the first time and I'm not sure if there are any fragment overrides that I can use for doing this. For example, the view's actual width is not determined when onCreateView or onStart are called.
So is there a straightforward way to implement this, or am I going to have to subclass a view and override onLayout or onMeasure?
You could set a global layout listener on the view tree and do your diddling in there.
final ViewTreeObserver vto = myView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
// do layout tweaking based on measurements
// remove the listener... or we'll be doing this a lot.
vto.removeGlobalOnLayoutListener(this);
}
}
Related
Haven't found something to understand how to use it correctly.
I need to measure 2 views.
For first view i use this:
viewPager.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//my measure code
viewPager.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
So what should i do for second view: i should use another same block of code, but for second view or can i put second measurement inside viewpager's measurements?
Added: logically it can be different cases:
One view inside another view.
Two views near each other in same parent (viewObserver on first view).
Two views near each other in same parent (viewObserver on parent).
I have the following hierarchy in my layout:
ScrollView
RadioGroup
RelativeLayout
RadioButton
ImageView
RelativeLayout
RadioButton
ImageView
...
Now the point is, that it looks fine in the XML editor where RadioButtons and ImageViews have default values (placeholders) defined, but when I launch an activity and call removeAllViews() on the RadioGroup, all ImageViews disappear. What's interesting, all buttons get new values, only ImageViews are not updated anyhow (setting new source images gives no results).
So, my question is: does removeAllViews() erase the child views completely (like they never existed in a layout XML file) or just removes some values leaving the views' arguments to be defined (like setting new source image or new button description)?
From the official documentation, removeAllViews():
Call this method to remove all child views from the ViewGroup.
Calling this method will set all the childs view to null, so it will remove the child views from itself, and this child becomes not valid (or not considered as child), but not like it never existed in XML file.
This is removeAllViews() code:
public void removeAllViews() {
removeAllViewsInLayout();
requestLayout();
invalidate();
}
As you can see in removeAllViewsInLayout() method, it set the child value to null:
children[i] = null;
I have been spending hours unsuccessfully trying to adjust the width, height, and offset of a simple view in Android as the result of a button press. I have discovered that setTranslationX and setTranslationY always work; the legacy method of setLayoutParams never works once the view is laid out initially. Calls to requestLayout() and invalidate() similarly produce no results.
I have tried to setLayoutParams within the context of posting a runnable, but this does nothing.
Because setTranslationX always works, I would just use that, but unfortunately there is no equivalent method like setWidth or setHeight.
As you can see in the AOSP, setTranslationX makes a call to invalidateViewProperty, which is a private method of View.
Is there an equivalent method to setTranslationX to adjust a view width or view margin, that presumably triggers invalidateViewProperty, and, by extension, works reliably?
EDIT
While in some situations, setLayoutParams may be expected to work after the initial layout, I am in a situation where setLayoutParams has no effect after the initial layout, but setTranslationX does. My setup is as follows:
Running Android KitKat 4.4
The view in question is MATCH_PARENT for both width, height
The view in question is a child of a RelativeLayout
The view in question is a View class with a simple solid-color background drawable
Here is the view:
<View
android:id="#+id/border"
android:background="#drawable/match_background_border_transparent"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
And here is the (non-working) code meant to dynamically alter its margins, but has no effect. Again, if I call setTranslationX, that always works.
holder.toggleButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
imageBorder.post(new Runnable() {
#Override
public void run() {
RelativeLayout.LayoutParams p = (LayoutParams) imageBorder.getLayoutParams();
p.leftMargin = 20;
p.rightMargin = 20;
p.topMargin = 20;
p.bottomMargin = 20;
imageBorder.setLayoutParams(p);
// imageBorder.setTranslationX does have an effect if I included it here
}
});
}
});
I have determined why setTranslationX was working, but setLayoutParams was not. My views were ultimately descendents of an AdapterView. I was able to programmatically manipulate LayoutParams of the AdapterView and his siblings, but none of the AdapterView's descendents.
Additional research showed that this was a common Android question:
Margin on ListView items in android
Why LinearLayout's margin is being ignored if used as ListView row view
What was confusing was that this view was several levels deep; i.e., it went:
AdapterView -> FrameLayout -> RelativeLayout -> View
Anyhow, I was able to accomplish my programmatic layout goals by wrapping view in another view, and using setPadding.
I have seen some widgets using addView and sometimes addViewInLayout.
What is the difference between them?
What will happen if I replace one with the other?
Should I keep a flag during layout and use "addViewInLayout" or "addView" accordingly?
Thanks.
BR,
Henry
ps. add more tags: removeview, removeviewinlayout
Its generally a bad idea to call addView inside onLayout because addView internally triggers a requestLayout which eventually will call onLayout. So you end up triggering a layout while you are in the middle of a layout.
addViewInLayout is a "safer" version of the addView in the case you really have to add a new view in onLayout. It basically doesn't trigger a layout pass (doesn't call requestLayout internally).
See this video (by android engineer) for more detail: http://www.youtube.com/watch?v=HbAeTGoKG6k
addViewInLayout
Adds a view during layout. This is useful if in your onLayout() method, you need to add more views (as does the list view for example). If index is negative, it means put it at the end of the list.
addView
Assign the passed LayoutParams to the passed View and add the view to the window.
*Note that addView is implemented by ViewManager, an Interface to let you add and remove child views to an Activity, so you will be able to add views to ViewGroup at run time (DYNAMICALLY). Also note that addViewInLayout is a protected method of ViewGroup so if you are doing to create a custom view group you can call addViewInLayout() in onLayout() method.
For more see this
for example: we have a Layout (mLayout) and you want to add 2 views (view1, view2) into this layout.so there are 2 ways (the same)
case1: simply you use following command
mLayout.addView(view1); //onLayout() will be called first time
mLayout.addView(view2); //onLayout() will be called second time after the first time.
in this case, you don't care function onLayout(). it is simple source code.
case2: complicate but better performance
//do something to <global variable>
bCheck = true; //check it in fuction onLayout()
requestLayout(); //use this function to call onLayout() function for only one time
//in onLayout() function of mLayout, you use addViewInLayout()
//addViewInLayout() dont call onLayout() function, so you can add 2 views with only one time to call onLayout()
//onLayout() is abstract function, so mLayout is a instant of subclass of ViewGroup (ex: RelativeLayout, LinearLayout....)
#Override
onLayout(boolean changed, int l, int t, int r, int b)
if(bCheck == true){
v.addViewInLayout(view1); //add view1 to mLayout
v.addViewInLayout(view1); //add view2 to mLayout
bCheck = false;
}
}
});
I have no time to test it. anyone can help me make it more clear.
I have created linearlayout with layout:height="fill_parent" I have set 3 buttons and one TextView. I am able to see all. But when I want to get get the height of linearlayout. I used the following code in onCreate method. I am getting linear layout height as zero. But My linearlayout height is fill parent. and also i am able to see elements that i have added. Why I am not getting the actual height of my linearlayout.
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_linearlayout);
System.out.println("...Height..."+mainLayout.getMeasuredHeight());
Thanks
Deepak
Refer to this question
Can't get height of Widgets
When an Activity receives focus, it will be requested to draw its layout. The Android framework will handle the procedure for drawing, but the Activity must provide the root node of its layout hierarchy.
See this link view drawing and layout padding and margis
Till onCreate, OnResume, onStart view is not yet created.
So if you want to get height or width of view do following and get it
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
SearchResultActivity.this.sideIndexHeight = SearchResultActivity.this.sideIndex.getHeight();
ViewTreeObserver obs = sideIndex.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});
Please get new TreeObserver to remove, otherwise you will get an exception, TreeObserver is not alive
overriding onWindowFocusChanged method, you get the height or width of view. But if your activity is started as child activity then onWindowFocusChanged method will not get called. So in that case use the above one.