Adjust the size of the view to the given space - android

I'm working on a custom view, which should have equal height and width.
I draw it in the following way:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = heightMeasureSpec;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
If I just add one view onto the screen, the width/height is ok. Though, if I add two of these views next to each other, the size of the views should be adjusted (each view: half width of the screen).
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<at.guger.widget.Light
android:id="#+id/lights_light1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp" />
<at.guger.widget.Light
android:id="#+id/lights_light2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp" />
</LinearLayout>
How can I do this?

Wouldn't it be easier to let the views take as much space as it can, but constrained them from the parent layoutmanager ? You can implement it with a recyclerview and your own LayoutManager overloading the measureChild method so that it would return the widthscreen divided by the number of elememnt in your adapter. Not a working example but something along the lines:
// setting up your recycler view
mRecyclerView = (RecyclerView) mView.findViewById(R.id.recycler);
// attaching your custom adapter
mRecyclerAdapter = new MyRecyclerAdapter(getActivity(), this);
mRecyclerView.setAdapter(statusRecyclerAdapter);
// setting the layoutmanager
mRecyclerLayoutManager = new MyRecyclerLayoutManager(
getActivity(),
LinearLayoutManager.HORIZONTAL, false);
mRecyclerView.setLayoutManager(mRecyclerLayoutManager);
Then you would create an innerclass MyRecyclerLayoutManager as follow:
private class MyRecyclerLayoutManager extends LinearLayoutManager {
MyRecyclerLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public void measureChild(View child, int widthUsed, int heightUsed) {
super.measureChild(child, widthUsed, heightUsed);
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int widthSpec = View.MeasureSpec.makeMeasureSpec((int) Math.round(displaymetrics.widthPixels / mRecyclerAdapter.size()), View.MeasureSpec.EXACTLY);
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
canScrollVertically());
child.measure(widthSpec, heightSpec);
}
public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
super.measureChildWithMargins(child, widthUsed, heightUsed);
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int widthSpec = View.MeasureSpec.makeMeasureSpec((int) Math.round(displaymetrics.widthPixels * 0.85), View.MeasureSpec.EXACTLY);
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
canScrollVertically());
child.measure(widthSpec, heightSpec);
}
}
Here the important bit is at widthSpec which is window width divided by your recycleradapter size.

Related

How to set the max height of a RecyclerView?

I have to implementing a RecylerView,it should wrap the content if its contain only item content height is less. If the item's increase like 250dp, it should be set to max heght(250dp) and that be able to scroll.How to achieve this.my parent layout is a Relative layout.
This is My Layout file
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom" />
</RelativeLayout>
Screen Shot if its only one item.This should be wrap_content.
you can programetically set height to RecylerView
if only one item... wrap content
ViewGroup.LayoutParams params=recycler_view.getLayoutParams();
params.height= RecyclerView.LayoutParams.WRAP_CONTENT;
recycler_view.setLayoutParams(params);
else
<dimen name="view_height">250dp</dimen>
float height= getResources().getDimension(R.dimen.view_height); //get height
ViewGroup.LayoutParams params_new=recycler_view.getLayoutParams();
params_new.height=height;
recycler_view.setLayoutParams(params_new);
1.) Create a class to handle setting maximum height to what is passed by the user:
public class OnViewGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private Context context;
private int maxHeight;
private View view;
public OnViewGlobalLayoutListener(View view, int maxHeight, Context context) {
this.context = context;
this.view = view;
this.maxHeight = dpToPx(maxHeight);
}
#Override
public void onGlobalLayout() {
if (view.getHeight() > maxHeight) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = maxHeight;
view.setLayoutParams(params);
}
}
public int pxToDp(int px) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int dp = Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return dp;
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
}
2.) Attach this to the view and pass the maximum height in DP:
messageBody.getViewTreeObserver()
.addOnGlobalLayoutListener(
new OnViewGlobalLayoutListener(messageBody, 256, context)
);
I have an answer here:
https://stackoverflow.com/a/29178364/1148784
Just create a new class extending RecyclerView and override it's onMeasure method.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (maxHeight > 0){
int hSize = MeasureSpec.getSize(heightMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
switch (hMode){
case MeasureSpec.AT_MOST:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.AT_MOST);
break;
case MeasureSpec.UNSPECIFIED:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
break;
case MeasureSpec.EXACTLY:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.EXACTLY);
break;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
maxHeight is in pixels.
You can use this:
maxHeight = (int) getContext().getResources().getDisplayMetrics().density * 250;
to convert 250 dp into pixels

Set Height of Inner RecyclerView wrap_content

I have a recyclerview inside another recyclerview. I want to make height of inner recyclerview "warp_content".
I came to know it is possible with new version of recyclerview which is mentioned below.
'com.android.support:recyclerview-v7:23.2.0'
But the problem is, by setting height = wrap_content I can see only first item.
Any Help will be great.
Because I am using a RecyclerView this does not work see:
https://code.google.com/p/android/issues/detail?id=74772
and
Nested Recycler view height doesn't wrap it's content
On both of these pages people suggest to extend LinearLayoutManager and to override onMeasure()
RecyclerView doesn't have the wrap_content by default. You have to use the RecyclerView with wrap_content Layout Manager such as, WrapLinearLayoutManager e.g CustomLinearLayoutManager .
CustomLinearLayoutManager.java
public class CustomLinearLayoutManager extends LinearLayoutManager {
private static final String TAG = CustomLinearLayoutManager.class.getSimpleName();
public CustomLinearLayoutManager(Context context) {
super(context);
}
public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
try {
View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Setting recyclerview item works for me.
Outer wrapper relative layout is added intentionally to address issue layout_height="wrap_content" does NOT
occupy the height of all the elements added to it via adapter. The result is cut out
items that is outside of device viewport when it loads initially.
Wrapping "RecyclerView" with "RelativeLayout" fixes the issue on Marshmallow devices.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/test_item"></android.support.v7.widget.RecyclerView>
</RelativeLayout>

Is it possible to implement a FlowLayout with RelativeLayout properties?

I would like to create a custom RelativeLayout that has two views in one row: one on the left side of the screen (android:layout_alignParentStart="true") and one on the right (android:layout_alignParentEnd="true"). The view on the right will grow toward the left view until it takes up all the space between the two views. Then it will move to a new line under the view on the left.
I have implemented a slightly modified version of Romain Guy's FlowLayout that extends RelativeLayout. However, this class seems to ignore the RelativeLayout's align properties and just sticks the views right next to each other. Is there a way to implement a such a layout that will anchor the views to the left and right?
FlowLayout class:
public class FlowLayout extends RelativeLayout {
private int mHorizontalSpacing;
private int mVerticalSpacing;
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mHorizontalSpacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_verticalSpacing, 0);
attributes.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingStart();
int currentHeight = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
measureChild(child, widthMeasureSpec, heightMeasureSpec);
if (currentWidth + child.getMeasuredWidth() > widthSize) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth);
currentWidth = getPaddingEnd();
}
int spacing = mHorizontalSpacing;
if (lp.spacing > -1) {
spacing = lp.spacing;
}
lp.x = currentWidth + spacing;
lp.y = currentHeight;
currentWidth += child.getMeasuredWidth();
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
}
width += getPaddingEnd();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height,
heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child
.getMeasuredHeight());
}
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout
.LayoutParams.WRAP_CONTENT);
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
#Override
public RelativeLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends RelativeLayout.LayoutParams {
public int spacing;
public int x;
public int y;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable
.FlowLayout_LayoutParams);
spacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_LayoutParams_layoutSpacing, -1);
attributes.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
}
}
It turns out that rather than calculating the right view's new position yourself, you can change its LayoutParams and have the OS handle positioning for you. I created a custom layout that extends RelativeLayout and overrides the onMeasure() method. This will adjust the LayoutParams accordingly.
More specifically:
Call the super method then find the widths of the two views and their parent in onMeasure(). Use these to figure out if the right view will overlap the left view. If so, change the right view's layout_alignParentEnd="true" property to be layout_alignParentStart="true" and give it the layout_below="#id/left_view" property. Do the opposite when there will be no overlap. Call the super method again to have the OS remeasure the views for you.
The layout class:
public class WrappingLayout extends RelativeLayout {
private TextView leftView;
private EditText rightView;
//Use this to prevent unnecessarily adjusting the LayoutParams
//when the right view is already in the correct position
private boolean isMultiline = false;
public WrappingLayout(Context context) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.wrapping_layout, this);
leftView = (TextView) findViewById(R.id.left_view);
rightView = (EditText) findViewById(R.id.right_view);
}
public WrappingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.wrapping_layout, this);
leftView = (TextView) findViewById(R.id.left_view);
rightView = (EditText) findViewById(R.id.right_view);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Call first to make sure the views' initial widths have been set
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int screenWidth = getMeasuredWidth();
int leftViewWidth = getPaddingStart() + leftView.getMeasuredWidth() + leftView.getPaddingEnd();
int rightViewWidth = getPaddingEnd() + rightView.getMeasuredWidth() + rightView.getPaddingStart();
LayoutParams rightViewParams = (LayoutParams) rightView.getLayoutParams();
if (!isMultiline && rightViewWidth + leftViewWidth > screenWidth) {
isMultiline = true;
rightViewParams.addRule(BELOW, R.id.left_view);
rightViewParams.removeRule(ALIGN_PARENT_END);
rightViewParams.addRule(ALIGN_PARENT_START);
//Call again here to adjust dimensions for new params
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else if (isMultiline && rightViewWidth + leftViewWidth < screenWidth) {
isMultiline = false;
rightViewParams.removeRule(BELOW);
rightViewParams.addRule(ALIGN_PARENT_END);
rightViewParams.removeRule(ALIGN_PARENT_START);
//Call again here to adjust dimensions for new params
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
The layout XML:
<?xml version="1.0" encoding="utf-8"?>
<merge
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">
<TextView
android:id="#id/left_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"/>
<EditText
android:id="#id/right_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:gravity="center"
android:text="#string/hello"/>
</merge>

extended View Group forces the child dimensions

I wrote the following ViewGroup
public class myViewGroup extends ViewGroup{
List<View> qResult;
List<Point> qLoc;
ImageView qImage;
public QueryViewLayout(Context context){
super(context);
}
public QueryViewLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QueryViewLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
qResult = new LinkedList<View>();
qLoc = new LinkedList<Point>();
qImage = null;
}
public void addMainView(ImageBorderView view){
qImage = view;
super.removeAllViews();
super.addView(view);
}
public void addResultView(View result, Point loc){
super.addView(result);
qResult.add(result);
qLoc.add(loc);
}
/**
* Any layout manager that doesn't scroll will want this.
*/
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
// Measurement will ultimately be computing these values.
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// Only main view affects the layouts measure
if (qImage != null) {
if (qImage.getVisibility() != GONE) {
// Measure the child.
qImage.measure(widthMeasureSpec, heightMeasureSpec);
maxWidth = qImage.getMeasuredWidth();
maxHeight = qImage.getMeasuredHeight();
childState = qImage.getMeasuredState();
}
}
for (View child:qResult){
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
child.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
}
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Report our final dimensions.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
int parentLeft = left + getPaddingLeft();
int parentRight = right - getPaddingRight();
final int parentTop = top + getPaddingTop();
final int parentBottom = bottom - getPaddingBottom();
if (qImage == null) return;
qImage.layout(parentLeft, parentTop, parentRight, parentBottom);
Iterator<Point> loc = qLoc.iterator();
for (View child:qResult) {
Point p = loc.next();
if (child.getVisibility() != GONE) {
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
Point locOnView = qImage.projectOnView(p);
width = (width < (int) Math.max(parentRight - (int) locOnView.x, locOnView.x - parentLeft)) ?
width : (parentLeft + parentRight)/2;
height = (height < (int) Math.max(parentBottom - (int) locOnView.y, locOnView.y - parentTop)) ?
height : (parentBottom + parentTop)/2;
int x = (width < (parentRight - (int) locOnView.x)) ? (int) locOnView.x : (parentRight - width);
int y = (height < parentBottom - (int) locOnView.y) ? (int) locOnView.y : (parentBottom - height);
// Place the child.
child.layout(x, y, x + width, y + height);
}
}
}
}
It is supposed to show some arbitrary view on top of an image, given a location for that view, when I use a GridView as the arbitrary view, even though I have defined a certain width for the GridView it is forced to have a width as large as the frame. In the measure phase I changed the mode to
MeasureSpec.AT_MOST
for both width and height of the overlay view, but this does not seem to work, can someone please help.
here is the xml where I, inflate the GridView from
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/result_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:columnWidth="#dimen/result_view_column_width"
android:numColumns="2"
android:verticalSpacing="2dp"
android:horizontalSpacing="2dp"
android:stretchMode="none"
android:gravity="center"
android:layout_margin = "2dp"
android:background="#drawable/solid_with_shadow" />
After a lot of trial and error, replacing
child.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
with
measureChild(child, MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
worked for me, I am not sure why, but a wild guess would be calling measure on a child does not read all the xml props, but measureChild(child, ...) does.

Set the Height of View to Make It Square By Knowing Only Its Width Weight

I have this scenario:
A horizontal LinearLayout that fills the container and weightSum=100, and two views inside with weight of 50 each.
Now how do I make these two views square (e.g. the height must be equal to their width). The number of LinearLayout rows is unknown, so basically, I can not wrap them in a vertical container with weights in this case.
Now how do I make these two views square (e.g. the height must be
equal to their width).
If you have just those two views in the LinearLayout you have two options:
Don't set your layout file directly as the content view, instead inflate it so you have a reference to the root from that file. Then post a Runnable on that root view, calculate the desired height and set it back to each of the two children of the LinearLayout that wraps them:
final View v = getLayoutInflater().inflate(
R.layout.views_specialheight, null);
v.post(new Runnable() {
#Override
public void run() {
setupHeight((ViewGroup) v);
}
});
setContentView(v);
where setupHeight() is the method:
private void setupHeight(ViewGroup vg) {
int count = vg.getChildCount();
for (int i = 0; i < count; i++) {
final View v = vg.getChildAt(i);
if (v instanceof LinearLayout) {
int width = v.getWidth();
LinearLayout.LayoutParams lp;
View one = ((LinearLayout) v).getChildAt(0);
lp = (LinearLayout.LayoutParams) one.getLayoutParams();
lp.height = width / 2;
one.setLayoutParams(lp);
View two = ((LinearLayout) v).getChildAt(1);
lp = (LinearLayout.LayoutParams) two.getLayoutParams();
lp.height = width / 2;
two.setLayoutParams(lp);
}
}
}
This method will work pretty well if you just have a wrapper ViewGroup subclass that wraps those LinearLayout rows. It can(and should) be improved, but I'm sure you get the point.
The second option is to use a custom ViewGroup(this may be used depending on what you were planing to do with the LinearLayout) instead of the LinearLayout like this:
<com.luksprog.oat.views.SpecialHeightViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0077cc"
android:text="Button" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#99cc00"
android:text="Button" />
</com.luksprog.oat.views.SpecialHeightViewGroup>
The SpecialHeightViewGroup is a class like this:
class SpecialHeightViewGroup extends ViewGroup {
public SpecialHeightViewGroup(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public SpecialHeightViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SpecialHeightViewGroup(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY
|| widthMode == MeasureSpec.AT_MOST) {
measureChildren(MeasureSpec.makeMeasureSpec(widthSize / 2,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
widthSize / 2, MeasureSpec.EXACTLY));
measureChildren(MeasureSpec.makeMeasureSpec(widthSize / 2,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
widthSize / 2, MeasureSpec.EXACTLY));
setMeasuredDimension(widthSize, widthSize / 2);
} else {
widthSize = 800; // we don't have restrictions, make it as big as we want
measureChildren(MeasureSpec.makeMeasureSpec(widthSize / 2,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
widthSize / 2, MeasureSpec.EXACTLY));
measureChildren(MeasureSpec.makeMeasureSpec(widthSize / 2,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
widthSize / 2, MeasureSpec.EXACTLY));
setMeasuredDimension(widthSize, 400);
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View one = getChildAt(0);
one.layout(0, 0, getWidth() / 2, getHeight());
View two = getChildAt(1);
two.layout(getWidth() / 2, 0, getWidth(), getHeight());
}
}
The custom class isn't properly tested so it may have bugs(it's just an example).
Offtopic: Are you trying by any chance to create an UI like on the new Windows phones(with the tiles thing)?

Categories

Resources