Xamarin.Formsで画像にTintColorを適用する - FFImageLoading編


[前回](http://qiita.com/sudachi808/items/c1fafb9098c93f185909)はカスタムレンダラーを利用して実現しましたが、カスタムレンダラーの実装は iOS や Android の知識が求められます。

両方ともまったく知らないワタシには手に負えません。

なので、今回はクロスプラットフォームの画像描画パッケージである [FFImageLoading](https://github.com/luberda-molinet/FFImageLoading)というものを使って、カスタムレンダラーを使わずに実装してみます。

## FFImageLoadingを追加

各プラットフォームに、下記のパッケージを追加します。

- FFImageLoading Plugin for Xamarin Forms
- FFImageLoading Transformations

![nuget.png](https://qiita-image-store.s3.amazonaws.com/0/169798/264130f5-68f3-c570-a9df-393b027bafa0.png "nuget.png")

## 実装

### PCL

Image の代わりに FFImageLoading が提供する CachedImage というクラスを継承して、カスタムコントロールを作成します。

```csharp:TintImage.cs
using System;
using System.Collections.Generic;

using Xamarin.Forms;

using FFImageLoading.Forms;
using FFImageLoading.Transformations;
using FFImageLoading.Work;

namespace MyApp
{
    public class TintImage : CachedImage
    {
        TintTransformation _Tint;
        
        public TintImage()
        {
            _Tint = new TintTransformation {
                EnableSolidColor = true
            };
        }
        
        private void ApplyTransformations()
        {
            Transformations = new List<ITransformation> {
                _Tint
            };
        }
        
        protected void OnTintColorChanged()
        {
            _Tint.R = (int)(TintColor.R * 255);
            _Tint.G = (int)(TintColor.G * 255);
            _Tint.B = (int)(TintColor.B * 255);
            _Tint.A = (int)(TintColor.A * 255);
            
            ApplyTransformations();
        }
        
        public static readonly BindableProperty TintColorProperty = BindableProperty.Create(
            "TintColor", typeof(Color), typeof(TintImage), Color.Default,
            propertyChanged: (bindable, oldValue, newValue) => ((TintImage)bindable).OnTintColorChanged()
        );
        
        public Color TintColor {
            get { return (Color)GetValue(TintColorProperty); }
            set { SetValue(TintColorProperty, value); }
        }
    }
}
```

```xml:XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyApp"
    x:Class="MyApp.MyAppPage">
    
    <StackLayout VerticalOptions="Center" HorizontalOptions="Center">
    
        <local:TintImage Source="sample.png" HeightRequest="48" />
        <local:TintImage Source="sample.png" HeightRequest="48" TintColor="Red"/>
        <local:TintImage Source="sample.png" HeightRequest="48" TintColor="Blue" />
        
    </StackLayout>
    
</ContentPage>
```

### iOS

FFImageLoading の初期化コードを追加します。

```csharp:AppDelegate.cs
using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

using FFImageLoading.Forms.Touch;   // <-- 追加

namespace MyApp.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            CachedImageRenderer.Init();     // <-- 追加

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}
```

### Android

FFImageLoading の初期化コードを追加します。

```csharp:MainActivity.cs
using System;

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using FFImageLoading.Forms.Droid;   // <-- 追加

namespace MyApp.Droid
{
    [Activity(Label = "MyApp.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            CachedImageRenderer.Init();     // <-- 追加

            LoadApplication(new App());
        }
    }
}
```

## 結果

### iOS
![ff_result_ios.png](https://qiita-image-store.s3.amazonaws.com/0/169798/c6df78df-bd72-8ec0-d00f-86ec629f41e2.png "ff_result_ios.png")

### Android
![ff_result_android.png](https://qiita-image-store.s3.amazonaws.com/0/169798/e3125714-9315-7d78-2f93-502d30d29d45.png "ff_result_android.png")


カスタムレンダラーを使わずに TintColor を設定できました。
これは助かりますね。

とりあえずは以上です。