프로그래밍/xamarin

xamarin 강좌 006. Custom Renderer를 만들며 배우는 자마린의 구조

panpro 2018. 9. 13. 16:33




xamarin은 마이크로소프트에서 Visual Studio와 함께 무료로 배포하고 있는 스마트폰 앱개발 툴입니다. 


xamarin은 크로스 플랫폼(cross platform) 기술로 한번 코드를 작성해 놓으면 안드로이드, 아이폰, 윈도우에서 실행되는 스마트폰 앱을 한꺼번에 만들 수 있는 강력한 도구입니다. 





xamarin 강좌 이전 글


2018/09/13 - [프로그래밍/xamarin] - xamarin 강좌 005. 데스크탑, 스마트폰 등 디바이스 타입마다 다른 UI 만들기

2018/09/12 - [프로그래밍/xamarin] - xamarin 강좌 004. 안드로이드, 아이폰, 윈도우, 플랫폼마다 다르게 실행되는 코드 만들기

2018/09/12 - [프로그래밍/xamarin] - xamarin 강좌 003. iOS 빌드를 위한 맥북 설정 - 아이폰 실행화면 보기

2018/09/11 - [프로그래밍/xamarin] - xamarin 강좌 002. xamarin 기본생성 프로젝트의 구조 - 버튼 추가

2018/09/10 - [프로그래밍/xamarin] - xamarin 강좌 001. xamarin의 시작



xamarin 6번째 시간입니다. 


xamarin은 한번 정의해 놓은 UI 위치에 각 플랫폼에 맞는 UI가 나타나서 보여집니다. 


xaml 파일 안에 <Button> 이라고 해 놓으면 그 자리에 버튼이 보여지지요.


그런데 이 버튼을 가만 보고 있으면 아이폰 실행했을 때 보여지는 버튼이랑 안드로이드 실행했을 때 보여지는 버튼이랑 모양이 다릅니다. 


이유는, 각 플랫폼마다 버튼을 만들어서 그 자리에 보여주기 때문입니다. 

뭔 소린가 싶으실텐데요.





이런 구조에요.


xamarin에서 xaml 파일 안에 <Label> 이라는 태그를 넣으면, 



<Label Text="데스크탑용 UI"

                VerticalOptions="CenterAndExpand" 

                HorizontalOptions="CenterAndExpand" />



이런 식으로요,


저 Label 태그는 자리만 딱 잡아놓는 역할을 합니다. 


저렇게 태그가 있으면, 각각의 디바이스들이 Label과 같은 기능을 하는 자기 자체의 컨트롤을 생성해 냅니다. 


위 표에서 보면 android 에서 Label과 같은 기능을 하는 녀석은 TextView 라는 이름의 컨트롤입니다. 

아이폰에서는 UILabel 이라는 이름의 컨트롤이고, 

UWP에서는 TextBlock 라는 이름의 컨트롤입니다. 


아이폰이라면 UILable이라는 이름의 컨트롤을 생성해 내는 거지요.


그리고 그 아이폰에서 제공해 주는 UILabel의 겉모습을 

xamarin에서 제공해 주는 Label Renderer를 이용해

<Label> 태그가 있는 자리에 그려주는 거에요.


이게 핵심입니다. 


이에 대한 증명으로 custom renderer를 만드는 과정을 보여 드립니다. 


Renderer는 xamarin에서 제공해 주는 기능으로, 

각 디바이스별 native 컨트롤의 모양을 화면에 그려주는 객체입니다. 



그래서 우리는 이렇게 해보려 해요. 


먼저 <Label> 태그처럼 사용할 수 있는 객체를 만듭니다. 

이런 걸 View라고 부르는데요, 

xamarin에서는 Label, Button 같은 것들 모두 View라고 부릅니다. 

컨트롤이라고 부르지 않는 이유 이제 아시겠죠?


본격적인 컨트롤이 아니라 Renderer에 의해 그려져 있기만 하기 때문이에요.



public class CustomView : ContentView

{

public CustomView ()

{

}

}


이렇게 만들어요. 

엄청 간단하죠. 


ContentView를 상속받은 클래스를 하나 만들면 되요. 

이렇게 만들면 이걸 xaml 파일에서 사용할 수 있어요.


계속 언급한 자리잡는 위치로 사용하는 거죠.


MainPage.xaml 안에서 이렇게 사용하면 되요




<Label Text="Welcome to Xamarin.Forms!" 

           HorizontalOptions="Center"

           VerticalOptions="CenterAndExpand" />

<local:CustomView WidthRequest="300" HeightRequest="300" BackgroundColor="Yellow" />



자리잡은 위치를 정확히 보기 위해 BackgroundColor="Yellow" 를 줘서 노란색으로 보이게 했어요.







이렇게 하면 모든 디바이스에서 Welcome to Xamarin.Forms! 라고 쓰인 Label 밑에 노란색 영역이 잡힌 게 보여지지요. 이건 아이폰이건 안드로이드건 모든 디바이스에서 똑같이 보이게 됩니다. 


이제부터 본격적으로 이 노란색 영역에 자기 native 컨트롤들을 보여줄 각 디바이스들의 렌더러를 만들 건데요,


아이폰용, 안드로이드용, UWP용 렌더러를 만들 거에요.


그런데 이 렌더러들이 다 서로 상관없는 컨트롤들과 연결되도록 하려 해요.






이렇게요,


똑같은 View에 대해 (여기서는 우리가 만든 CustomView) 각각의 디바이스가 어떤 렌더러를 제공해 주느냐에 따라 각각 다른 모습을 보여주는 거지요.


xamarin의 View와 렌더러에 대해 알고 계셔야 xamarin에 대해 정확히 이해하면서 개발해 나갈 수 있어요.




각 디바이스에서 돌아가는 렌더러를 만드려면 아래와 같이


[assembly: ExportRenderer(typeof(CustomView), typeof(CustomRenderer_iOS))]


선언해 줘야 해요. 


typeof가 2개 보이는데 첫번째는 우리가 만든 CustomView 이고, 

두번째는 이 CustomView라는 view에다 대고 렌더링할 렌더러 클래스 이름입니다. 



그러니까 우리는 CustomRender_iOS 라는 클래스, 렌더러를 만들고 있는 중인거죠.


이 렌더러 클래스 안에서,


CustomView에 연결해줄 아이폰의 native 컨트롤을 생성해 CustomView에 붙여줍니다. 


이렇게 되면 그 아이폰의 native 컨트롤의 모양이 CustomView에 그려지는 거죠. (렌더링)


음.. 어렵지 않죠? ㅎ



아래 동영상 강좌로 확인하세요.









전체 소스 코드는 다음과 같습니다. 


[공용코드 (xa006 프로젝트)]



CustomView.cs (새로 생성)


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;


using Xamarin.Forms;


namespace xa006

{

public class CustomView : ContentView

{

public CustomView ()

{

}

}

}





MainPage.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:xa006"

             x:Class="xa006.MainPage">


    <StackLayout>

        <!-- Place new controls here -->

        <Label Text="Welcome to Xamarin.Forms!" 

           HorizontalOptions="Center"

           VerticalOptions="CenterAndExpand" />

        <local:CustomView WidthRequest="300" HeightRequest="300" BackgroundColor="Yellow" />

    </StackLayout>


</ContentPage>





[Android용 코드 (xa006.Android 프로젝트)]



CustomRenderer_android.cs (새로 생성)


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;


using Android.App;

using Android.Content;

using Android.OS;

using Android.Runtime;

using Android.Util;

using Android.Views;

using Android.Widget;

using xa006;

using xa006.Droid;

using Xamarin.Forms;

using Xamarin.Forms.Platform.Android;


[assembly: ExportRenderer(typeof(CustomView), typeof(CustomRenderer_android))]

namespace xa006.Droid

{

    public class CustomRenderer_android : ViewRenderer<CustomView, Android.Widget.DatePicker>

    {

        protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)

        {

            base.OnElementChanged(e);


            var datePicker = new Android.Widget.DatePicker(Forms.Context);


            SetNativeControl(datePicker);


        }

    }

}






[아이폰용 코드 (xa006.iOS 프로젝트)]



CustomRenderer_iOS.cs (새로 생성)


using System;

using System.Drawing;


using CoreGraphics;

using Foundation;

using UIKit;

using xa006;

using xa006.iOS;

using Xamarin.Forms;

using Xamarin.Forms.Platform.iOS;


[assembly: ExportRenderer(typeof(CustomView), typeof(CustomRenderer_iOS))]


namespace xa006.iOS

{    

    public class CustomRenderer_iOS : ViewRenderer<CustomView, UIKit.UISlider>

    {

        protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)

        {

            base.OnElementChanged(e);


            var slider = new UIKit.UISlider();


            SetNativeControl(slider);


        }

    }

}






[UWP용 코드 (xa006.UWP 프로젝트)]



CustomRenderer_UWP.cs (새로 생성)


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using xa006;

using xa006.UWP;

using Xamarin.Forms;

using Xamarin.Forms.Platform.UWP;


[assembly: ExportRenderer(typeof(CustomView), typeof(CustomRenderer_UWP))]

namespace xa006.UWP

{

    public class CustomRenderer_UWP : ViewRenderer<CustomView, Xamarin.Forms.Platform.UWP.EntryCellTextBox>

    {

        protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)

        {

            base.OnElementChanged(e);


            var box = new Xamarin.Forms.Platform.UWP.EntryCellTextBox();


            SetNativeControl(box);


        }


    }

}