ListView 에서 선택한 셀에 대한 색상을 변경하는 방법입니다.

아래는 기본 적인 ListView 코드입니다.

        <ListView x:Name="originalListView"
                  RowHeight="30"
                  SeparatorVisibility ="Default"
                  SeparatorColor="Black"
                  Margin="10"                  >
            <ListView.Header>
                <StackLayout Orientation="Horizontal"
                             VerticalOptions="CenterAndExpand"
                             Padding="0"
                             Spacing="0"
                             BackgroundColor="Gray"
                             HeightRequest="30">
                    <Label  Text="항목"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            FontSize="12"
                            WidthRequest="80"/>
                    <Label  Text="데이터1"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            WidthRequest="80"
                            FontSize="12"/>
                    <Label  Text="데이터2"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            WidthRequest="80"
                            FontSize="12"/>
                </StackLayout>
            </ListView.Header>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <ViewCell.View>
                            <StackLayout>
                                <StackLayout VerticalOptions="CenterAndExpand"
                                             Orientation="Horizontal"
                                             Padding="0"
                                             Spacing="0">
                                    <Label  Text="{Binding Content}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                    <Label  Text="{Binding Data1}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                    <Label  Text="{Binding Data2}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                </StackLayout>
                            </StackLayout>
                        </ViewCell.View>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

위처럼 Page 에 위치 시킨 후 구동하면 아래와 같이 선택한 컬럼에 대해서 아래와 같이 나타납니다.

(선택 색상이 전혀 어울리지 않습니다.^^; - 안드로이드 버젼별로 색이 다름)

 

이상한 자주색이 맘에 들지 않습니다.; 선택한 컬럼에 대해 색상을 변경해봅시다.

먼저 .Net Standard 프로젝트에 ExtendedViewCell 클래스를 추가합니다.

using Xamarin.Forms;

namespace Test.Extended
{
    public class ExtendedViewCell : ViewCell
    {
        public static readonly BindableProperty SelectedBackgroundColorProperty =
            BindableProperty.Create("SelectedBackgroundColor",
                                    typeof(Color),
                                    typeof(ExtendedViewCell),
                                    Color.Default);

        public Color SelectedBackgroundColor
        {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value); }
        }
    }
}

여기선 단순히 SelectedBackgroundColor 속성을 선언만 할 뿐입니다.

실질적인 동작은 각 장치별 렌더러에서 맡아서 처리하게됩니다.

 

Android 프로젝트로 가서 ExtendedViewCellRenderer 클래스를 추가합니다.

using Android.Content;
using Android.Graphics.Drawables;
using Android.Views;
using Test.Droid.ExtendedRenderer;
using Test.Extended;
using System.ComponentModel;
 
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace Test.Droid.ExtendedRenderer
{
    public class ExtendedViewCellRenderer : ViewCellRenderer
    {
        private Android.Views.View cellCoreView;
        private Drawable unSelectedBackground;
        private bool isSelected;

        protected override Android.Views.View GetCellCore(
            Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
        {
            this.cellCoreView = base.GetCellCore(item, convertView, parent, context);

            this.isSelected = false;
            this.unSelectedBackground = cellCoreView.Background;

            return cellCoreView;
        }

        protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            base.OnCellPropertyChanged(sender, args);

            if (args.PropertyName == "IsSelected")
            {
                this.isSelected = !this.isSelected;

                if (this.isSelected)
                {
                    var extendedViewCell = sender as ExtendedViewCell;
                    this.cellCoreView.SetBackgroundColor(extendedViewCell.SelectedBackgroundColor.ToAndroid());
                }
                else
                {
                    this.cellCoreView.SetBackground(this.unSelectedBackground);
                }
            }
        }
    }
}

 

iOS 프로젝트로 가서 ExtendedViewCellRenderer 클래스를 추가합니다.

using Test.Extended;
using Test.iOS.ExtendedRenderer;
using UIKit;

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

[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace Test.iOS.ExtendedRenderer
{
    public class ExtendedViewCellRenderer : ViewCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var cell = base.GetCell(item, reusableCell, tv);
            var view = item as ExtendedViewCell;
            cell.SelectedBackgroundView = new UIView
            {
                BackgroundColor = view.SelectedBackgroundColor.ToUIColor(),
            };

            return cell;
        }
    }
}

사전 작업은 완료되었습니다.

 

이제 기본 코드에서 ExtendedViewCell 을 사용하도록 변경합니다. (굵게표시된부분)

        <ListView x:Name="extendedListView"
                  RowHeight="30"
                  SeparatorVisibility ="Default"
                  SeparatorColor="Black"
                  Margin="10"                  >
            <ListView.Header>
                <StackLayout Orientation="Horizontal"
                             VerticalOptions="CenterAndExpand"
                             Padding="0"
                             Spacing="0"
                             BackgroundColor="Gray"
                             HeightRequest="30">
                    <Label  Text="항목"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            FontSize="12"
                            WidthRequest="80"/>
                    <Label  Text="데이터1"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            WidthRequest="80"
                            FontSize="12"/>
                    <Label  Text="데이터2"
                            HorizontalOptions="CenterAndExpand"
                            VerticalOptions="Center"
                            TextColor="Black"
                            HorizontalTextAlignment="Center"
                            WidthRequest="80"
                            FontSize="12"/>
                </StackLayout>
            </ListView.Header>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <extended:ExtendedViewCell SelectedBackgroundColor="LightGray">
                        <ViewCell.View>
                            <StackLayout>
                                <StackLayout VerticalOptions="CenterAndExpand"
                                             Orientation="Horizontal"
                                             Padding="0"
                                             Spacing="0">
                                    <Label  Text="{Binding Content}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                    <Label  Text="{Binding Data1}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                    <Label  Text="{Binding Data2}"
                                            HorizontalOptions="CenterAndExpand"
                                            VerticalOptions="Center"
                                            TextColor="Black"
                                            HorizontalTextAlignment="Center"
                                            WidthRequest="80"
                                            FontSize="12"/>
                                </StackLayout>
                            </StackLayout>
                        </ViewCell.View>
                    </extended:ExtendedViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

Selected Color 로 LightGray 를 설정한 결과는 아래과 같습니다.

완료!

xaml 을 디자인 하기란 어려운게 사실입니다.

특히나 힘든건 디자인한 화면을 꼭 빌드해서 봐야 할때입니다.

비쥬얼스튜디오 내에서 제공해 주는 디자인(Forms Previewer)은 정말 오류가 많이 납니다.

거의 보여주는걸 본적이 별로 없는...;

(도대체 언제 보여줄꺼니 ㅜㅠ 10분이 지남;;)

하지만 좋은점은 CustomRenderer 를 적용한 디자인도 무리없이 보여줍니다. (보여줄때는..)

고릴라 플레이어를 사용하는 사람들이 있어 써보았는데

코딩하는데로  빠르게 표현을 잘해서 이걸써야겠다 생각했는데....

CustomRenderer 가 있으면 표현을 하지 못합니다. ㅜㅠ

 

그런데 자마린 카페에서 LiveXAML  을 알게 되었고 깔아서 해봤는데

정말 신세계네요^^ (단, 유료입니다. ㅜㅠ)

 

경험한 신세계를 소개합니다.

1. 사이트로 가서 다운 설치파일을 다운로드 받고 설치합니다.

   웹사이트 : http://www.livexaml.com/

   다운로드 링크 : https://marketplace.visualstudio.com/items?itemName=ionoy.XamarinFormsLive-18843

   설치 완료후 비쥬얼 스튜디오를 실행하면 아래와 같은 화면이 뜹니다. (15일은 경험할수 있군요^^;)

 

2. 솔루션을 열고 솔루션 NuGet Package 관리에서 LiveXAML 을 검색 후 설치합니다.

 

3. Debug 모드로 핸드폰이나 애뮬레이터로 실행합니다.

  이 상태에서 xaml 파일을 열어서 수정을 하면 바로 고쳐져서 보이는걸 확인 할수 있습니다.

  정말 좋은건 애뮬에선 괜찮아서 막상 폰에 배포하면 먼가 안맞을 때가 많은데

  디자인을 보면서 수정을 할수 있어서 기기에 어떻게 표현되는지 바로 확인이 가능한 것입니다.

  

가격정책.. 일단 구독을 해봐야할까요.ㅜㅠ 아.. 정말 편하네요^^

MS 야 이것도 어떻게 안되겠니...ㅋ

 

 

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 는 안됨.^^;)

 

+ Recent posts