wrap_content height, but limited to half of parent - android

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.

Related

Place three items of recyleview in the screen programmatically

I want to set width and height of each item programmatically (textview + recyleview) so that I can place three ones no matter which device renders it (when devices differ I got different width and height because of density metrics). My code for doing this issue is as following:
public class ItemRowHolder extends RecyclerView.ViewHolder {
protected TextView playlistTitle;
protected Button playlistMoreButton;
protected RecyclerView playlist_recycler_view_list;
public ItemRowHolder(View view) {
super(view);
this.playlistTitle = (TextView) view.findViewById(R.id.playlist_title);
this.playlist_recycler_view_list = (RecyclerView) view.findViewById(R.id.playlist_recycler_view_list);
this.playlistTitle.measure(0,0);
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
int screenHeight = (int )displayMetrics.heightPixels;
int titleHeight = this.playlistTitle.getMeasuredHeight();
int rcvHeight = this.playlist_recycler_view_list.getLayoutParams().height;
int layoutHeight = 0;
if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
p.setMargins(5, 20, 5, 20);
view.requestLayout();
}
layoutHeight = (int)(screenHeight - displayMetrics.density* titleHeight-displayMetrics.density*120)/3;
Log.d("MOMOPix","ydpi: "+ displayMetrics.density);
Log.d("MOMOPix","Screen height: "+ screenHeight);
Log.d("MOMOPix","RCV height: "+ rcvHeight);
Log.d("MOMOPix","Title height: "+titleHeight);
Log.d("MOMOPix","Layout height: "+layoutHeight);
this.playlist_recycler_view_list.getLayoutParams().height= layoutHeight;
}
}
layout is as following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/playlist_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:text="Sample title"
android:textColor="#android:color/white"
android:textSize="18dp" />
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/playlist_recycler_view_list"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center_vertical"
android:orientation="horizontal" />
</LinearLayout>
for some devices I get desired results and I want for all devices
Thanks in advances.
You're doing it in very weird and error prone way.
If you want to alter how items are laid out, you should override RecyclerVIew.LayoutManager (which is specifically made to lay out the views as its name suggests) instead of performing weird measurement hacks in your ViewHolder.
To have RecyclerView fit three items vertically (I assume it's LinearLayoutManager, your code sample does not mention it) we only have to override one method in LayoutManager:
recyclerView.setLayoutManager(new LinearLayoutManager(context){
#Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
// force height of viewHolder to be a third of RecyclerView
// this will override layout_height from xml
lp.height = getHeight() /3;
return true;
}
});

get height size ( in pixel ) of a ViewGroup ( RecyclerView child of LinearLayout ) in runtime

i have a main LinearLayout and two RecyclerView as child and need to get height size of second RecyclerView
i tried by rv_show_adv.getHeight() and rv_show_adv.getLayoutParams().height but get me 0 but when i test the app in a real device i could to see i have height and width on both but why in java code i always got 0 pixel !
/layout/activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainlinear"
android:orientation="vertical"
tools:context=".main.MainActivity"
android:background="#f3cdcd"
android:weightSum="1"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="55dp"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_selected_adv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_show_adv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#b9f1ce"
android:layout_weight="1">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
really confused because when i have a View (like as TextView) i could to get height by View.getHeight() but why not worked when i had a ViewGroup
i also try to get height of second child of main LinearLayout by LinearLayout.getChildAt(1).getHeight() but again get me 0
also try to define RecyclerView variable in onCreate() and wants to get height in onResume() lifecycle but not different!
i think i miss somethings in my code what do you suggestion ?
Because of View.getHeight() on onCreate is 0, view need onMeasure() when onCreateView so onMeasure() maybe finished after onCreated, you should do this:
int mWidth , mHeight ;
rv_show_adv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
rv_show_adv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
rv_show_adv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
mWidth = rv_show_adv.getWidth();
mHeight = rv_show_adv.getHeight();
}
});

Android: stretch layout above another

I create layout (ContentLayout) with wrap_content.
And I want to show loading layout (LoadingLayout) same size as ContentLayout above it.
Something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout.
android:id="#+id/ContentLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
...
</LinearLayout>
<RelativeLayout
android:id="#+id/LoadingLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
...
</RelativeLayout>
</RelativeLayout>
But when I want set LoadingLayout size in onMeasure, it will be set to right size only in second chance.
Works only something like this:
Activity
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) loadingLayout.getLayoutParams();
params.height = getMeasuredHeight();
loadingLayout.setLayoutParams(params);
super.onMeasure(widthMeasureSpec, heightMeasureSpec); // android realignment hack
}
I think this is a hack. Do you have a better solution?
If I understood correctly, you need LoadingLayout to keep width of ContentLayout
<RelativeLayout
android:id="#+id/LoadingLayout"
android:layout_alignLeft="#id/ContentLayout"
android:layout_alignRight="#id/ContentLayout"
....>
....
</RelativeLayout>
Edit: Below is one way for the height
You can add a one time listener when global refresh happens. Then all sizes will be available.
// in your activity
#Override
protected void onCreate(Bundle state) {
final View loading = findViewById(R.id.LoadingLayout);
final View content = findViewById(R.id.ContentLayout);
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
loading.getLayoutParams().height = content.getHeight();
loading.onRequestLayout();
loading.invalidate();
}
}
}

Android fullscreen layout with additional layouts underneath, which are scrollable

I have a layout displaying some images and text, with buttons at the bottom.
When a button is pressed, I need to display some new information below the buttons.
So the initial content needs to remain at the same height (height of the device screen), and the new content needs to be added beneath it, allowing the user to scroll down.
When a button is pressed it will ideally need to show the new content like a page anchor, but the part I'm having difficulty is getting the initial content to be fullscreened, and maintain that size when new content is added whilst also making the whole thing scroll-able.
I have been playing with different layouts, different height parameters, android:fillViewport="true" or not etc.
I can provide some XML / further explanation if necessary. But I'm not sure whether what I am aiming to achieve is possible or not. At least I'd like to get a scrollable overall view, with the top layout as fullscreen and some layouts underneath which the user can scroll to.
image:
Try this:
Make ScrollView container and add your layout #1 into it
Set height of layout #1 into the code according per screen height
After button click add layout #2 into ScrollView
UPDATED:
Ok, I can suggest you only this solution (it worked for me in emulator).
XML:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:onClick="btnClick"/>
</RelativeLayout>
</LinearLayout>
</ScrollView>
Code:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void btnClick(View v){
LinearLayout container = (LinearLayout)findViewById(R.id.container);
ScrollView scroll = (ScrollView)findViewById(R.id.scroll);
RelativeLayout layout1 = (RelativeLayout)findViewById(R.id.layout1);
RelativeLayout layout2 = new RelativeLayout(this);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, 300);
layout2.setBackgroundColor(Color.YELLOW);
layout1.getLayoutParams().height = scroll.getHeight();
scroll.setFillViewport(false);
container.addView(layout2, params);
}
}
I would create a custom ScrollView which sets the height of its first child to be its own height. This will simulate a fullscreen view while still being able to add content below it.
To handle screen size changes properly, the best is to override onSizeChanged() :
public class CustomScrollView extends ScrollView {
// ...
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
View view = getChildAt(0);
if (view != null)
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, h)));
}
}

Dynamically setting relative dimension of a linearlayout

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

Categories

Resources