Reputation: 453
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:
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):
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
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:
Upvotes: 1