Dynamically setting relative dimension of a linearlayout - android

I want to resize my LinearLayout (or a view) to a dimension which is relative to the parent or itself. For example, I want the width to be 1/3 of the parent's width. Or, the height should be same as its own width. I don't want to use any constants , so that it works for all devices.
adding code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<LinearLayout android:id="#+id/ll_board"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
...
</LinearLayout>
code:
public class GMActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
LinearLayout board_layout = (LinearLayout)findViewById(R.id.ll_board);
// I wanted to resize board_layout here ..
// getParent().getWidth() returns 0
Log.d("gm", "layout: " + ((LinearLayout) board_layout.getParent()).getWidth());
// ..
}
}
getWidth() is giving 0. Is it too early to call this? If yes, what is the correct place to call this?
Basically my intention is to make the width of the layout a fraction of the screen size width, and, height same as its own width.

Considering layout your LinearLayout and that its parent it's another LinearLayout:
Get the parent's width:
int parentWidth = ((LinearLayout) layout.getParent()).getWidth();
Get the view's width:
int viewWidth = ((LinearLayout) layout).getWidth();
set the
view.setHeight(viewWidth );
view.setWidth(parentWidth / 3);

I found height=width solution (square shaped layout) in LinearLayout in Square Form

Related

Programmatically setting height of LinearLayout inside ScrollView not working

I have a ScrollView whose unique child is a LinearLayout:
<ScrollView
android:id="#+id/d_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/d_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparency"
android:orientation="vertical"/>
</ScrollView>
I later dynamically add children to the LinearLayout by using a LayoutInflator (let's say 5 children in this example), and they all have a weight of 1 (so equal heights). I want to only have 4.5 children visible at a time. To do this, I have used a post method to do change the layout_height property of the LinearLayout once the height of the ScrollView is known:
if (n > visibleItems) { // here n = 5 and visibleItems = 4.5
dScroll.setFillViewport(false);
dScroll.post(() -> {
int height = dScroll.getHeight();
ViewGroup.LayoutParams lp = dLayout.getLayoutParams();
lp.height = (int) Math.round(n * height / visibleItems);
dLayout.setLayoutParams(lp);
});
}
However, when I do this, the LinearLayout and its children behave as if their height was set to wrap_content (which is not the case). When using the Layout Inspector in Android Studio, I find that the layout_height of the LinearLayout is set to 1071 (the correct computed value), but the getHeight() method returns 578.
I have tried several combinations of requestLayout(), invalidate() and forceLayout() but it didn't change anything.
Keeping the fillViewport property of the ScrollView to true makes the LinearLayout take the height of the ScrollView, so in this case 964px instead of the 1071 that I want.
Edit: the problem really comes from LinearLayout. If I add a RelativeLayout between the ScrollView and LinearLayout like so:
<ScrollView
android:id="#+id/d_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:id="#+id/d_rel"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/d_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparency"
android:orientation="vertical"/>
</RelativeLayout>
</ScrollView>
and apply the new height to the RelativeLayout instead:
if (n > visibleItems) { // here n = 5 and visibleItems = 4.5
dScroll.setFillViewport(false);
dScroll.post(() -> {
int height = dScroll.getHeight();
ViewGroup.LayoutParams lp = dRel.getLayoutParams();
lp.height = (int) Math.round(n * height / visibleItems);
dRel.setLayoutParams(lp);
});
}
then the RelativeLayout takes the correct height (1071px), but the LinearLayout within it (that is in match_parent height) is still only 578px high.

ImageView expanding container despite adjustViewBounds

I want to wrap an ImageView inside a LinearLayout so that I can center a group of views. However, the original image needs to be scaled down to fit in the ImageView, and the original size expands the LinearLayout, despite my use of adjustViewBounds="true" and an enclosing FrameLayout as suggested by previous questions on SO.
The desired layout should look like this,
but the observed layout looks like this,
as produced by the XML below:
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="project.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
app:layout_heightPercent="32%"
android:orientation="horizontal"
android:background="#b44343"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample Text"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#5555ae">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#2c8c4c"
android:src="#drawable/spades_icon"
android:adjustViewBounds="true"
android:scaleType="centerInside"/>
</FrameLayout>
</LinearLayout>
</android.support.percent.PercentRelativeLayout>
I can't use the other suggestion of setting android:maxHeight="100dp" because I need the height to be relative to that of the screen.
I see that you have added android:adjustViewBounds="true".
You can combine that with android:maxWidth="60dp"
So your imageView should look like this.
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#2c8c4c"
android:src="#drawable/spades_icon"
android:adjustViewBounds="true"
android:maxWidth="60dp"
android:scaleType="centerInside"/>
You can change the max width to any number you want.
Things you can do:
1) Set a specific width / height to the FrameLayout enclosing the ImageView and set android:scaleType to centerInside, fitCenter, or fitXY for the ImageViwe.
2) Programatically, in your activity, after onCreate, in onResume for example, you can get the LayoutParams and change the width and height of the ImageView doing you own scaleing. I take this aproach when I scale against the screen widht or height at run time.
EDIT 1
Example of second alternative:
public class TestActivity extends AppCompatActivity {
ImageView imgView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testactivity_layout);
imgView = (ImageView) findViewById(R.id.imgview);
}
#Override
protected void onResume() {
super.onResume();
ViewGroup.LayoutParams params = imgView.getLayoutParams();
params.width = 100;
}
}
Notes:
The width is expressed in pixel.
To get the display metrics:
How to get screen display metrics in application class
To establish a relative width for the ImageView, get the width of the display and calculate the desireed % as the width of the image.
Based on this answer to another question, a solution that removes the whitespace in the LinearLayout while preserving the height and aspect ratio of the image is:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
LinearLayout layout = (LinearLayout) findViewById(R.id.mLinearLayout);
ImageView imageView = (ImageView) findViewById(R.id.mImageView);
TextView textView = (TextView) findViewById(R.id.mTextView);
layout.getLayoutParams().width = textView.getWidth()+imageView.getWidth();
layout.requestLayout();
}
EDIT:
Based on #Juan's answer and these instructions, the following code also achieves the desired result:
#Override
protected void onResume() {
DisplayMetrics displayMetrics = new DisplayMetrics();
this.getWindowManager()
.getDefaultDisplay()
.getMetrics(displayMetrics);
ViewGroup.LayoutParams params = imgView.getLayoutParams();
params.height = (int)Math.floor(displayMetrics.heightPixels * 0.32);
}

wrap_content height, but limited to half of parent

What I want is to have a layout with 2 views arranged vertically. Let's call the top view A and the bottom one B. I want the amount of height given to B to be its normal height (i.e. wrap content) except that I don't want it to be given more than half of the available space. A gets what is left.
Another way to word it is that A should always get at least 50% of the available height and B should get at most 50%.
I can't seem to find an easy way to achieve that. I can set both layout heights to 0 and give them equal weights which makes them both 50% always, but if B is smaller than 50% it should be given only what it needs.
The only way I can see to do it is use a custom class for A or B and override onMeasure to constrain the height to 50% of the parent, but it seems there should be an easier way.
Ok, I got it now. If I understood correctly you want to have it like this:
if A > B -> do nothing
if B > A & B > parent layout -> 50% to both of them
if B > A & B < parent layout -> A = parent layout - B
I had to do it all in onWindowFocusChanged because otherwise in onCreate the height of the Views would return 0. I did it with 2 LinearLayouts as child layouts, but you can take what ever you want.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/parent_lay"
android:orientation="vertical"
android:layout_height="match_parent" >
//Layout A:
<LinearLayout
android:id="#+id/lay_1"
android:layout_width="match_parent"
android:background="#android:color/background_dark"
android:layout_height="10dp" >
</LinearLayout>
//Layout B:
<LinearLayout
android:id="#+id/lay_2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#123456" >
</LinearLayout>
</LinearLayout>
MainActivity:
public class MainActivity extends Activity {
LinearLayout parent_lay;
LinearLayout lay_1;
LinearLayout lay_2;
int parent_height;
int lay_1_height;
int lay_2_heigth;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
parent_lay = (LinearLayout) findViewById(R.id.parent_lay);
lay_1 = (LinearLayout) findViewById(R.id.lay_1);
lay_2 = (LinearLayout) findViewById(R.id.lay_2);
lay_1_height = lay_1.getHeight();
lay_2_heigth = lay_2.getHeight();
parent_height = parent_lay.getHeight();
if (lay_2.getHeight() > lay_1.getHeight()
&& lay_2.getHeight() > (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
lay_2.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
} else if (lay_2.getHeight() < (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, (parent_height - lay_2_heigth)));
}
}
}
Example:
If A is 60dp and B is 40dp:
If A is 60dp and B is 400dp:
You must write your own component to achieve this.
For example, if you use LinearLayout here, you can extends a LinearLayout with overdid onMeasure method. You can implement onMeasure like this:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
setMeasuredDimension(width, height / 2);
}
This code is not elegant enough. If you really want to do it well, copy the original onMeasure method from Android source code (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/LinearLayout.java#LinearLayout.onMeasure%28int%2Cint%29), and in measureVertical(int widthMeasureSpec, int heightMeasureSpec), set mTotalLength = mTotalLength / 2.
For detailed information of onMeasure, visit http://developer.android.com/guide/topics/ui/custom-components.html and http://developer.android.com/reference/android/view/View.html#onMeasure(int, int).
Now the desired effect can be achieved with the ConstraintLayout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/containerFrameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<FrameLayout
android:id="#+id/containerFrameLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guideline"
app:layout_constraintVertical_bias="1">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Create a linear layout with two inner frames, each with .5 weight. Inside those frames, place your views, setting them to wrap_content or match_parent as appropriate.

How to measure the width of view in android

I am having two text views in Linear layout having horizontal orientation. Width of text views are wrap_content. If the sum of width of two text views is less than the screen width it is fine. If the sum of width exceeds the screen width then i need to change the orientation from horizontal to vertical.
I tried using getWidth() in onCreate of the activity but it returned 0. I can try creating a custom view with onSizeChanged() function but i am using two text views so i am not sure that when onSizeChanged() in one text view will not make sure that the other textview is fully drawn to get the width. Any suggestions is really helpful for me.
<LinearLayout android:id="#+id/status_container"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal">
<TextView android:id="#+id/view1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<TextView android:id="#+id/view2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
</LinearLayout>
// In OnCreate() function
TextView view1 = (TextView) findViewById(R.id.view1);
TextView view2 = (TextView) findViewById(R.id.view2);
view1.setText("Good Morning,");
view2.setText("I am Ron");
int view1_width = view1.getWidth();
int view2_width = view2.getWidth();
if ((view1_width + view2_width) > screen_width) {
// Change the Linear Layout orientation to Vertical
}
Here view1_width and view2_width are returning 0. I want to check if the view1_width + view2_width is greater than the screen width then i need to change the orientation into vertical, or else Horizontal orientation is fine.
-Ron
Add this to your activity's onCreate
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//You should be able to get the width and height over here.
layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});

How do I calculate the required height of a GridView that is inside of a ScrollView on Android?

I have a GridView inside of a LinearLayout inside of a ScrollView that pages in data from the server. Beneath the GridView is a button to load more data. My GridView will have an ultimate height that is larger than the screen. If I set the height of my GridView to either wrap_content or parent_fill, it sizes itself to the exact available on-screen height and does not scroll at all, cropping out the extra rows. If I explicitly set the layout_height to something large, like 1000dip, scrolling behaves properly, however I cannot predict the final height of my scroll view apriori.
How do I programmatically determine the necessary height of a GridView to get the desired behaviour?
Here is my layout below. As you can see I set the height to 1000dip, but that is bogus, I need that value to get set automatically/programmatically:
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:layout_weight="1"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid"
android:layout_width="fill_parent"
android:layout_height="1000dip"
android:columnWidth="70dp"
android:numColumns="auto_fit"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:background="#000000"
android:layout_weight="1"
/>
<Button
android:id="#+id/load_more"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Load More Foo"
/>
</LinearLayout>
</ScrollView>
Here is one way to do this, if someone needs it. A bit of a hack but does the trick. You have to set GridView initially big enough for all the views (e.g. 10000dip)
final GridView imageContainer = // your GridView
imageContainer.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
imageContainer.getViewTreeObserver().removeGlobalOnLayoutListener( this );
View lastChild = imageContainer.getChildAt( imageContainer.getChildCount() - 1 );
imageContainer.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.FILL_PARENT, lastChild.getBottom() ) );
}
});
I know it's an old case, but I had a similar problem where my ScrollView contained multiple LinearLayouts, which in their turn contained a header and a GridView.
Basically I made categorised sections with headers containing images belonging to that category.
The GridView had to have a flexible height.
I found a lot of answers about overriding onMeasure(), but it worked only on some devices, not all. The height would eventually be 1, or 3 or just 0, displaying only a few pixels of the image.
StretchingGridView class
I overrode the drawableStateChanged() method with this code, inspired by #Karitsa's solution:
#Override
public void drawableStateChanged() {
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
getViewTreeObserver().removeOnGlobalLayoutListener( this );
View lastChild = getChildAt( getChildCount() - 1 );
if (lastChild != null) {
int height = Math.max(lastChild.getBottom(), getColumnWidth());
float child = getAdapter().getCount();
float col = getNumColumns();
int rows = (int) Math.ceil(child / col);
height = rows * getColumnWidth() + (getHorizontalSpacing() * rows-1);
setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, height ) );
}
}
});
}
Note: My GridView uses square images, so I base the height on their width. I don't think it works well with flexible grid item heights.
Apparently GridViews inside ScrollViews are not kosher in Android-land. Switching to ListView with custom-made rows. That seems to behave better.

Categories

Resources