如何根据 Android 视图的父视图的尺寸来调整视图的大小

如何根据视图的父布局大小调整视图的大小。例如,我有一个 RelativeLayout,填充全屏幕,我想一个子视图,比如说 ImageView,占据整个高度,和1/2的宽度?

我试过在 onMeasureonLayoutonSizeChanged等上覆盖所有信息,但是我不能让它工作... ..。

159498 次浏览

When you define a layout and view on XML, you can specify the layout width and height of a view to either be wrap_content, or fill_parent. Taking up half of the area is a bit harder, but if you had something you wanted on the other half you could do something like the following.

<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:layout_height="fill_parent"
android:layout_width="0dp"
android:layout_weight="1" />
<ImageView android:layout_height="fill_parent"
android:layout_width="0dp"
android:layout_weight="1" />
</LinearLayout>

Giving two things the same weight means that they will stretch to take up the same proportion of the screen. For more info on layouts, see the dev docs.

Roman, if you want to do your layout in Java code (ViewGroup descendant), it is possible. The trick is that you have to implement both onMeasure and onLayout methods. The onMeasure gets called first and you need to "measure" the subview (effectively sizing it to the desired value) there. You need to size it again in the onLayout call. If you fail to do this sequence or fail to call setMeasuredDimension() at the end of your onMeasure code, you won't get results. Why is this designed in such complicated and fragile way is beyond me.

You could solve this by creating a custom View and override the onMeasure() method. If you always use "fill_parent" for the layout_width in your xml then the widthMeasureSpec parameter that is passed into the onMeasusre() method should contain the width of the parent.

public class MyCustomView extends TextView {


public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);


int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth / 2, parentHeight);
}
}

Your XML would look something like this:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<view
class="com.company.MyCustomView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

I don't know if anyone is still reading this thread or not, but Jeff's solution will only get you halfway there (kinda literally). What his onMeasure will do is display half the image in half the parent. The problem is that calling super.onMeasure prior to the setMeasuredDimension will measure all the children in the view based on the original size, then just cut the view in half when the setMeasuredDimension resizes it.

Instead, you need to call setMeasuredDimension (as required for an onMeasure override) and provide a new LayoutParams for your view, then call super.onMeasure. Remember, your LayoutParams are derived from your view's parent type, not your view's type.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

I believe the only time you'll have problems with the parent LayoutParams is if the parent is an AbsoluteLayout (which is deprecated but still sometimes useful).

If the other side is empty. I guess the simplest way would be to add an empty view (e.g. a linear layout) then set both views' widths to fill_parent and both their weights to 1.

This works in a LinearLayout...

I've found that it's best not to mess around with setting the measured dimensions yourself. There's actually a bit of negotiation that goes on between the parent and child views and you don't want to re-write all that code.

What you can do, though, is modify the measureSpecs, then call super with them. Your view will never know that it's getting a modified message from its parent and will take care of everything for you:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
int myWidth = (int) (parentHeight * 0.5);
super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}

I believe that Mayras XML-approach can come in neat. However it is possible to make it more accurate, with one view only by setting the weightSum. I would not call this a hack anymore but in my opinion the most straightforward approach:

<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:weightSum="1">
<ImageView android:layout_height="fill_parent"
android:layout_width="0dp"
android:layout_weight="0.5"/>
</LinearLayout>

Like this you can use any weight, 0.6 for instance (and centering) is the weight I like to use for buttons.

It's something like this:

<RelativeLayout 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"
tools:context="com.company.myapp.ActivityOrFragment"
android:id="@+id/activity_or_fragment">


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linearLayoutFirst"
android:weightSum="100">   <!-- Overall weights sum of children elements -->


<Spinner
android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
android:layout_weight="50"
android:layout_height="wrap_content"
android:id="@+id/spinner" />


</LinearLayout>




<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_below="@+id/linearLayoutFirst"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:id="@+id/linearLayoutSecond">


<EditText
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="75"
android:inputType="numberDecimal"
android:id="@+id/input" />


<TextView
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="25"
android:id="@+id/result"/>


</LinearLayout>
</RelativeLayout>

Try this

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

then you can use LinearLayout.LayoutParams for setting the chileView's parameters

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);

You can now use PercentRelativeLayout. Boom! Problem solved.

I've been struggling this question for a long time, I want to change my DrawerLayout's drawer width, which is the second child of its parent. Recently I figured it out, but doesn't know if there's a better idea.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
(getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}