Animation Text 에 이어 Animation Button 입니다.

AnimationButton 으로 명명을 하지 않은 이유는 이쪽은 앞으로 여러가지 기능이 추가될 예정이기 때문입니다.

.NET Standard 에 클래스 추가후 아래와같이 코딩합니다.

버튼이 눌렸을때 살짝 작아졌다 다시 원래 크기로 돌아오는 에니메이션을 적용한 내용입니다.

using Xamarin.Forms;

namespace Test.Cntrol
{
    public class CustomButton : Button
    {
        public CustomButton() : base()
        {
            const int animationTime = 50;

            Clicked += async (sender, e) =>
            {
                var btn = (CustomButton)sender;
                await btn.ScaleTo(0.8, animationTime, Easing.SinOut);
                await btn.ScaleTo(1, animationTime, Easing.SinIn);
            };
        }
    }
}

 

 

이제 화면 Page 에서 컨트롤을 위치시킵니다.

<?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:Text"
             xmlns:cntrol="clr-namespace:Text.Cntrol"
             x:Class="Text.MainPage">

    <StackLayout Spacing="5">
        <Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="Center" />
        <cntrol:AnimationText Text="Welcome to Xamarin.Forms" IsRunning="True" TextColor="Blue" HorizontalOptions="Center" Margin="0,15,0,15"/>
        <cntrol:CustomButton Text="Custom Button" WidthRequest="130" HeightRequest="40" HorizontalOptions="Center"
                             BackgroundColor="#81BE1D" CornerRadius="3" TextColor="White"/>

       
    </StackLayout>

</ContentPage>

 

아래는 결과 화면입니다.

Text 에 Animation 을 적용하여 글자가 순서대로 커졌다가 작아지는 효과를 주는 방법입니다.

.NET Standard 프로젝트에 클래스를 추가하여 아래와 같이 코딩합니다.

using Xamarin.Forms;

namespace Test.Cntrol
{
    public class AnimationText : StackLayout
    {
        private const string AnimationName = "AnimatedTextAnimation";

        public static readonly BindableProperty IsRunningProperty
            = BindableProperty.Create(nameof(IsRunning), typeof(bool), typeof(AnimationText), default(bool));

        public static readonly BindableProperty TextProperty
            = BindableProperty.Create(nameof(Text), typeof(string), typeof(AnimationText), default(string));

        private Animation animation;

        public AnimationText()
        {
            Orientation = StackOrientation.Horizontal;
            Spacing = -1;
        }

        public bool IsRunning
        {
            get => (bool)GetValue(IsRunningProperty);
            set => SetValue(IsRunningProperty, value);
        }

        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }

        public Color TextColor { get; set; } = Color.Blue;

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            if (propertyName == nameof(IsRunning) && IsEnabled)
            {
                if (IsRunning)
                {
                    StartAnimation();
                }
                else
                {
                    StopAnimation();
                }
            }

            if (propertyName == nameof(Text))
            {
                InitAnimation();
            }
        }

        private void InitAnimation()
        {
            this.animation = new Animation();
            Children.Clear();

            if (string.IsNullOrWhiteSpace(Text)) return;

            var index = 0;
            foreach (var textChar in Text)
            {
                var label = new Label
                {
                    Text = textChar.ToString(),
                    TextColor = this.TextColor,
                    FontAttributes = FontAttributes.Bold,
                    FontSize = 12
                };

                Children.Add(label);

                var oneCharAnimationLength = (double)1 / (Text.Length + 1);

                this.animation.Add(index * oneCharAnimationLength, (index + 1) * oneCharAnimationLength, 
                  new Animation(v => label.Scale = v, 1, 1.75, Easing.Linear));
                this.animation.Add((index + 1) * oneCharAnimationLength, (index + 2) * oneCharAnimationLength, 
                  new Animation(v => label.Scale = v, 1.75, 1, Easing.Linear));

                this.animation.Add(index * oneCharAnimationLength, (index + 1) * oneCharAnimationLength, 
                  new Animation(v => label.TranslationY = v, 0, -10, Easing.Linear));
                this.animation.Add((index + 1) * oneCharAnimationLength, (index + 2) * oneCharAnimationLength, 
                  new Animation(v => label.TranslationY = v, -10, 0, Easing.Linear));

                index++;
            }
        }

        private void StartAnimation()
        {
            animation.Commit(this, AnimationName, 16, (uint)Children.Count * 200, Easing.Linear, null, () => true);
        }

        private void StopAnimation()
        {
            this.AbortAnimation(AnimationName);
        }
    }
}

 

이제 Page 의 화면단에 아래와 같이 컨트롤을 위치시킵니다.

<?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:Test"
             xmlns:cntrol="clr-namespace:Test.Cntrol"
             x:Class="Test.MainPage">

    <StackLayout>
        <Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="Center" />
        <cntrol:AnimationText Text="Welcome to Xamarin.Forms" IsRunning="True" HorizontalOptions="Center"/>
    </StackLayout>

</ContentPage>

아래는 실행결과 입니다.

안드로이드, iOS 모두 정상적으로 동작합니다.^^

 

 

 

 

이전에 NetworkComms  을 이용해 윈도우 프로그램을 만든적이 있었습니다.

여기에 Xamarin.Android 프로젝트를 이용해

앱을 만들어 보았는데 생각보다 거침없이?? 되더군요^^

기본 설명과 소스는 아래 깃헙 주소에 있습니다.

https://github.com/kjundev/NetworkComms.Chat

 

아래와 같이 안드로이드 및 윈도우 프로그렘간에 메세지를 주고 받을 수 있습니다.

 

실행방법은 간단합니다.

1. IP, Port 를 넣고 서버를 구동합니다. (CommsService)

2. 방화벽 해제가 필요하면 해제합니다.

3. 클라이언트를 실행합니다. > 윈도우용(CommsClient), 안드로이드용(CommsApp)

  - 서버의 IP 와 Port 를 입력하고 사용자명 입력후 Connect 합니다.

   (안드로이드는 사용자 명이 Android 로 고정) 

4. 메세지를 전달합니다.

앱을 개발하다보면 기기 화면 크기에 따른 상대 비율로 컨트롤의 크기를 지정해야할 때가 있습니다.

당연히 화면 크기에 대한 처리가 기본적으로 제공될꺼라고 생각했는데 아니더군요;;

그래서 화면 크기를 알수 있는 방법을 소개합니다.

2가지 방법이 있습니다.

 

1. Xamarin.Essentials 사용하는 방법

NuGet 에서 Xamarin.Essentials 를 설치하고

using Xamarin.Essentials;

을 추가 후 아래와 같이 사용하면 됩니다.

// Get Metrics
var metrics = DeviceDisplay.ScreenMetrics;

// Orientation (Landscape, Portrait, Square, Unknown)
var orientation = metrics.Orientation;

// Rotation (0, 90, 180, 270)
var rotation = metrics.Rotation;

// Width (in pixels)
var width = metrics.Width;

// Height (in pixels)
var height = metrics.Height;

// Screen density
var density = mainDisplayInfo.Density;

 

2. 기기별로 값을 가져오도록 직접 구현

.Net Standard 프로젝트의 App.xaml.cs 파일에 아래 처럼 속성을 추가합니다.

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation (XamlCompilationOptions.Compile)]
namespace Test
{
    public partial class App : Application
    {
        public static int ScreenHeight { get; set; }
        public static int ScreenWidth { get; set; }

        public App ()
        {
            InitializeComponent();
            MainPage = new MainPage();
        }

        protected override void OnStart ()
        {
             // Handle when your app starts
        }

        protected override void OnSleep ()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume ()
        {
            // Handle when your app resumes
        }
    }
}

 

Android 프로젝트의 MainActivity.cs 에 아래처럼 화면 크기를 가져오는 코딩을 추가합니다.

using Android.App;
using Android.Content.PM;
using Android.Gms.Ads;
using Android.OS;
using Microsoft.AppCenter.Push;

namespace Test.Droid
{
    [Activity(Label = "Test", Icon = "@drawable/test", Theme = "@style/MainTheme", MainLauncher = false, 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);

             App.ScreenHeight = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
             App.ScreenWidth = (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);

             LoadApplication(new App());
        }
    }
}

 

iOS 프로젝트의 AppDelegate.cs 에 아래처럼 화면 크기를 가져오는 코딩을 추가합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;

namespace Test.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();

            App.screenWidth = (int)UIScreen.MainScreen.Bounds.Width;
            App.screenHeight = (int)UIScreen.MainScreen.Bounds.Height;

            LoadApplication(new App());

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

 

이제 프로젝트 어디에서든 App.ScreenWidth, App.ScreenHeight 으로 화면 크기값을 알수 있습니다.

앱의 수익을 위한 전면광고 다는 방법입니다.

먼저 AdMob 을 통해 광고 ID 를 Android, iOS 별로 만들었다는 가정하에 진행합니다.

참고 : http://kjcoder.tistory.com/276 및 구글링

 

전면 광고를 넣기위해 DependencyService 를 사용해야합니다.

개념은 아래 링크를 통해서..^^

https://docs.microsoft.com/ko-kr/xamarin/xamarin-forms/app-fundamentals/dependency-service/introduction

간단하게 말하면 각 플랫폼별 기능을 .Net Standard 프로젝트에서 하나의 코드로 호출할 수 있도록 해주는 것입니다.

 

.Net Standard 프로젝트에 IInterstitialAd 인터페이스를 만듭니다.

namespace Test
{
    public interface IInterstitialAd
    {
        void Show(string adUnitId);
    }
}


Android 프로젝트에 아래의 NuGet 패키지를 설치합니다.

InterstitialAdByAndroid 클래스를 아래와 같이 추가합니다.

using Android.Content;
using Android.Gms.Ads;
using Android.Media;
using Test.Droid;
using System.Threading.Tasks;

[assembly: Xamarin.Forms.Dependency(typeof(InterstitialAdByAndroid))]
namespace Test.Droid
{
    class InterstitialAdByAndroid : AdListener, IInterstitialAd
    {
        InterstitialAd interstitialAd;

        public void Show(string adUnitId)
        {
            var context = Android.App.Application.Context;
            InterstitialAd ad = new InterstitialAd(context);
            ad.AdUnitId = adUnitId;

            this.interstitialAd = ad;
            OnAdLoaded();
            ad.AdListener = this;

            var requestBuilder = new AdRequest.Builder();
            ad.LoadAd(requestBuilder.Build());
        }

        public override void OnAdClosed()
        {

        }

        public override void OnAdLoaded()
        {
            base.OnAdLoaded();

            if (this.interstitialAd.IsLoaded)
            {
                this.interstitialAd.Show();
            }
        }
    }
}


iOS 프로젝트에 아래와 같은 NuGet 패키지를 설치합니다.

InterstitialAdByiOS 클래스를 아래와 같이 추가합니다.

using Google.MobileAds;
using Test.iOS;
using System.Threading.Tasks;
using UIKit;
using Xamarin.Forms;

[assembly: Xamarin.Forms.Dependency(typeof(InterstitialAdByiOS))]
namespace Test.iOS
{
    public class InterstitialAdByiOS : IInterstitialAd
    {
        Interstitial interstitialAd;

        public async void Show(string adUnitId)
        {
            this.interstitialAd = new Interstitial(adUnitId);
            this.interstitialAd.LoadRequest(Request.GetDefaultRequest());

            while (!this.interstitialAd.IsReady)
            {
                await Task.Delay(100);
            }

            Device.BeginInvokeOnMainThread(() => this.interstitialAd.PresentFromRootViewController(UIApplication.SharedApplication.Windows[0].RootViewController));
        }
    }
}


이제 마지막으로 실제 .Net Standard 프로젝트에서 호출하는 방법입니다.

// 전면 광고
string adUnitId = "ca-app-pub-4681470946279796/3339648514";
if (Device.RuntimePlatform == Device.iOS)
{
    adUnitId = "ca-app-pub-4681470946279796/8947198411";
}

DependencyService.Get<IInterstitialAd>().Show(adUnitId);

 

위와 같이 하면 전면광고가 노출되게 됩니다.

Editer 컨트롤에는 불행하게요 PlaceHolder 관련 속성이 없습니다.

(PlaceHolderColor 는 최근에 추가된 속성입니다.)

이를 만드는 방법입니다.

먼저 기본은 지난 글을 기본으로 해서 진행합니다.

 

.Net Statndrd 프로젝트에 CustomEditor 를 추가합니다.

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace Test.Controls
{
    public class CustomEditor : Editor
    {
        public static BindableProperty PlaceholderProperty
         = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(CustomEditor));

        public static BindableProperty PlaceholderColorProperty
           = BindableProperty.Create(nameof(PlaceholderColor), typeof(Color), typeof(CustomEditor), Color.Gray);

        public static BindableProperty IsExpandableProperty
        = BindableProperty.Create(nameof(IsExpandable), typeof(bool), typeof(CustomEditor), false);

        public string Placeholder
        {
            get { return (string)GetValue(PlaceholderProperty); }
            set { SetValue(PlaceholderProperty, value); }
        }

        public Color PlaceholderColor
        {
            get { return (Color)GetValue(PlaceholderColorProperty); }
            set { SetValue(PlaceholderColorProperty, value); }
        }

        public bool IsExpandable
        {
            get { return (bool)GetValue(IsExpandableProperty); }
            set { SetValue(IsExpandableProperty, value); }
        }

        public CustomEditor()
        {
            TextChanged += OnTextChanged;
        }

        private void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (IsExpandable) InvalidateMeasure();
        }
    }
}

 

Android 프로젝트에 CustomEditorRenderer 를 추가합니다.

using Android.Content;
using Android.Graphics.Drawables;
using Test.Controls;
using Test.Droid;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace Test.Droid
{
    public class CustomEditorRenderer : EditorRenderer
    {
        public CustomEditorRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Editor> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                // 밑줄을 보이지 않도록 하기
                GradientDrawable gd = new GradientDrawable();
                gd.SetColor(global::Android.Graphics.Color.Transparent);
                this.Control.SetBackgroundDrawable(gd);
            }

            if (e.NewElement != null)
            {
                var customControl = (CustomEditor)Element;

                if (!string.IsNullOrEmpty(customControl.Placeholder))
                {
                    Control.Hint = customControl.Placeholder;
                    Control.SetHintTextColor(customControl.PlaceholderColor.ToAndroid());
                }
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            var customControl = (CustomEditor)Element;

            if (CustomEditor.PlaceholderProperty.PropertyName == e.PropertyName)
            {
                Control.Hint = customControl.Placeholder;
            }
            else if (CustomEditor.PlaceholderColorProperty.PropertyName == e.PropertyName)
            {
                Control.SetHintTextColor(customControl.PlaceholderColor.ToAndroid());
            }
        }
    }
}

 

iOS 프로젝트에 CustomEditorRenderer 를 추가합니다.

using Foundation;
using Test.Controls;
using Test.iOS;
using System.ComponentModel;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace Test.iOS
{
    public class CustomEditorRenderer : EditorRenderer
    {
        UILabel placeholderLabel;

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Editor> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                if (placeholderLabel == null)
                {
                    CreatePlaceholder();
                }
            }

            if (e.NewElement != null)
            {
                var customControl = (CustomEditor)e.NewElement;

                if (customControl.IsExpandable)
                    Control.ScrollEnabled = false;
                else
                    Control.ScrollEnabled = true;
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            var customControl = (CustomEditor)Element;

            if (CustomEditor.IsExpandableProperty.PropertyName == e.PropertyName)
            {
                if (customControl.IsExpandable)
                    Control.ScrollEnabled = false;
                else
                    Control.ScrollEnabled = true;
            }
        }

        public void CreatePlaceholder()
        {
            var element = Element as CustomEditor;

            if (element == null) return;

            placeholderLabel = new UILabel
            {
                Text = element.Placeholder,
                TextColor = element.PlaceholderColor.ToUIColor(),
                BackgroundColor = UIColor.Clear
            };

            var edgeInsets = Control.TextContainerInset;
            var lineFragmentPadding = Control.TextContainer.LineFragmentPadding;

            Control.AddSubview(placeholderLabel);

            var vConstraints = NSLayoutConstraint.FromVisualFormat(
                "V:|-" + edgeInsets.Top + "-[PlaceholderLabel]-" + edgeInsets.Bottom + "-|", 0, new NSDictionary(),
                NSDictionary.FromObjectsAndKeys(
                    new NSObject[] { placeholderLabel }, new NSObject[] { new NSString("PlaceholderLabel") })
            );

            var hConstraints = NSLayoutConstraint.FromVisualFormat(
                "H:|-" + lineFragmentPadding + "-[PlaceholderLabel]-" + lineFragmentPadding + "-|",
                0, new NSDictionary(),
                NSDictionary.FromObjectsAndKeys(
                    new NSObject[] { placeholderLabel }, new NSObject[] { new NSString("PlaceholderLabel") })
            );

            placeholderLabel.TranslatesAutoresizingMaskIntoConstraints = false;

            Control.AddConstraints(hConstraints);
            Control.AddConstraints(vConstraints);
        }
    }
}

 

xaml 코드

콘텐츠 페이지를 만들어 아래와 같이 코딩합니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:Test.Controls"
             x:Class="Test.TestPage" BackgroundColor="#253570">
    <ContentPage.Content>
            <StackLayout>
                    <controls:CustomEditor x:Name="customEditor" Margin="7,0,7,0"
                                       BackgroundColor="#DEDFE4" FontSize="15"
                                       IsExpandable="true" Placeholder = "내용(30자이내)" PlaceholderColor="Blue"
                                       MaxLength="30" />
            </StackLayout>
    </ContentPage.Content>
</ContentPage>

 

결과

PlaceholderColor="SlateGray"

 

Editer 컨트롤에서 사용자가 입력한 글자가 컨트롤 크기를 벗어날 때 자동으로 옆과 아래쪽으로

Editer 가 늘어나도록 하는 방법입니다.

우선 .Net Standard 프로젝트에 CustomEditor 를 추가합니다.

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace Test.Controls
{
    public class CustomEditor : Editor
    {
        public static BindableProperty IsExpandableProperty
        = BindableProperty.Create(nameof(IsExpandable), typeof(bool), typeof(CustomEditor), false);

        public bool IsExpandable
        {
            get { return (bool)GetValue(IsExpandableProperty); }
            set { SetValue(IsExpandableProperty, value); }
        }

        public CustomEditor()
        {
            TextChanged += OnTextChanged;
        }

        private void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (IsExpandable) InvalidateMeasure();
        }
    }
}

InvalidateMeasure 부분이 핵심입니다

 

Android 프로젝트에 CustomEditorRenderer 를 추가합니다.

Android 쪽은 추가 하지 않아도 기능은 동작합니다.

다만 밑줄을 안보이게 하기위해 추가된거라고 보면되겠네요.(그리고 나중을 위해서^^)

using System.ComponentModel;
using Android.Content;
using Android.Graphics.Drawables;
using Test.Controls;
using Test.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace Test.Droid
{
    public class CustomEditorRenderer : EditorRenderer
    {
        public CustomEditorRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Editor> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                // 밑줄을 보이지 않도록 하기
                GradientDrawable gd = new GradientDrawable();
                gd.SetColor(global::Android.Graphics.Color.Transparent);
                this.Control.SetBackgroundDrawable(gd);
            }
        }
     }
}

 

iOS 프로젝트에도 CustomEditorRenderer 를 추가합니다.

using System.ComponentModel;
using Test.Controls;
using Test.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace Test.iOS
{
    public class CustomEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Editor> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                var customControl = (CustomEditor)e.NewElement;

                if (customControl.IsExpandable)
                    Control.ScrollEnabled = false;
                else
                    Control.ScrollEnabled = true;
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            var customControl = (CustomEditor)Element;

            if (CustomEditor.IsExpandableProperty.PropertyName == e.PropertyName)
            {
                if (customControl.IsExpandable)
                    Control.ScrollEnabled = false;
                else
                    Control.ScrollEnabled = true;
            }
        }
    }
}

 

xaml 코드

콘텐츠 페이지를 만들어 아래와 같이 코딩합니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:Test.Controls"
             x:Class="Test.TestPage" BackgroundColor="#253570">
    <ContentPage.Content>
            <StackLayout>
                    <controls:CustomEditor x:Name="customEditor" Margin="7,0,7,0"
                                                   BackgroundColor="#DEDFE4" FontSize="15" HorizontalOptions="Start"
                                                   IsExpandable="true" MaxLength="30" />
            </StackLayout>
    </ContentPage.Content>
</ContentPage>

위코드 CustomEditor 의 IsExpandable = true 로 가변크기가 가능하도록 했고 MaxLength 30으로 길이 제한이 있도록 했습니다.

 

이제 실행해 보면

Editer 에 글자를 입력하면 입력한 대로 Editer 크기가 변하는 걸 확인할 수 있습니다.

(캡쳐하다가 마우스 포인터까지..;;)

아래위나 옆으로 컨트롤이있다면 그 컨트롤들이 밀리면서 늘어나기때문에 글자 입력이 잘리지 않습니다.

 

 

모바일에서 기본 폰트는 깔끔하지만

디자인을 위해 다른 폰트를 적용하고 싶을 때가 있습니다.

안드로이드, iOS  에 적용하는 방법을 소개합니다.

 

우선 Android 안드로이드나 iOS 프로젝트에 폰트 파일을 import 시켜야합니다.

(폰트 파일 예시 : Binggrae.ttf)

Android 프로젝트는 Assets 폴더에

iOS 프로젝트는 Resources 폴더에 폰트 파일을 추가합니다.

추가로, iOS 는 Info.plist 파일에 아래 처럼 내용을 추가해야합니다.

<key>UIAppFonts</key>
 <array>
  <string>Binggrae.ttf</string>
 </array>

 

이제 각 프로젝트(Android,iOS)에 파일을 하나씩 추가해야합니다.

ExtendedRendrerer 를 추가할껀데 이것의 의미는 쉽게 말하면 기본 컨트롤에 대해서

각 기기 별로 커스텀하게 적용할 부분이 필요할 때 사용되는 것이라 보면됩니다.

우리는 폰트를 변경할 것이므로 폰트만 바꾸는 코드를 넣을 것입니다.

Button 예시입니다.

Android

using Android.Content;
using Android.Graphics;
using Test.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(Button), typeof(ExtendedButtonRenderer))]
namespace Test.Droid
{
    public class ExtendedButtonRenderer : ButtonRenderer
    {
        public ExtendedButtonRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                  Control.Typeface = Typeface.CreateFromAsset(base.Context.Assets, "Binggrae.ttf");
            }
        }
    }
}

iOS

using Test.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(Button), typeof(ExtendedButtonRenderer))]
namespace Test.iOS
{
    public class ExtendedButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                Control.Font = UIKit.UIFont.FromName("Binggrae", (System.nfloat)e.NewElement.FontSize);
            }
        }
    }
}

위처럼 적용하면 앱내 모든 버튼의 Text 는 Binggrae 폰트가 적용되어 보여집니다.

Picker, Entry, Label.. 등의 컨트롤 모두가 ExtendedRendrerer 를 구성해야 폰트가 적용됨을 유의해야합니다.

 

추가로 Android 는 한가지 방법이 더 있습니다.

아래 처럼 각 컨트롤의 FontFamily 에 아래처럼 폰트 파일명과 폰트 이름을 넣으면 폰트가 적용됩니다.

FontFamily="Binggrae.ttf#Binggrae"

(iOS 는 안됨.^^;)

 

폰 사용자가 기본 글자 크기를 키워버린 경우

아래처럼 디자인에 악영향을 줄수 있다.

안드로이드에서는 아래와 같은 경우 아래 코드를 삽입하여 해결할 수 있다.

안드로이드 프로젝트에서 MainActivity.cs 파일에 아래 코드를 삽입하면 된다.

using Android.Content.Res; 을 추가하고

        public override Resources Resources
        {
            get
            {
                Resources res = base.Resources;
                Configuration config = new Configuration();
                config.SetToDefaults();
                res.UpdateConfiguration(config, res.DisplayMetrics);
                return res;
            }
        }

위 코드를 삽입하면 아래처럼 사용자가 글자크기를 키웠음에도 크기를 기본 폰트 크기로 고정할 수 있다.

https://www.mono-project.com/docs/about-mono/releases/5.18.0/

.NET Framework 4.7.2와 호환
 macOS에서 구동할 때에는 최소 10.9 (매버릭스) 버전 이상이 필요하도록 변경
 그 외 성능, 보안, 문제점 수정



+ Recent posts

티스토리 툴바