xTuMiOx
xTuMiOx

Reputation: 453

RelativeLayout not respecting parent bounds

I'm having problems while rendering a RelativeLayout at runtime inside a Canvas. I know this is Xamarin.Android, but it's actually pretty identical to "original" Android, so I'll accept every solution that works.

I have this xml that renders correctly inside my view (scroll down for the "score_progress_bar" xml):

<LinearLayout android:layout_width="wrap_content"
              android:layout_height="wrap_content">
            <include
                layout="@layout/score_progress_bar"
                android:layout_width="50dp"
                android:layout_height="50dp" />
</LinearLayout>

And here is the correct result:

Expected result

If I try to load the same thing via a Canvas, I'm not getting the same result. Here's the code where I try to load the same control at runtime:

var id = Application.Context.Resources.GetIdentifier("score_progress_bar", "layout", Application.Context.PackageName);

var ll = new LinearLayout(Application.Context);
ll.SetBackgroundColor(AndroidColor.Red);
ll.Measure(50, 50);
ll.Layout(0, 0, 50, 50);

var li = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService);
var v = (RelativeLayout)li.Inflate(id, null, true);
v.LayoutParameters = new RelativeLayout.LayoutParams(50, 50);
v.Measure(50, 50);
v.Layout(0, 0, 50, 50);

ll.AddView(v);
ll.Draw(Native);

Just to be clear, "Native" is my Canvas object and I'm setting a red background color to the LinearLayout so I can see where this is loaded in my view.

The result I'm getting is weird, the LinearLayout is being rendered correctly but the "score_progress_bar" object is not, it seems like it has margin somewhere or that the bounds are not set (notice that the TextView with the number "25" is not in the view center):

Actual result

If you need the "score_progress_xml", here it is (it's just a RelativeLayout with some views):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/score_white_background"
        android:layout_marginLeft="2dp"
        android:layout_marginRight="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginBottom="2dp" />
    <ProgressBar
        android:id="@+id/progressRing"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:indeterminate="false"
        android:progressDrawable="@drawable/score_background_ring"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:progress="25" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="25"
        android:textSize="17dp"
        android:textColor="#000000"
        android:id="@+id/progressText"
        android:gravity="center" />
</RelativeLayout>

Do you have any idea on what's going on here?

Edit: found a solution thanks to SushiHangover! Just to complete this question in case someone else may need it, here's how I solved in this specific case:

var id = Application.Context.Resources.GetIdentifier("score_progress_bar", "layout", Application.Context.PackageName);
var li = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService);
var progressView = (RelativeLayout)li.Inflate(id, null, true);

var measuredWidth = View.MeasureSpec.MakeMeasureSpec(50, MeasureSpecMode.Exactly);
var measuredHeight = View.MeasureSpec.MakeMeasureSpec(50, MeasureSpecMode.Exactly);
progressView.Measure(measuredWidth, measuredHeight);
progressView.Layout(0, 0, progressView.MeasuredWidth, progressView.MeasuredWidth);

progressView.Draw(Native);

Upvotes: 1

Views: 189

Answers (1)

SushiHangover
SushiHangover

Reputation: 74094

You need to use the result from View.MeasureSpec.MakeMeasureSpec as the origin of your X/Y measurements for the call to View.Measure and then use View.MeasuredWidth, which should be 50 in your case, in your View.Layout call. Now the View and all of its children are laid out correctly and you can Draw to your Canvas.

Here is a stripped down a version of the routine that I use to generate a series of countdown bitmaps (they get fed as Textures to OpenGL).

var xy = 200;
var inflater = (LayoutInflater)ApplicationContext.GetSystemService(LayoutInflaterService);
var progressIncludeID = ApplicationContext.Resources.GetIdentifier("progress_include", "layout", ApplicationContext.PackageName);
var pseudoProgressView = (RelativeLayout)inflater.Inflate(progressIncludeID, null, false);

int measuredWidth = View.MeasureSpec.MakeMeasureSpec(xy, MeasureSpecMode.Exactly);
int measuredHeight = View.MeasureSpec.MakeMeasureSpec(xy, MeasureSpecMode.Exactly);
pseudoProgressView.Measure(measuredWidth, measuredHeight);
pseudoProgressView.Layout(0, 0, pseudoProgressView.MeasuredWidth, pseudoProgressView.MeasuredWidth);

var imageBitmap = Bitmap.CreateBitmap(xy, xy, Bitmap.Config.Argb8888);
var canvas = new Canvas(imageBitmap);
pseudoProgressView.Draw(canvas);

The top ProgressBar in the image below is rendered within a include -based RelativeLayout on-screen and the one on the bottom is a ImageView showing the BitMap of a completely off-screen rendering of the same RelativeLayout inflated axml:

enter image description here

Upvotes: 1

Related Questions