/ XAMARIN

Xamarin and Android – Specific Solutions to Specific Problems #3

Welcome to another installment in my series exploring the specific problems that nearly beat me this week. I write these on Sunday night after a huge week of balancing my work and fatherly responsibilities so I hope they make sense…

** 

1. I have put a relative layout inside a scroll view and my layout_alignParentBottom properties don’t work any more. How can I align elements to the bottom of a layout inside a scroll view?

As soon as put things in a scroll view I saw this exact behaivour. After some searching on the interwebs I found the following quote by Romain Guy on Stack Overflow:

Using fill_parent as the height of a child of a ScrollView is meaningless. You are telling the RelativeLayout to be always as tall as its parent. In this case, the ScrollView becomes useless! The height of the RelativeLayout should be set to wrap_content, in which case, depending on what the RelativeLayout contains, alignParentBottom may not work as you expect.

The trick here is to use the android:fillViewport property on the scroll view. As soon as you set this to true, the scroll view will fill the screen and the layout_alignParentBottom will start to function as expected:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@color/white" android:fillViewport="true"> ... </ScrollView>

</div>

 

2. When I use async/await in my code the execution will fall through to other methods that require data that hasn’t been intialised yet. Is there anyway to signal in an async/await scenario?

I first witnessed this kind of scenario when I needed to use async/await in my OnActivityResult methods. In my examples I had started an activity and when I returned I needed to load data to be used in the layout. As soon as I used await the execution would fall through to the OnRestart/OnResume and if I wanted to use a single set of logic in these methods to display data I was in trouble. Bear in mind that with some re-organisation of my code and where I do my filling of controls I could probably minimise the impact of this issue but this example is being used to show the problem more clearly.

async protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); string itemId = data.GetStringExtra("ItemId"); _item = await ItemHelper.GetItem(itemId); }

</div>

In the following code in my OnResume I am setting a text edit to the item description:

protected override void OnResume() { base.OnResume(); // I'm in trouble here because the item hasn't actually // been retrieved yet. _itemTextEdit.Text = _item.Description; }

</div>

In normal threading situations I would have usually used something like an AutoResetEvent  to signal the logic that it is now ready to load but in the async/await world I needed something a little more appropriate. Enter TaskCompletionStatus. For more information on this bad boy please have a look at Stephen Toub’s article on MSDN. The nuts and bolts of it are that I can await on this in my OnResume and signal for it to continue once I have actually loaded the data it needs. The code for the OnActivityResult ends up looking like this:

private TaskCompletionSource<object> _taskCompletionSource = new TaskCompletionSource<object>(); async protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); _taskCompletionSource = new TaskCompletionSource<object>(); string itemId = data.GetStringExtra("ItemId"); _item = await ItemHelper.GetItem(itemId); // Set the result and signal any async code awaiting // on the TaskCompletionSource _taskCompletionSource.SetResult(null); }

</div>

The OnResume  is changed to include an await to prevent it from running until the data is ready:

protected override void OnResume() { base.OnResume(); // The execution will wait here until SetResult // is called. await _taskCompletionSource.Task; _itemTextEdit.Text = _item.Description; }

</div>

As I am exclusively using async/await the UI will continue to run without blocking but now the logic won’t attempt to update my controls until the data is ready.

As I said, this problem could probably be addressed by reorganising where the data retrieval is performed but it still demonstrates a strategy that could potentially be utilised in a variety of scenarios.

 

3. All of a sudden when I use a map fragment in my application the map loads with a grey screen. It was working before, what is going on?

I have seen this weirdness a few times and it seems to be a permission problem. I originally found the answer in this post by Ryan Langton on the Xamarin forums.

The crux of it is that when you change the properties of your AppManifest.xml using the property pages in Visual Studio, sometimes some strange stuff can happen to your xml and your maps will start showing up as blank.

I simply made sure my AndroidManifest.xml looked something like the following an everything starts working again:


<manifest xmlns:android=”
http://schemas.android.com/apk/res/android” android:installLocation=”auto” package=”au.com.company.someApp” android:versionCode=”3″ android:versionName=”1.2″>
    <uses-sdk android:targetSdkVersion=”14″ android:minSdkVersion=”14″ />
    <uses-permission android:name=”android.permission.CAMERA” />
    <!– Google Maps for Android v2 requires OpenGL ES v2 –>
    <uses-feature android:glEsVersion=”0x00020000″ android:required=”true” />
  <permission
          android:name=”au.com.company.someApp.permission.MAPS_RECEIVE”
          android:protectionLevel=”signature”/>
  <uses-permission android:name=”au.com.company.someApp.permission.MAPS_RECEIVE”/> 
    <!– We need to be able to download map tiles and access Google Play Services–>
  <uses-permission android:name=”android.permission.INTERNET” />
  <!– Allow the application to access Google web-based services. –>
  <uses-permission android:name=”com.google.android.providers.gsf.permission.READ_GSERVICES” />
  <!– Google Maps for Android v2 will cache map tiles on external storage –>
  <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
  <!– Google Maps for Android v2 needs this permission so that it may check the connection state as it must download data –>
  <uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
  <!– These are optional, but recommended. They will allow Maps to use the My Location provider. –>
  <uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />
  <uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
    <application android:label=”@string/app_name”>
        <!– Put your Google Maps V2 API Key here. This key will not work for you.–>
        <!– See
https://developers.google.com/maps/documentation/android/start#obtaining_an_api_key –>
        <meta-data android:name=”com.google.android.maps.v2.API_KEY” android:value=”” />
    </application>
</manifest></font> </p> </blockquote>

I don’t know why this happens but fixing the xml manually has sorted it for me several times.