Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Xamarin Mvvmcross Android Shared Element Navigation example?

I'm trying to get this animation/transition working in my Xamarin Android application with Mvx.

I have a recyclerview with cards. When tapping on a card, I now call:

private void TimeLineAdapterOnItemClick(object sender, int position)
{
    TimeLineAdapter ta = (TimeLineAdapter) sender;
    var item = ta.Items[position];

    int photoNum = position + 1;
    Toast.MakeText(Activity, "This is photo number " + photoNum, ToastLength.Short).Show();

    ViewModel.ShowDetails(item.Id);
}

I'm trying to find out how to translate this java navigation with transition to Xamarin with Mvvmcross:

ActivityOptionsCompat options = 
   ActivityOptionsCompat.MakeSceneTransitionAnimation(this, imageView, getString(R.string.activity_image_trans));

startActivity(intent, options.toBundle());

I know that within Mvx you can make use of custom presenters, but how do I get hold of, for example, the ImageView of the tapped Card within the RecyclerView which I would like to 'transform' to the new ImageView on the new Activity?

Thanks!

.

like image 519
JonHendrix Avatar asked Dec 24 '22 18:12

JonHendrix


1 Answers

Is there a Xamarin Mvvmcross Android Shared Element Navigation example?

I do not believe so.

I know that within Mvx you can make use of custom presenters, but how do I get hold of, for example, the ImageView of the tapped Card within the RecyclerView which I would like to 'transform' to the new ImageView on the new Activity?

The easiest way that I can think of to achieve the sharing of control elements you want to transition is via the use of view tags and a presentation bundle when using ShowViewModel.

I would suggest making some changes to your Adapter Click handler to include the view of the ViewHolder being selected (See GitHub repo for example with EventArgs). That way you can interact with the ImageView and set a tag that can be used later to identity it.

private void TimeLineAdapterOnItemClick(object sender, View e)
{
    var imageView = e.FindViewById<ImageView>(Resource.Id.imageView);
    imageView.Tag = "anim_image";

    ViewModel.ShowDetails(imageView.Tag.ToString());
}

Then in your ViewModel, send that tag via a presentationBundle.

public void ShowDetails(string animationTag)
{
    var presentationBundle = new MvxBundle(new Dictionary<string, string>
    {
        ["Animate_Tag"] = animationTag
    });

    ShowViewModel<DetailsViewModel>(presentationBundle: presentationBundle);
}

Then create a custom presenter to pickup the presentationBundle and handle the creating of new activity with the transition. The custom presenter which makes use of the tag to find the element that you want to transition and include the ActivityOptionsCompat in the starting of the new activity. This example is using a MvxFragmentsPresenter but if you are not making use of fragments and using MvxAndroidViewPresenter the solution would be almost identical (Override Show instead and no constructor required).

public class SharedElementFragmentsPresenter : MvxFragmentsPresenter
{
    public SharedElementFragmentsPresenter(IEnumerable<Assembly> AndroidViewAssemblies)
        : base(AndroidViewAssemblies)
    {
    }

    protected override void ShowActivity(MvxViewModelRequest request, MvxViewModelRequest fragmentRequest = null)
    {
        if (InterceptPresenter(request))
            return;

        Show(request, fragmentRequest);
    }

    private bool InterceptPresenter(MvxViewModelRequest request)
    {
        if ((request.PresentationValues?.ContainsKey("Animate_Tag") ?? false)
            && request.PresentationValues.TryGetValue("Animate_Tag", out var controlTag))
        {
            var intent = CreateIntentForRequest(request);

            var control = Activity.FindViewById(Android.Resource.Id.Content).FindViewWithTag(controlTag);
            control.Tag = null;

            var transitionName = control.GetTransitionNameSupport();
            if (string.IsNullOrEmpty(transitionName))
            {
                Mvx.Warning($"A {nameof(transitionName)} is required in order to animate a control.");
                return false;
            }

            var activityOptions = ActivityOptionsCompat.MakeSceneTransitionAnimation(Activity, control, transitionName);

            Activity.StartActivity(intent, activityOptions.ToBundle());
            return true;
        }

        return false;
    }
}

GetTransitionNameSupport is an extension method that just does a platform API check when getting the TransitionName.

public static string GetTransitionNameSupport(this ImageView imageView)
{
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
        return imageView.TransitionName;

    return string.Empty;
}

The final step would be to register the custom presenter in you Setup.cs

protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
    var mvxPresenter = new SharedElementFragmentsPresenter(AndroidViewAssemblies);
    Mvx.RegisterSingleton<IMvxAndroidViewPresenter>(mvxPresenter);
    return mvxPresenter;
}

You can check the repo on GitHub which demonstrates this example. The solution is designed so that the presenter does not have to care about the type of the control that is being transitioned. A control only requires a tag used to identify it. The example in the repo also allows for specifying multiple control elements that you want to transition (I did not want to include more complexity in the example above).

Shared Element Demo

like image 185
Plac3Hold3r Avatar answered Jan 05 '23 00:01

Plac3Hold3r