I have a LinearLayout (vertical) with two child views in it. The 1st one is a ScrollView and the 2nd one is another layout with Visibility.GONE, its size is not fixed (determined by its children).
<LinearLayout vertical>
<ScrollView> ... </ScrollView>
<AnotherLayout visibility=GONE height=wrap_content> ... </AnotherLayout>
</LinearLayout>
At some point of time I want to show AnotherLayout. But, once it pops up, I also want to adjust the scrolling of my ScollView one. For this, I need to know the size of this AnotherLayout.
I'm doing something like that:
int oldHeight = scrollArea.getHeight();
linearLayout.setVisibility(VISIBLE);
int newHeight = scrollArea.getHeight();
But oldHeight and newHeight are still the same.
How can I calculate the new height?
The two dimensions are the same because visibility change took time and the line of code was run before that so it returns the same.
You can use a visibility listener to calculate the dimension after visibility change , You may use that
linearLayout.setTag(linearLayout.getVisibility());
linearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int newVis = linearLayout.getVisibility();
if((int)linearLayout.getTag() != newVis)
{
linearLayout.setTag(linearLayout.getVisibility());
//visibility has changed
int newHeight = scrollArea.getHeight();
}
}
});
Related
I make some custom view, and now im stuck with one specific issue.
Now ill try to explain: in my case the view hierarchy is
<view extends linear layout>
<child-view extends relative layout>
<some content>
<relative-layout>
<progressbar> (align text view right, match parent)
<textview> (align parent right, wrap content) <- there's i have to make some changes
<relative-layout>
<child-view>
<...>
<view>
the child items are added according to the data coming from the server.
I want to make all textviews with the same width - width of textview which content length is max. How can I implement this? I tried to calculate width in onMeasure callback, and then set width to another views, but getMeasuredWidth() and getWidth() returns 0 every time. I Heard something about ViewTreeObserver, but i can not understand how to use it right. Thanks in advance!
Try this,
int maxWidth = 0;
....
tv1.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = layout.getMeasuredWidth();
if(width > maxWidth){
maxWidth = width;
// update all textView width with max width.
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
this.layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
this.layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
same code for tv2,tv3 and tv4. it will work.
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);
I have the a LinearLayout with width set in xml as fill_parent , now i need its width at runtime programatically. So i did this:
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
LayoutParams layParamsGet= layoutGet.getLayoutParams();
int width=layParamsGet.width;
But width value on debugging was found to be -1, anybody with idea why can't i get the exact width at runtime for LinearLayout with fill_parent set in layout xml.
I got a simple approach working.
myLayout = (RelativeLayout) findViewById(R.id.my_layout);
myLayout.post(new Runnable()
{
#Override
public void run()
{
Log.i("TEST", "Layout width : "+ myLayout.getWidth());
}
});
Jens Vossnack's approach mentioned below works fine. However, I found that the onGlobalLayout() method of GlobalLayoutListener is called repeatedly, which may not be appropriate in certain cases.
You can try to listen to the globalLayout event, and get the width in there. You probably get the -1 because you are trying to get the width before the views are layed-out.
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Do it here
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
LayoutParams layParamsGet= layoutGet.getLayoutParams();
int width=layParamsGet.width;
removeOnGlobalLayoutListener(layoutGet, this); // Assuming layoutGet is the View which you got the ViewTreeObserver from
}
});
#SuppressLint("NewApi")
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
if (Build.VERSION.SDK_INT < 16) v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
else v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
(vto is the view you want to get the width of)
If you look at the value for FILL_PARENT (you should be using MATCH_PARENT since FILL_PARENT is now deprecated) you will notice the value for it is -1. LayoutParams are simply attributes for how your view should look. Once the view is inflated and looks the way the params specify, the view does not go back and change those Params to reflect the actual values of the view (width/height/etc).
If you wanted to get the actual width of your view you would have to call getWidth() on your view once the layout has been inflated and displayed. Calling getWidth() before your layout has been displayed will result in 0.
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
int width = layoutGet.getWidth();
First of all, you should use match_parent instead of fill_parent (deprecated).
When you do int width=layParamsGet.width;, you take the right result which is -1 corresponding at fill_parent.
If you wan't to get the real width, you need to wait onMesure() call and use
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
int width = layoutGet. getMeasuredHeight();
layoutGet.getWidth() should work out
I would like to set the Height of my ScrollView per Code dynamically cause my ScrollView is actually higher then it shall be (empty space at bottom).
My thoughts were, that I could get the Heights of all Controls within the the ScrollView, sum-up them and then I could set that Height to my ScrollView.
What I tried is following code:
protected override void OnStart()
{
base.OnStart();
SetScrollViewSize();
}
private void SetScrollViewSize()
{
var root = FindViewById<ScrollView>(Resource.Id.root);
if (root != null)
{
var controls = GetSelfAndChildrenRecursive(root); //Gives me all Controls in the root (The ScrollView)
int heightOfAllControlsTogether = 0;
foreach (ViewGroup control in controls)
{
heightOfAllControlsTogether += control.Height;
}
ViewGroup.LayoutParams parameters = new ViewGroup.LayoutParams(root.Width, heightOfAllControlsTogether);
root.LayoutParameters = parameters;
}
}
The Heights and MeasuredHeights are always 0 (zero) - (I know it needs to be rendered first, but what would be the right place then?) and I'm not even sure if my approach would work.
Any Help would be appreciated!
Well I found out why.
The Background-Graphic was too high. That was creating the empty space at the bottom.
THis is an example to set scrollview height based on total height of Children:
private void setScrollViewHeightBasedOnChildren() {
int size = layoutRoot.getChildCount();
LinearLayout item = (LinearLayout) layoutRoot.getChildAt(0);
item.measure(0, 0);
int height = item.getMeasuredHeight();
//Reset size of ScrollView
scroll.setMinimumHeight(height / size);
scroll.invalidate();
}
See details: Set the height of ScrollView equal total height of children
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() {
...
}
}