Wednesday, 23 May 2018

Droid explicit activities

You can think of activities as Views, they are more or less equivalent to Xaml pages with code-behinds; not as elegantly separated into partial classes, but in essence the same principle. Internal activities that is activities within the same package (.apk file) are made up of a axml file in the Resources->layout folder that represent the xml markup as well as activity c# classes.


Now I move my c# {name}Activity.cs files which are in essence our code behinds into their own folder with their own namespace, by default they're are located in the root of the project which doesn't sit well with me.

Activities in Android are not started directly, it's something the ART does for you. to switch to an activity you must:
  1. package your activity request inside of an intent
  2. Using the context you send the intent to the ART
  3. ART looks inside the intent and decides which intent to start for you
Intents are used to start activities and come in two flavors: implicit and explicit; explicit intents are used when you know the identity of the Activity to start.

To start an explicit activity

private void StatsBUTTON_Click(object sender, EventArgs e)
{
    var intent = new Intent(packageContext: this, type: typeof(StatsActivity));
    base.StartActivity(intent);
}

We define our intent by supplying it with our context and they type of the activity we want to start, keep in mind that activities inherit from context down in the class hierarchy which is why we can pass a reference to itself as the context.

once you navigate to you target activity, make sure is to set the content view inside your activity to the xml markup of your view that's that axml file

using Android.App;
using Android.OS;
using Android.Widget;

namespace pav.TicTacToe
{
    [Activity(Label = "StatsActivity")]
    public class StatsActivity : Activity
    {
        Button CancelBUTTON;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set the view from the resource.layout folder
            SetContentView(Resource.Layout.activity_stats);

            CancelBUTTON = FindViewById<Button>(Resource.Id.CancelBUTTON);
            CancelBUTTON.Click +=(s,a)=> this.Finish();
        }
    }
}

notice that in my OnCreate(Bundle savedInstanceState) method after i call the base OnCreate method i set the content view to that of my axml file that contains the markup for my view. without this you'll just get a blank page.

now in many situations when navigating between activities we're going to want to pass arguments from one to the next and maybe even back, the arguments that are passed between activities must be serializable types .

one way to pass data between activities is to leverage the putExtra method inside the intent, this is the same as creating a bundle and adding value key pairs to it, but a little less verbose

void ItemClicked(object sender, EventArgs e)
{
    var intent = new Intent(this, typeof(DetailsActivity));
    intent.PutExtra("Id", 45);
    base.StartActivity(intent);
}

To retrieve values from our bundle we can extract it from our intent

protected override void OnCreate(Bundle bundle)
{
    int position = base.Intent.GetIntExtra(name:"id",defaultValue: -1);
}

This is a rather trivial pattern, a more interesting approach would be for one activity to navigate to another, then that second activity to process the request and return some sort of result code with some data.

For example let's say that we have a guess the number game, now in this game we define the number to guess on the source Activity(View), but to guess the number we go to a target Activity (View), then return our guess from the target back to the source to check if it was correct.

In our source activity we would override or just call the StartActivityForResult(intent, requestCode, bundle) method to launch our target activity. this is where we'd set the data to send via the intent or bundle parameters, and the request code that would then identify the target returning results.

then in our Target activity we would extract our extras like we normally would wire up event receivers, etc; basically go about our business as usual.

the difference in our target would be that before we call the base finish method we'd have to call the SetResult method, here we'd set our result code [Canceled, Ok, FirstUser].

finally back in our source we have to override the OnActivityResult(requestCode, result, intent) method , here we can correlate our request and response using the request code, then identify the result using the result enum and finally extract any data from our target using the extras on the intent.