Erhan Ballıeker

Xamarin.Forms ConfinstaApp Sample Bölüm 4 – SlideOverKit, Swipable Menu

Selamlar,

Bu serimizin son yazısında yine çok faydalı bir plugin den bahsetmek istiyorum. Hem bu örnek boyunca ilerlemiş olduğumuz Confinsta app i sona erdirecek hem de uygulamalarımızda bizden ekranın farklı yerlerinde farklı şekillerde açılıp kapanan menüler istendiğinde bunu kısa ve hızlı bir şekilde nasıl hallederiz buna bakacağız.

Kullanacak olduğumuz pluginimiz SlideOverKit

Yine solutionımızdaki her projeye bunu ekledikten sonrai plaform spesifik taraflarda gerekli initialize işlemini yapıyoruz.

Detaylı kullanımı için SlideOverKit in kendi github sayfasını ve örneklerini incelemenizi öneririm. Buradan ulaşabilirsiniz.

Bu plugin confinsta app boyunca Customrenderer yazmış olmamı gerekitiren tek şey. Ama aslında yazacağınız custom renderer ın örneklerden alıp copy paste yapmaktan başka neredeyse değiştirmeniz gereken peki bir kısmı kalmıyor(class isimleri hariç) 🙂

Ama arka planda neler yaptığını bilmek isterseniz benim gibi github dan inceleyebilirsiniz.

Önce bir ekranı hatırlayalım, sonra da tek tek kod taraflarına bakalım.

Resimde gördüğünüz gibi, sağ üst köşede ki hamburger buton ikonuna basıldığında ekranın yarısı kadar ene sahip ve tam boyda bir menü sayfası açıyoruz. Tıpkı instagramda olduğu gibi.

Önce sayfanın kendisine bir bakalım.

Sayfada yukarıdaki gibi bir navigation bar kullanmak eskiden custom renderer yazmak gerektirirdi. Ama yeni gelen özellikler ile sayfanın NavigationView ını istediğimiz gibi design edebiliyoruz artık xaml tarafında. Ben sağ üst köşeye bir imaj koydum ve buna bir tapgesture ekledim. Basıldığında da menü yü toggle ediyorum. Bu kadar. Xaml tarafı şu şekilde.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ConfinstaApp.Views.ProfileView">

    <NavigationPage.TitleView>
        <StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10,0,10,0" Spacing="10">
            <Image Source="17.png" HorizontalOptions="StartAndExpand" WidthRequest="25" HeightRequest="25"></Image>
            <Label Text="erhanballieker" FontSize="Medium" FontAttributes="Bold" HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Margin="0,0,25,0"/>
            <Image Source="11.png" HorizontalOptions="End" WidthRequest="25" HeightRequest="25">
                <Image.GestureRecognizers>
                    <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
                </Image.GestureRecognizers>
            </Image>
        </StackLayout>
    </NavigationPage.TitleView>
    
    <ContentPage.Content>
        <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
            <Label FontSize="Medium" FontAttributes="Bold" Text="erhanballieker Profile Page" HorizontalOptions="Center"></Label>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Yukarıda gördüğünüz gibi NavigationPage.TitleView içerisinde navigation design ım var. Sayfanın content ine hiçbir şey koymadım (Bir adet label ) dışında çünkü burada odaklandığımız yer sayfa içerisine eklediğimiz açılıp kapanan menü.

Sayfanın backend tarafı ise aşağıdaki gibi. Dikkat etmemiz gereken tek şey, Sayfanın ContentPage dışında bir de IMenuContainerPage interface ini implemente etmiş olması.

Bu interface den bize 3 adet property geliyor.

Bunlar;

  • SlideMenu Tipinde bir SlideMenu Prop u
  • Action tipinde bir ShowMenuAction prop u
  • Action tipinde bir HideMenuAction prop u

bunlar dışında sayfada sadece hamburger butonun gesturetapped event i var.

Bir diğer dikkat edeceğimiz nokta ise sayfanın constructor nda bana interface den gelen SlideMenu propertysine atadığım RightSideMenuContentView. 

	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class ProfileView : ContentPage, IMenuContainerPage
        {
           public ProfileView()
	   {
	       InitializeComponent ();

               this.SlideMenu = new RightSideMenuContentView();
            }

        public SlideMenuView SlideMenu { get; set; }
        public Action ShowMenuAction { get; set; }
        public Action HideMenuAction { get; set; }

        private async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            if (this.SlideMenu.IsShown)
            {
                HideMenuAction?.Invoke();
            }
            else
            {
                ShowMenuAction?.Invoke();
            }
        }
    }

RightSideMenuContentView ın xaml tarafına bir bakalım. Projemize bir adet ContentView ekleyip xaml tarafını aşağıdaki gibi düzenliyoruz.Basit bir StackLayout içerisinde bir kaç label var. Ama dikkat etmemiz gereken şey tüm sayfanın namespacinin SlideOverKit den geliyor olması.

<?xml version="1.0" encoding="utf-8" ?>
<t:SlideMenuView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                 xmlns:t="clr-namespace:SlideOverKit" 
                 x:Class="ConfinstaApp.Views.SlideViews.RightSideMenuContentView">

    <StackLayout Padding="15,50,15,30" Spacing="20" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

        <Label HeightRequest="30" Margin="0,0,0,80" Text="Menu" FontSize="20" XAlign="Center" TextDecorations="Underline" />

        <Label HeightRequest="30" Text="Insights" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline" />
        <Label HeightRequest="30" Text="Your Activity" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Nametag" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Saved" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Label HeightRequest="30" Text="Discover People" FontSize="20" XAlign="Start" YAlign="Center" TextDecorations="Underline"/>
        <Image HeightRequest="40" Source="nature.png"></Image>
        <Label HeightRequest="30" Text="Settings" FontSize="20" XAlign="Start" YAlign="Center" VerticalOptions="EndAndExpand" TextDecorations="Underline"/>

    </StackLayout>
</t:SlideMenuView>

Slide menünün backend tarafına bakalım. ContentView dan değil de SlideMenuView dan miras alıyor olmamız ilk dikkat çeken nokta. Bu miras ın bize sunduğu bir kaç property ile de nasıl görüneceğini ayarlıyoruz.

IsFullScreen true diyerek tam sayfa açılır olmasını söylüyoruz.

WidthRequest ile istediğimiz genişliği söylüyoruz.

MenuOrienations ı RightToLeft diyerek sağdan sola açılmasını belirtiyoruz.

[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class RightSideMenuContentView : SlideMenuView
    {
		public RightSideMenuContentView()
		{
			InitializeComponent ();
                        this.IsFullScreen = true;
                        this.WidthRequest = 250;
                        this.MenuOrientations = MenuOrientation.RightToLeft;

                        this.BackgroundColor = Color.White;
                        this.BackgroundViewColor = Color.Transparent;
                }
    }

Daha öncede de söylediğim gibi tüm projenin içerisinde sadece bir adet custom renderer var. o da bu plugin i kullanılabilir kulmak için. iOS Custom Renderer Kısmı aşağıdaki gibi.

[assembly: ExportRenderer(typeof(ProfileView), typeof(ProfileViewRenderer))]

namespace ConfinstaApp.iOS
{
    public class ProfileViewRenderer : PageRenderer, ISlideOverKitPageRendereriOS
    {
        public Action ViewDidAppearEvent { get; set; }

        public Action OnElementChangedEvent { get; set; }

        public Action ViewDidLayoutSubviewsEvent { get; set; }

        public Action ViewDidDisappearEvent { get; set; }

        public Action<CGSize, IUIViewControllerTransitionCoordinator> ViewWillTransitionToSizeEvent { get; set; }

        public ProfileViewRenderer()
        {
            new SlideOverKitiOSHandler().Init(this);
        }

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            OnElementChangedEvent?.Invoke(e);
        }

        public override void ViewDidLayoutSubviews()
        {
            base.ViewDidLayoutSubviews();
            ViewDidLayoutSubviewsEvent?.Invoke();

        }

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            ViewDidAppearEvent?.Invoke(animated);

        }

        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);
            ViewDidDisappearEvent?.Invoke(animated);
        }

        public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
        {
            base.ViewWillTransitionToSize(toSize, coordinator);
            ViewWillTransitionToSizeEvent?.Invoke(toSize, coordinator);
        }
    }
}

Custom renderer kullanmak zorunda olmanın yanında bir iyi haber ise şu. SlideOverKit sample larından aldığınız bir ios yada android rendererlarda sadece class isimlerini düzeltip başka bir customization yapmasanız bile proje çalışır hale gelecektir. Yani aslında tam da custom renderer yazıyor sayılmazsınız.

Android Custom Renderer kısmı

[assembly: ExportRenderer(typeof(ProfileView), typeof(ProfileViewRenderer))]
namespace ConfinstaApp.Droid
{

    public class ProfileViewRenderer : PageRenderer, ISlideOverKitPageRendererDroid
    {
        public Action OnElementChangedEvent { get; set; }

        public Action<bool, int, int, int, int> OnLayoutEvent { get; set; }

        public Action<int, int, int, int> OnSizeChangedEvent { get; set; }

        public ProfileViewRenderer (Context context) : base (context)
        {
            new SlideOverKitDroidHandler ().Init (this, context);
        }

        protected override void OnElementChanged (ElementChangedEventArgs e)
        {
            base.OnElementChanged (e);
            OnElementChangedEvent?.Invoke (e);
        }

        protected override void OnLayout (bool changed, int l, int t, int r, int b)
        {
            base.OnLayout (changed, l, t, r, b);
            OnLayoutEvent?.Invoke (changed, l, t, r, b);
        }

        protected override void OnSizeChanged (int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged (w, h, oldw, oldh);
            OnSizeChangedEvent?.Invoke (w, h, oldw, oldh);
        }
    }
}

Evet tüm hikaye bu kadar. .NetConf 2018 de sunumunu yapmış olduğum ve yaklaşık 1 iş günümü almış olan (sadece ui olduğu için :)) bir app in, xamarin forms ile ve xamarin forms a yeni gelen özellikler ile de nasıl bir şey ortaya çıkarabildiğini gördük.

Ekranları bir hatırlayalım.

Xamarin.Forms özelinde faydalı örneklerle dolu olduğunu düşündüğüm bu Confinsta app in proje haline buradan ulaşabilirsiniz. Aynı şekilde aynı gün yapmış olduğum diğer sunum dosyaları ve projeler burada mevcut.

Bir sonraki yazımda görüşmek üzere.

Xamarin.Forms ConfinstaApp Sample Bölüm 3

Selamlar,

Confinsta app in detaylarını incelemeye devam ediyoruz. Bu bölümde xamarin.forms un yeni gelen özellikleri ile de birlikte 1 gün gibi kısa bir sürede ne şekilde ui lar çıkartabileceğimizi örneklemeye çalıştığım Confinsta app de, kullanıcıların fotoğraflarının kimler tarafından beğenildiğini ve hangi fotoğraflarının beğenildiğini görecekleri ve kimlerin kendisini Follow ettiklerini görecekleri 2 tab lı sayfadan oluşan sayfamızı inceleyeceğiz.

Bu sayfa içerisinde kullandığım temel özellikler şöyle;

  • Syncfusion TabView
  • FlexLayout
  • ListView, GroupedListView
  • BindableSpan

gibi özellikler bulunuyor.

Sayfaların  görüntülerini hatırlayalım.

Sayfanın en temel özelliği, parmak ile sağa sola swipe edilerek tabların geçişlerini de sağlayan Syncfusion ın TabView ı üzerine kurulu olması. syncfusion ın ChartBar ları dışında en çok kullandığım paketi bu paket. Bir çok uygulamada buna benzer swipe ile geçişi istenen tablı yapılar mevcut. Özellikle iOS native tarafta bile bunu yapmak için ayrı cocoaPod ihtiyaç duyacakken böyle bir yapıyı custom rendererlar ile yapmak çok akıl karı değil. Bu yüzden kullanmaya kaçınsam da, bazı durumlarda paralı paketleri kullanmak gerekebiliyor, özellikle zamanınız dar ise 🙂

Sayfa iki tab a ayrılmış durumda. Soldaki tab da kullanıcıların sizin resimlerinizden beğendiklerini listeliyoruz. Sağda ise sizi takip edilenler gösteriliyor. Özellikle sol taraftaki her bir kullanıcının altında “sizin şu resimleriniz beğendi” ile beraber resimlerin dinamil geldiğini görsem ve FlexLayout olmasa yine sinir olurdum. Ama bir önceki yazımda gördüğümüz gibi FlexLayout ile bu tarz ui lar çok basit hale geliyor.

Gelelim sayfanın xaml tarafına. Sayfada ki iki farklı ekranı da aslında aynı xaml dosyası içinde tanımlayabiliyoruz. Aslında tabii bunları ayrı ContentView lar olarak tanımlayıp TabView ın olduğu sayfada referanslarınıda verebilirdik (ilk yazıda Tabbed page template inde kullandığımız yapı gibi).

Sayfanın xaml tarafı şu şekilde.

<?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:tabView="clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.XForms"
             xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
             x:Class="ConfinstaApp.Views.FeedActivityView">
    <ContentPage.Content>

        <tabView:SfTabView VisibleHeaderCount="2" TabHeaderBackgroundColor="White" x:Name="mainTabView" Margin="0,20,0,0">
            <tabView:SfTabItem Title="Following" TitleFontAttributes="Bold" TitleFontColor="Black" TitleFontSize="15">
                <tabView:SfTabItem.Content>
                    <StackLayout>
                        <ListView x:Name="FollowingFeedListView" 
                                  ItemsSource="{Binding FollowingItems}"
                                  SelectionMode="None"
                                  SeparatorVisibility="None"
                                  HasUnevenRows="true"
                                  RefreshCommand="{Binding LoadFollowingFeedListCommand}"
                                  IsPullToRefreshEnabled="true"
                                  IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                                  CachingStrategy="RecycleElement">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell Appearing="ViewCell_Appearing">
                                        <Grid RowSpacing="5" Padding="10">
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="50"></RowDefinition>
                                                <RowDefinition Height="Auto"></RowDefinition>
                                            </Grid.RowDefinitions>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="50"></ColumnDefinition>
                                                <ColumnDefinition Width="*"></ColumnDefinition>
                                            </Grid.ColumnDefinitions>

                                            <controls:CircleImage BorderThickness="2" 
                                                                  BorderColor="#ab423f" 
                                                                  WidthRequest="40"
                                                                  HeightRequest="40"
                                                                  Source="{Binding ProfileUrl}" 
                                                                  Aspect="AspectFit"
                                                                  Grid.Row="0"
                                                                  Grid.Column="0"></controls:CircleImage>

                                            <Label LineBreakMode="TailTruncation"
                                                   FontSize="Small"
                                                   Grid.Column="1"
                                                   Grid.Row="0"
                                                   VerticalOptions="Center"
                                                   TextColor="Black">
                                                <Label.FormattedText>
                                                    <FormattedString>
                                                        <Span Text="{Binding Name}" FontAttributes="Bold" FontSize="Small"></Span>
                                                        <Span Text=" liked some posts." TextColor="DarkGray" FontAttributes="None" FontSize="Small"></Span>
                                                    </FormattedString>
                                                </Label.FormattedText>
                                            </Label>

                                            <FlexLayout x:Name="imagesLayout" 
                                                        Wrap="Wrap" 
                                                        JustifyContent="Start"
                                                        Direction="Row"
                                                        Grid.Row="1"
                                                        Grid.Column="1"></FlexLayout>
                                        </Grid>
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </StackLayout>
                </tabView:SfTabItem.Content>
            </tabView:SfTabItem>
            <tabView:SfTabItem Title="You" FontIconFontAttributes="Bold" TitleFontColor="Black" TitleFontSize="15">
                <tabView:SfTabItem.Content>
                    <StackLayout>
                        <StackLayout>
                            <ListView x:Name="AboutYouFeedListView" 
                                  ItemsSource="{Binding GroupedItems}"
                                  SelectionMode="None"
                                  IsGroupingEnabled="True"
                                  GroupDisplayBinding="{Binding Key}"
                                  SeparatorVisibility="None"
                                  HasUnevenRows="true"
                                  RefreshCommand="{Binding LoadFollowingFeedListCommand}"
                                  IsPullToRefreshEnabled="true"
                                  IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                                  CachingStrategy="RecycleElement">
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <ViewCell Appearing="ViewCell_Appearing">
                                            <Grid RowSpacing="5" Padding="10">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="Auto"></RowDefinition>
                                                </Grid.RowDefinitions>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="40"></ColumnDefinition>
                                                    <ColumnDefinition Width="*"></ColumnDefinition>
                                                    <ColumnDefinition Width="100"></ColumnDefinition>
                                                </Grid.ColumnDefinitions>

                                                <controls:CircleImage BorderThickness="2" 
                                                                  BorderColor="#ab423f" 
                                                                  WidthRequest="30"
                                                                  HeightRequest="30"
                                                                  Source="{Binding ProfileUrl}" 
                                                                  Aspect="AspectFill"
                                                                  Grid.Column= "0"></controls:CircleImage>

                                                <Label LineBreakMode="TailTruncation"
                                                   FontSize="Small"
                                                   Grid.Column="1"
                                                   VerticalOptions="Center"
                                                   MaxLines="3"
                                                   TextColor="Black">
                                                    <Label.FormattedText>
                                                        <FormattedString>
                                                            <Span Text="{Binding Name}" FontAttributes="Bold" FontSize="Small"></Span>
                                                            <Span Text=" lstarted following you" TextColor="DarkGray" FontAttributes="None" FontSize="Small"></Span>
                                                        </FormattedString>
                                                    </Label.FormattedText>
                                                </Label>

                                                <Button BackgroundColor="#3893e8" 
                                                        TextColor="White" 
                                                        Text="Follow" 
                                                        CornerRadius="10" 
                                                        WidthRequest="80" 
                                                        HeightRequest="40"
                                                        Grid.Column="2"></Button>

                                            </Grid>
                                        </ViewCell>
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                        </StackLayout>
                    </StackLayout>
                </tabView:SfTabItem.Content>
            </tabView:SfTabItem>
        </tabView:SfTabView>
    </ContentPage.Content>
</ContentPage>

aslında ana yapı şu şekilde; Bir adet SFTabView. Bunun içerisine iki adet SfTabItem. Bunların içerisinde de yukarıda görmüş olduğunuz gibi sanki bir ContentPage in Content tag ının içini doldurur gibi sayfamızın xaml kodlarını dolduruyoruz.

        <tabView:SfTabView VisibleHeaderCount="2" TabHeaderBackgroundColor="White" x:Name="mainTabView" Margin="0,20,0,0">
            <tabView:SfTabItem Title="Following" TitleFontAttributes="Bold" TitleFontColor="Black" TitleFontSize="15">
                <tabView:SfTabItem.Content>

                </tabView:SfTabItem.Content>
            </tabView:SfTabItem>
            <tabView:SfTabItem Title="You" FontIconFontAttributes="Bold" TitleFontColor="Black" TitleFontSize="15">
                <tabView:SfTabItem.Content>
                    <StackLayout>

                </tabView:SfTabItem.Content>
            </tabView:SfTabItem>
        </tabView:SfTabView> 

Sayfanın backend tarafına bakığımızda durum aşağıdaki gibi. Sadece yine anasayfadakine benzer bir trick kullanıyoruz. sayfadaki ListView ın ViewCell_Appearing event i içerisinde, sayfaya bind etmiş olduğumuz model in içerisindeki, o anda ki satıra denk gelen model içerisinde ki fotoğraf sayısı kadar dönüp, bir image oluşturup flexlayou un Children property sine ekliyoruz. Gerisini resimde görmüş olduğunuz gibi kaç satır ve sütunda göstereceğine kendisi karar veriyor.

[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class FeedActivityView : ContentPage
	{
        FeedActivityViewModel viewModel;
		public FeedActivityView ()
		{
			InitializeComponent ();
            NavigationPage.SetHasNavigationBar(this, false);
            viewModel = new FeedActivityViewModel();
            BindingContext = viewModel;
		}

        private void ViewCell_Appearing(object sender, EventArgs e)
        {
            if (sender is ViewCell cell)
            {
                if (cell.BindingContext is FollowingItemModel viewModel)
                {
                    var flexLayout = cell.FindByName("imagesLayout");

                    if (flexLayout?.Children.Count == 0 && viewModel.IamgeUrls.Count > 0)
                    {
                        foreach (var url in viewModel.IamgeUrls)
                        {
                            flexLayout.Children.Add(new Image
                            {
                                Source = new UriImageSource
                                {
                                    Uri = new Uri(url),
                                    CacheValidity = TimeSpan.FromDays(1),
                                    CachingEnabled = true
                                },
                                WidthRequest = 40,
                                HeightRequest = 40,
                                Margin = new Thickness { Left = 5, Bottom = 0, Right = 0, Top = 5},
                                Aspect = Aspect.AspectFill
                            });
                        }
                    }
                }
            }
        }
    }

Sayfanın backendinde başka bir numara yok. Viewmodel e baktığımızda ise durum şöyle;

public class FeedActivityViewModel : BaseViewModel
    {
        public FeedActivityViewModel()
        {
            FollowingItems = new ObservableCollection();
            AboutYouItems = new ObservableCollection();
            LoadFollowingItems();
            LoadAboutYouItems();

            GroupedItems = AboutYouItems.GroupBy(x => x.GroupName);

            LoadFollowingFeedListCommand = new Command(() => { LoadMoreActivity(); });
        }

        private void LoadMoreActivity()
        {
            if (IsBusy)
                return;

            IsBusy = true;

            LoadFollowingItems();

            IsBusy = false;
        }

        private void LoadFollowingItems()
        {
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Ian Dooley",
                ProfileUrl = "https://randomuser.me/api/portraits/women/41.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
                "https://images.unsplash.com/photo-1539604880233-d282d9bac272?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=902aa0705d67ac390c0170c68aa4907f&auto=format&fit=crop&w=1051&q=80",
                "https://images.unsplash.com/photo-1539593608687-ccae798ff3ba?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=abbf25cb0a9e3f706263ff5fa81bf9d9&auto=format&fit=crop&w=1051&q=80" }
            });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Christian Becker",
                ProfileUrl = "https://randomuser.me/api/portraits/women/81.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539553521736-053bd7e14cf5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=debb88414bb2ee774ce6229a72adac17&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60" }
            });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Velizer Ivanov",
                ProfileUrl = "https://randomuser.me/api/portraits/women/69.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539572996946-c0665d491f04?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=3ed5d83abf5f65bfae42fec7c5e44dd2&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539550298564-8a06769aa728?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=5a3cdac18faf595762d48ac529233dd3&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            }
            });
            FollowingItems.Add(new FollowingItemModel { Name = "Sam Dawson", ProfileUrl = "https://randomuser.me/api/portraits/women/20.jpg", IamgeUrls = new List { "https://images.unsplash.com/photo-1539607547234-e09cdb14d473?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ccf5de3e1f25a87315d6201723d67d26&auto=format&fit=crop&w=500&q=600" } });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Simon King",
                ProfileUrl = "https://randomuser.me/api/portraits/women/57.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539602783210-221ffbec8280?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=4b5bf2be9796a2d7b9b005a7cbf28372&auto=format&fit=crop&w=500&q=60"
                , "https://images.unsplash.com/photo-1539602783210-221ffbec8280?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=4b5bf2be9796a2d7b9b005a7cbf28372&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1539602783210-221ffbec8280?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=4b5bf2be9796a2d7b9b005a7cbf28372&auto=format&fit=crop&w=500&q=60" }
            });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Ian Dooley",
                ProfileUrl = "https://randomuser.me/api/portraits/women/23.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539587310936-afda09bd0dc7?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=daf5f114d2545336bcab3e53bc4568e1&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539602010674-1346135ab34e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=eb91750f2ad63d82661fb76b0772d6fd&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539585173613-89e3967da7d5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=18ea6cc3988589adb75af4d8fe57d959&auto=format&fit=crop&w=500&q=60",
             "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60", "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60",
            "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
            "https://images.unsplash.com/photo-1539546978801-fbee5d0fe203?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=1f2df90670dbf90177e118ff434fd708&auto=format&fit=crop&w=500&q=60",}
            });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Christian Becker",
                ProfileUrl = "https://randomuser.me/api/portraits/women/41.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539608170043-f55d83afe1c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89a54fa339be3dde93fa137a213655c7&auto=format&fit=crop&w=1189&q=80",
                "https://images.unsplash.com/photo-1539604880233-d282d9bac272?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=902aa0705d67ac390c0170c68aa4907f&auto=format&fit=crop&w=1051&q=80",
                "https://images.unsplash.com/photo-1539593608687-ccae798ff3ba?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=abbf25cb0a9e3f706263ff5fa81bf9d9&auto=format&fit=crop&w=1051&q=80" }
            });
            FollowingItems.Add(new FollowingItemModel
            {
                Name = "Sam Dawson",
                ProfileUrl = "https://randomuser.me/api/portraits/women/81.jpg",
                IamgeUrls = new List { "https://images.unsplash.com/photo-1539553521736-053bd7e14cf5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=debb88414bb2ee774ce6229a72adac17&auto=format&fit=crop&w=500&q=60",
                "https://images.unsplash.com/photo-1539547018184-e5b1ce85fb07?ixlib=rb-0.3.5&s=1358c21c9ecdbbd65dd2993958ee4021&auto=format&fit=crop&w=500&q=60" }
            });
        }

        private void LoadAboutYouItems()
        {
            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Ian Dooley",
                ProfileUrl = "https://randomuser.me/api/portraits/women/41.jpg",
                GroupName = "Yesterday"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Christian Becker",
                ProfileUrl = "https://randomuser.me/api/portraits/women/81.jpg",
                GroupName = "Yesterday"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Velizer Ivanov",
                ProfileUrl = "https://randomuser.me/api/portraits/women/69.jpg",
                GroupName = "Yesterday"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Sam Dawson",
                ProfileUrl = "https://randomuser.me/api/portraits/women/20.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Simon King",
                ProfileUrl = "https://randomuser.me/api/portraits/women/57.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Ian Dooley",
                ProfileUrl = "https://randomuser.me/api/portraits/women/23.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Christian Becker",
                ProfileUrl = "https://randomuser.me/api/portraits/women/41.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Sam Dawson",
                ProfileUrl = "https://randomuser.me/api/portraits/women/81.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Simon King",
                ProfileUrl = "https://randomuser.me/api/portraits/women/57.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Ian Dooley",
                ProfileUrl = "https://randomuser.me/api/portraits/women/23.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Christian Becker",
                ProfileUrl = "https://randomuser.me/api/portraits/women/41.jpg",
                GroupName = "This Month"
            });

            AboutYouItems.Add(new FollowingItemModel
            {
                Name = "Sam Dawson",
                ProfileUrl = "https://randomuser.me/api/portraits/women/81.jpg",
                GroupName = "This Month"
            });
        }

        public ObservableCollection<FollowingItemModel> FollowingItems { get; set; }
        public ObservableCollection<FollowingItemModel> AboutYouItems { get; set; }

        public IEnumerable<IGrouping<string, FollowingItemModel>> GroupedItems { get;  set; }

        public ICommand LoadFollowingFeedListCommand { get; set; }
    }

    public class FollowingItemModel
    {
        public string GroupName { get; set; }
        public string Name { get; set; }
        public string ProfileUrl { get; set; }
        public List IamgeUrls { get; set; }
    }

FollowintItems ve AboutYouItems adında iki adet ObservableCollection ımız var. Bunların içerisi bize gruplu data gösterirken yardımcı olması adına oluşturduğumuz bir sınıf ile dolu. Bu sınıf FollowingItemModel . Yukarıdaki gibi porpertyleri var. Bu propertyleri listview grouplu bir şekilde kullanmak istediğimiz de ilgili alanlara bind etmemiz yeterli oluyor.

ItemsSource=”{Binding GroupedItems}”
IsGroupingEnabled=”True”
GroupDisplayBinding=”{Binding Key}”

gibi. Constructor tarafında da linq sorgusu ile

GroupedItems = AboutYouItems.GroupBy(x => x.GroupName);

diyerek follow tarafına basacağımız itemları gruplamış oluyoruz. bu şekilde karşımıza resimdeki gibi sectionlara ayrılmış bir listview çıkıyor.

Ek olarak sadece ListView refresh olduğunda çağırdımız bir command imiz var. Bu command de ObservableCollection larımızı dolduran metodlarımızı çağırıyor bu kadar.

Bir sonraki yazımda uygulamanın detayları için son kısımlara değineceğiz. Herhangi bir ekran daki açılan kapanan menüler istediğimizde bunu en kolay ve hızlı bir şekilde nasıl yaparız buna bakacağız.

Bir sonraki yazımda görüşmek üzere.

Xamarin.Forms ConfinstaApp Sample Bölüm 2

Selamlar,

Geçen yazımızda anlatmaya başladığım xamarin.forms ile kısa sürede ortaya çıkarabildiğim ve .NetConf da anlattığım Confinsta App in detaylarına devam ediyoruz.

En son uygulamanın temel navigasyon yapısını Tabbed template i ile nasıl oluşturuğumuzdan ve FeedView.xaml, .cs ve ViewModel tarafarından bahsetmiştik.

Bu yazımızda da xamarin yeni gelen özelliklerinden olan FlexLayout, SwipeGestureRecognizer gibi yeni özellikleri Instagramın Explore sayfasına benzer olarak bir sayfa yapmak için nasıl kullandığımıza bakalım.

Bu sayfada bir navigationbar bulunmuyor. Sayfa temel olarak 3 satırı olan bir Grid den oluşuyor. ilk satırında bir searchbar ve buton mevcut. İkinci satırında ise ana sayfadaki kulanıcı profil resimlerine benzer biraz daha eliptik imajlar üzerinden kategori isimlerinin olduğu bir scrollview var. üçüncü satır ise sadece bir FlexLayout tan oluşuyor. FlexLayout tan önce Native taraflardaki CollectionView görünümüne benzer olarak bir görüntü olutşturmak için GridView dinamil olarak satır ve sütunları kod tarafında oluşturup her bir içeriği onun ilgili satır ve sütununa basmak için uğraştırıcı böir yöntem kullanılıyorduk. FlexLayout da ise bir kaç property ile nasıl davramasını istediğinizi söylüyorsunuz ve içeriğini kod tarafta verdikten sonra gerisini o hallediyor. Yani FlexLayout un en güzel kullanım alanı bence, içeriği dinamil olarak değişecek olan UI larda ekranın yapısını ona bırakmak.

Bu ekranın xaml tarafı aşağıdaki gibi;

<?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:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
             x:Class="ConfinstaApp.Views.FeedCategoryView">
    
    <Grid Padding="0,20,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="70"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        
        <StackLayout Orientation="Horizontal" Grid.Row="0" HeightRequest="40" Padding="10" Spacing="10">
            <SearchBar Placeholder="Search" HorizontalOptions="FillAndExpand" WidthRequest="300" HeightRequest="30" BackgroundColor="Transparent"></SearchBar>
            <Image HorizontalOptions="EndAndExpand" Source="3ss.png" WidthRequest="20" HeightRequest="20"></Image>
        </StackLayout>

        <ScrollView Orientation="Horizontal" Grid.Row="1" HorizontalScrollBarVisibility="Never">
            <StackLayout Orientation="Horizontal">

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100"  HeightRequest="80" Source="music.jpg" Aspect="AspectFill"></controls:CircleImage>
                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Music" 
                           FontSize="Small" 
                           FontAttributes="Bold"
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="nature.jpg" Aspect="AspectFit"></controls:CircleImage>
                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Nature" 
                           FontSize="Small" 
                           FontAttributes="Bold"
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="science.jpg" Aspect="AspectFill"></controls:CircleImage>
                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Science" 
                           FontAttributes="Bold"
                           FontSize="Small" 
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="sports.jpg" Aspect="AspectFill"></controls:CircleImage>

                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Sports" 
                           FontAttributes="Bold"
                           FontSize="Small" 
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="animals.jpg" Aspect="AspectFill"></controls:CircleImage>

                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Animals" 
                           FontAttributes="Bold"
                           FontSize="Small" 
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="fitness.jpg" Aspect="AspectFill"></controls:CircleImage>

                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Fitness" 
                           FontAttributes="Bold"
                           FontSize="Small" 
                           TextColor="White"></Label>
                </Grid>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <controls:CircleImage Grid.Row="0" Grid.RowSpan="2" WidthRequest="100" HeightRequest="80" Source="comics.jpg" Aspect="AspectFill"></controls:CircleImage>

                    <Label Grid.Row="1"
                           HorizontalOptions="Center"
                           VerticalTextAlignment="Center"
                           Text="Comics" 
                           FontAttributes="Bold"
                           FontSize="Small" 
                           TextColor="White"></Label>
                </Grid>
            </StackLayout>
            
        </ScrollView>

        <ScrollView  Grid.Row="2">
            <FlexLayout x:Name="flexLayout"
                        Wrap="Wrap"
                        JustifyContent="SpaceEvenly" />
        </ScrollView>
    </Grid>

</ContentPage>

Xaml tarafında yukarıda bahsettiğim gibi, dikkat edeceğim en önemli yer Grid View ın son satırına attığımız ScrollView içerisine koyduğumuz FlexLayout.

Sadece iki tane özellikle FlexLayout üzerinde, ekranda eşit aralıklarla dizilmiş bir koleksiyon göstermek istediğimi söylüyoruz.

  • Wrap:  Wrap olarak set ederek, tek bir satıra sığmayan itemları eşit aralıklarla alt alta dizmesini istediğimizi söylüyoruz. Örneğim iphone 6 ekranında test ettiğimiz de ekrana 3 kolon olacak şekilde diziyor. Telefon yan çevirdiğimiz de ise aralıkları biraz daha arttırarak (ama eşit olacak şekilde)  kolon sayısını 5 e çıkartıyor
  • JustifyContent: SpaceEvenly diyerek de yukarıda bahsettiğimiz gibi aralıkların eşit olarak bölünmesini istediğimizi söylüyoruz.

sayfanın c# tarafında neler yaptığımıza bakalım. Burada yaptığımız şey aslında sadece viewmodel i initialize edip sayfanın BindingContext ine atadıktan sonra, model içerisinde olan PhotoList içerisinde dönüp her biri için istediğimiz UI ı kod tarafında oluşturmuş oluyoruz. Bunun için bir content view oluşturup UI orada xaml tarafında da oluşturabilirdik, bu örnekte ben kod tarafında oluşturmayı seçtim. Aslında bu kod tarafından ui oluşturma alışkanlığım doğru mu yanlış mı bilmem ama, native developer lar arasında halen tartışılmakta olan şeyler (ör; native ios da storyboard kullanılmalı mı kullanılmama lı gibi).

UI için sadece bir imaj oluşturduk Width ve Height değerleri fix 120. Buna göre FlexLayout hangi boyutta ekran karşısına gelirse gelsin, yanyana sığdırabildiği kadar sığdırıp gerisini farklı satırlara bölüyor.

Her bir imaja instagram da olduğu gibi uzun basıldığında popup olarak açılma ve yukarı slide edildiğinde ActionSheet gösterip kullanıcıya seçenekler sunma kısmını da yapabilmek için şunu yaptım.

Xamarin forms a yeni gelen SwiperGestureRecognizer ile yukarı parmak hareketini algılayıp resmi azıcık yukarı doğru kaydırıyorum ve ActionSheet açıyorum. Ama buradan sırayla bahsetmek gerekirse adımlar şöyle;

  • Xamarin forms da LongTapGestureRecognizer olmadığından dolayı ben burada iki tıkla zorunlu bir TapGestureRecognizer koydum. Çift tap yapıldığında pop içerisinde resmi açıyorum.
  • Popup için Rg.Plugins.Popup plugini ni kullandım. Açılan popup tasarımı ve kod behind ı ayrı dosyalarda. Resmi yukarı kaydırma ve popup ı komple animate etme kısmı bu tarafta bulunuyor.
[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class FeedCategoryView : ContentPage
    {
        FeedCategoryViewModel viewModel;
        public FeedCategoryView()
        {
            InitializeComponent();
            NavigationPage.SetHasNavigationBar(this, false);
            viewModel = new FeedCategoryViewModel();

            foreach (var item in viewModel.PhotoList)
            {
                var image = new Image()
                {
                    Source = new UriImageSource
                    {
                        Uri = new Uri(item.PhotoUrl),
                        CacheValidity = TimeSpan.FromHours(1),
                        CachingEnabled = true
                    },
                    WidthRequest = 120,
                    HeightRequest = 120,
                    Margin = new Thickness { Left = 0, Top = 3, Right = 0, Bottom = 0},
                    Aspect = Aspect.AspectFill,
                };

                image.GestureRecognizers.Add(new TapGestureRecognizer
                {
                    NumberOfTapsRequired = 2,
                    Command = new Command(async () =>
                    {
                        await Navigation.PushPopupAsync(new CategoryFeedPopupView(item));
                    })
                });

                flexLayout.Children.Add(image);
            }
        }
    }

Popup tarafına bakalım.

Eğer bu plugini daha önce hiç kullanmadıysanız hemen denemenizi tavsiye ederim. Popuplar la uğraşmak uygulama içerisindeki en kolay işlemlere dönüyor. Rg.Plugins.Popup nugetten indirip (hem standard hemde ios android projelerine de) uygulama spesifik taraflarda init etmeniz yeterli. Örneğin ios tarafına AppDelegate de aşağıdaki kodu LoadApplication dan önce yazmanız yeterli.

Rg.Plugins.Popup.Popup.Init();

sayfanın xaml tarafı aşağıdaki gibi. Birkaç farklı gelebilecek namespace var. Biri bu sayfanın bir popup sayfasına dair olduğu namespace, biri açılırken ki animasyon için olan diğerleride popup ın davranışlarını belirleyen özellikler

Örneğin;

CloseWhenBackgroundIsClicked=”False” diyerek popup ın arka kısmında ekranda bir yere basıldığına kapanmasın demiş oluyoruz. True dersek kapanır tıkladığımızda.

Tüm ekranı kenarları oval bir şekilde göstermek için Frame içine alıp BorderRadius özelliğini kullandım. Popup ın ekranın ortasında olması ve tüm ekranı kaplamaması içinde frame e VerticalOptions=”Center” dedim.

 <?xml version="1.0" encoding="UTF-8"?>
<pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                 xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup" 
                  x:Class="ConfinstaApp.Views.Popups.CategoryFeedPopupView"
                 xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup" 
                 xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
                 CloseWhenBackgroundIsClicked="False"
                 InputTransparent="False"
                 Padding="20,0,20,0">
    <pages:PopupPage.Animation>
        <animations:ScaleAnimation PositionIn="Center" PositionOut="Center" HasBackgroundAnimation="True" />
    </pages:PopupPage.Animation>

    <Frame BackgroundColor="#fff" VerticalOptions="Center" CornerRadius="15" x:Name="mainFrame">
        <Frame.GestureRecognizers>
            <SwipeGestureRecognizer Direction="Down" Swiped="SwipeGestureRecognizer_Swiped"></SwipeGestureRecognizer>
            <SwipeGestureRecognizer Direction="Up" Swiped="SwipeGestureRecognizer_Swiped"></SwipeGestureRecognizer>
        </Frame.GestureRecognizers>
        <StackLayout IsClippedToBounds="True" Spacing="0">
            <StackLayout Orientation="Horizontal" BackgroundColor="White" Padding="10" Spacing="0">
                <controls:CircleImage HorizontalOptions="StartAndExpand" Source="https://randomuser.me/api/portraits/women/72.jpg" Aspect="AspectFill" WidthRequest="50" HeightRequest="50"></controls:CircleImage>
                <Label FontSize="Large" TextColor="Black" FontAttributes="Bold" Text="Jojo Mayer" VerticalOptions="Center" HorizontalOptions="StartAndExpand"></Label>
            </StackLayout>
            <Image Source="{Binding PhotoUrl}" WidthRequest="300" HeightRequest="250"></Image>
        </StackLayout>
    </Frame>
</pages:PopupPage>

Dikkat ederseniz frame e xaml tarafında atanmış iki tane swipegesture var.

Up kısmında popup açıcaz, Down olarak set ettiğimizde ise popup ı kapayacağız.

Kod tarafı aşağıdaki gibi.İlk dikkat edeceğimiz şey sayfanın bir ContentPage sınıfından değil de PopupPage den türüyor olması. Sayfaya dışardan bir adet Photo sınıfı geçiyoruz bunu da BindingContext e set ediyoruz. Resim ve ismi bu şekilde xaml tarafına bind ediyoruz.

İkinci dikkat etmemiz gereken yer de, SwipeGesture ların aynı eventhandler ları kullanıyor olması. event e gelen EventArgument lardan Direction propertysi ile hangi gesture ile uğraşttığımızı anlayabiliyoruz.

Down ise sadece popup ı kapatıyoruz. Bu arada bu popup plugin inin Navigation üzerine yazılmış extension metodları mevcut. Yani normal xamarin.forms daki sayfa geçişlerinde Navigation sınıfını kullandığımız gibi popup lar içinde kullanabiliriz.

Örneğin popup ı kapatmak için;

await Navigation.PopPopupAsync(); diyebiliriz.

[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class CategoryFeedPopupView : PopupPage
	{
        Photos selecedPhoto;
		public CategoryFeedPopupView(Photos selectedPhoto)
		{
			InitializeComponent ();
            this.selecedPhoto = selectedPhoto;
            BindingContext = selecedPhoto;
		}

        private async void SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
        {
            if (e.Direction == SwipeDirection.Down)
            {
                await Navigation.PopPopupAsync();
            }
            else if(e.Direction == SwipeDirection.Up)
            {
                mainFrame.TranslateTo(0, -140, 300, Easing.CubicOut);
                var res = await DisplayActionSheet(string.Empty, "Cancel", "Share", "Like");

                if (!string.IsNullOrEmpty(res))
                {
                    mainFrame.TranslateTo(0, 0, 150, Easing.CubicOut);
                    await Navigation.PopPopupAsync();

                }
            }
        }
    }

Up olarak slide ettiğimiz de ise en dıştaki Frame imize verdiğimiz name ile ona kod tarafından ulaşıp,

mainFrame.TranslateTo(0, 0, 150, Easing.CubicOut);

diyerek yukarı kaydırıyoruz.

Genel olarak sayfamıza dönecek olursak, sayfada kullandığımız viewmodel ise aşağıdaki gibi. Hiçbir numara yok, sadece dummy bir Photo sınıfı listesi oluşturuyoruz ObservableCollection olarak.

public class FeedCategoryViewModel
    {
        public FeedCategoryViewModel()
        {
            PhotoList = new ObservableCollection();
            LoadDummData();
        }

        private void LoadDummData()
        {
            for (int i = 0; i < 10; i++)
            {
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539578741486-e0d3a45e16c2?ixlib=rb-0.3.5&s=05b39cf560d156b0e5f7408c0ca05ccb&auto=format&fit=crop&w=1900&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539578903083-0d9bb0ed39d4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=044382ada2f12060a3b4f28c120365a2&auto=format&fit=crop&w=634&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539604880233-d282d9bac272?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=902aa0705d67ac390c0170c68aa4907f&auto=format&fit=crop&w=1051&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539578839907-f463d05d7ad9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=d7a43a0fdf7e74bb78e3413341a2598e&auto=format&fit=crop&w=634&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539572408025-26bebcbb750e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=b3f3a1e1037df2e7adcf65ba2816d3db&auto=format&fit=crop&w=1050&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539609413529-1166774c3954?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=2c7b7da55abdaf0759143fbfc8e0b59b&auto=format&fit=crop&w=634&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539593758884-2e9d7b18e451?ixlib=rb-0.3.5&s=6e3b06927d46acb4071f7de4c8ba1a4c&auto=format&fit=crop&w=634&q=80" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539553281713-4f86b514e86f?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=33a4f691badb8c4b237727ef96c31414&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539535879069-3c148518f8c9?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=0e707059e2e9d16f94a682a189be8e42&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539553139747-e2ae5159d2e5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89e76f2a8cf3a81c8dba40f5dd01bb0f&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539576282236-40272d2dbe7e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=9cee2739772eb498885974c2f2542a83&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539541364455-3a385c64f82d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=503bce372f4b0071c629e5620a929bc1&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539601001507-887d25d5449e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=4ee2cba1be085263937478f5f71803fc&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539609301259-1dd126206e5e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=dcec9da5f439e073559530f679a1f23f&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539597583595-2069afd2b107?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=64b66dfa88a1b0f2ebffd20197857ed2&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539544048267-9744e3b84996?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=16959568ee70ac013296240aa5f7ced4&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539603584498-db314ea45182?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a62e0d1be0a73a5030616caabd8b7cc7&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539598735229-e5918f5408b5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=3a526577df19d1ec423b2220532475e7&auto=format&fit=crop&w=500&q=60" });
                PhotoList.Add(new Photos { PhotoUrl = "https://images.unsplash.com/photo-1539553521736-053bd7e14cf5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=debb88414bb2ee774ce6229a72adac17&auto=format&fit=crop&w=500&q=60" });
            }
           
        }

        public ObservableCollection PhotoList { get; set; }
    }

bu sayfanın da sonuna geldik, birçok şeyin kullanım şeklini görmşü olduk, diğer detaylarla Confinsta App ve xamarin forms yazılarına devam edeceğim.

Bir sonraki yazımda görüşmek üzere.

Xamarin Forms 3.0, 3.1, 3.2, 3.3 Yeni Gelen Özellikler

Selamlar

Geçen haftalarda Microsoft Türkiyede yapmış olduğumuz etkinliğin ikinci oturumunda anlatıığım ve göstermiş olduğum Xamarin.Forms a 3.0 – 3.3 arası yeni gelen ve hayatı çok daha kolaylaştıran özelliklerden bahsetmek istiyorum.

Xamarin.Forms 3.0 Özellikleri

Bu major versiyonla ile beraber hayatımıza yepyeni bir layout ve küçük ölçekte de olsa yeni güzellikler geldi. En temel yenilikleri aşağıdaki gibi inceleyebiliriz.

  • Visual State Manager
  • FlexLayout
  • StyleSheets
  • Right-To-Left Localization

Detaylı örneklere bakmadan önce diğer versiyonlarda hayatımıza neler girmiş bunlara bakalım. Ve hepsinin kullanıldığı bir uygulama ile örneklendirelim.

Xamarin.Forms 3.1 Özellikleri

Hayatımıza 3.1 versiyonu ile küçük gibi gözükse de birçok custom renderer dan bizi kurtaracak özellikler girdi Bunlar şöyle;

  • Android Bottom Tabs
  • Hide ScrollView scroll Bars
  • Bindable Span
  • Autoresizable Editor
  • Add SelectionMode Property To ListView

bunların dışında diğer detyalara buradan ulaşabilirsiniz.

Xamarin.Forms 3.2 Özellikleri

Burada en göe çarpan 2 özellik var bence. Birincisi Page Title View ını artık istediğimiz gibi edign edebiliyor olmamız. Diğeri SwipeGestureRecognizer.

  • SwipeGestureRecognizer
  • Rounded Corners on BoxView
  • Padding on Buttons
  • Page TitleView

yine çoğu zaman custom rendererlar ile yazmak durumunda kaldığımız birçok şeyden kurtulmuş durumdayız.

Son olarak da 3.3 ile hayatımıza neler girdi buna bakalım.

Xamarin.Forms 3.3 Özellikleri

Bu versiyonda, birçok etkinlikte duyduğum, xamarine başlarken garip bir şekilde insanların uzaklaşmasına sebep olan Label ve özelliklerine abanmışlar gibi. Aşağıda yeni özellikleri listeliyorum.

  • Max Lines on Label
  • Label with Wrap/LineNumber and Truncation
  • Label underline/strikethrough support
  • Use WKWebView on iOS

Bu özelliklerin çoğunu kullandığımı bir demo örneği ve kodları üzerinden gidelim istiyorum. Meetupda göstermiş olduğum ve insanların oldukça hoşuna gitmiş olan çok yaratıcı örneğimin adı Confinsta(bir instagram çakması arayüz) idi. Instagramın arayüzünün birçok ekranını xamarin.Forms ve yeni gelen özellikleri ile nasıl oluşturduğumu göstermek için bu örneği hazırlamıştım.

ÖRneği hazırlamak yaklaşık olarak 1 iş günümü aldı.

Arayüz ler şu şekilde.

5 temel sayfa üzerinde uğraştım. Kodların neler olduğunu bakacağız zaten ama özetlemem gerekirse, bu sayfalar ve yeni gelen xamarin forms özelliklerinden, yardımcı olacak pluginler den kullandıklarım şunlar;

  1. Ana Sayfa: Burada şimdiye kadar kullandıklarınızdan farklı özel bir şey yok. Sadece yardımcı birkaç plugin var. Bunlar;
    1. ImageCircle
    2. MediaManager
    3. CarouselView
    4. Page TitleView
  2. Explore Sayfası: Burada zaman kazandıran en güzel yeni özelliğimiz FlexLayout
    1. Rg.Plugins.Popup
    2. FlexLayout
    3. SwipeGestureRecognizer
  3. Like Sayfası: Burada tab geçişlerinin yukarıda ve swipe lı olabilmesi için syncfusion ın TabView ını kullandım.
    1. Syncfusion.XForms.TabView
    2. ImageCircle
    3. FlexLayout
    4. Bindable Span
  4. Follow Sayfası: Basit bir listview. Sycnffusion TabView ının ikinci tabı oluyor  zaten bu sayfa.
    1. Bindable Span
  5. Profile: Bu sayfanın da tasarımında çok sağdan açılan hamburger menu dışında bir özellik olmadığından sadece o kısmı ekledim.
    1. SlideOverKit

exp1exp2exp3follow1follow2likemain1main2pro1pro2

 

Tüm resimlerin yanında yukarı birde uygulamanın akışını görebilmeniz için kısa bir video koydum. Unutmayın yalnızca 1 iş günü ayrılmış bu ekran prototipleri bence gayet güzel gözüküyor. Xamarin.Forms ile yapabileceklerinizin gücünü anlamamız için yeterli bence.

Bir sonraki yazımda her bir sayfanın kod taraflarında detaylarına girip, hem xamarin in yeni özelliklerini göreceğiz hem de varolan özelliklerinden nasıl faydalanmışız bunlara bakacağız.

Görüşmek üzere.

Azure Machine Learning Studio ile Machine Learning’e Giriş

Selamlar,

20 Ekim Cumartesi günü Microsoft Türkiye’de gerçekleştirmiş olduğumuz .NetKonf Türkiye etkinliğinde bir çok değerli konuya değindik. İki farklı sunumda iki farklı konu paylaştım gelen arkadaşlarımızla. Çok keyifli geçen bu etkinliğin ardından anlattıklarımı buraya da daha detaylı olarak yazmak istedim.

Yaptığım iki sunum şunlardı;

  • Machine Learning with Azure Machıne Learnıng Studio
  • Xamarin forms 3.0-1-2-3 features and samples

Bu iki konudan ilkine bakalım bu yazımızda. Bugünlerde artık çokça Machine Learning, AI, DeepLearning vs vs terimlerini duymaya başladığınıza eminim. Birçok ürün herhangi bir reklamında bu terimlerle yer vermeden geçmiyorlar. Peki bizde bir data scientist olmadan kendi projelerimizde ML çözümleri uygulayamaz mıyız?

Tabii ki uygulayabiliriz. Bilgi düzeyimiz bir DataScientist insanının matematik, istatistik ve veri analizi kadar olmasa bile, şunu söyleyebilirim ki, elimizde yeterli derece de analiz edip tahminler yürütmek istediğimiz veriler var ise, kendi uygulamamıza da bu ML çözümlerini bir özellik katmanın özellikle Azure Machine Learning ile ne kadar olduğunu görünce çok şaşıracaksınız.

Tabii hiç bilmeden de olmaz değilmi, bir AI uzmanı olmasak da, öncelikle bu yazımızda bu Machine Learning denen şey nedir, ne değildir bundan bahsedelim. Sonra Azure un bize sağladığı kolaylıkları ve çözümleri görelim.

Machine Learning tam olarak şudur ;

Capture

:). Komik gelebilir, ama akademik camia da insanlar senelerdir matematik ve istatistik bilimi üzerinde çalışarak birçok algoritma geliştirdiler zaten. Bu ML in son zamanlarda çok popüler olmasının temel 3 sebebi var;

  1. Artık çok sayıda denemeler yapıp sonuçlar elde edeceğimiz algoritmalar oluşmuş ve bunlar denenmiş sonuç alınmış durumda
  2. Artık bilgisayarlar eskisinden çok daha güçlü, bu algoritmalar, belli problemler üzerinde çalıştırılarak çeşitli simülasyon ve analizler yapmak çok daha masrafsız hale gelmiş olmakta.
  3. Artık çok çok fazla data birikmiş durumda. Her konuda etrafımızda tonlarca data mevcut diyebiliriz.

Bu 3 temel madde bir araya geldi mi artık MachineLearning konuşmakta kaçınılmaz oluyor.

Peki yazının başında bahsettiğim gibi, Machine Learning ile beraber duymaya başlamış olduğunuz daha başka terimler de mevcuttur tahminimce

bunlar;

  • Artificial Neural Networks
  • Deep Learning
  • Bayesian Networks
  • Artificial Intelligence

gibi bu liste uzar bile. Peki bunlar nerede çakışır? çakışır mı? Daha büyük resme baktığımız zaman ne nereye oturur buna bakalım.

Capture.PNG

Yukarıdaki resimde kısaca özetlenmiş bir hali mevcut. Aslında bakarsanız özet şu;

  • DeepLearning, Artificial N. Networks, vs vs hepsi özelinde Machine Learning kullanan ve farklı algoritmalar ile farklı sorunlara farklı yaklaşımlar getiren birer özel çözümlerdir. Ve tüm bu çözümler kendi içerisinde Machine Learning kullanırken, nihai amaçları ise, herşeyin özel çözümleri ile sonuçlanabilir olduğu bir Yapay Zeka (Artificial Intelligence) üretme çabasıdır.

Peki, madem artık biliyoruz ki tüm duyduğumuz terimler içerisinde Machine Learning daha da core bir seviyede kullanılıyor. O zaman şunu anlamaya çalışalım. Machine Learning ne demek?

Aslında biraz süslü bu tanımın arkasında yatan şey, insan gözüyle ve hızıyla bakıp çıkarımlar yapmamızın çok uzun süreceği şeyleri, bilgisayara aktarıp onun yapmasını sağlamak.

Capture.PNG

Yukarıda ki resme bir bakalım. Elimizde olan bu kredi kartı satışlarının fraud(sahte-hileli) olup olmama durumunu içeren kayıtlar sadece bu kadar olsa, bu datalara bakıp bir süre sonra birtakım çıkarımlarda bulunabilirdik değil mi?

Örneğin;

  • 20 ile 30 yaşları arasında, Amerika da verilmiş, Rusyada kullanılmış ve 1000$ üzerinde olan işlemlerde fraud olma riski yüksek diyebilirdik.

Ama bu datanın milyonlarca kayıttan oluştuğunu düşünün. Üzerine bir de bu kadar az kolon yerine onlarca kolondan oluşan farklı detaylar içeren kayıtlar olduğunu düşünün.

Bu durumda bir yada onlarca insanın bu datalara bakıp çıkarımlar yapması yerine, yıllardır geliştirilmiş algoritmaları ve günümüzün güçlü bilgisayarlarını kullanıp bu işi onlara bıraksak daha kolay olmaz ve kısa sürmez mi? Şüphesiz. İşte tam da bu işi bilgisayarlara bırakalım o zaman dediğimiz noktada Machine Learning devreye girmiş oluyor.

Peki bir de büyük resimde bu Machine Learning nasıl işliyor buna bakalım.

Capture.PNG

Aslında en üstten baktığımızda tüm süreç bu kadar desek yanlış olmaz 🙂 tabii her zaman olduğu gibi detaylara indikçe boğulmalar olacaktır.  Ama özetle olay şu;

  • Elimizde bir tek bir kaynaktan yada farklı kaynaklarda olan takım datalar var. Bunları bir araya getirip incelemek istediğimiz tek bir data set i olarak birleştirelim. (Burası başlı başına olay zaten, sadece bunun için bile bir ton tool var)
  • Daha sonra bizim yukarıdaki örnekte, insan gözüyle bir kaç saniyede yaptığımız gibi, bazı benzerlikler bulmaya çalışalım bu data içerisinde. Bunu yaparken de var olan onlarca data algoritmayı deneyerek başlayabiliriz.
  • Artık bir benzerlik bulduğumuzu düşündüğümüz anda, bunu bir model olarak benimseyelim ve bundan sonraki datalara bakarken dataların bu modele e ne kadar uyup uymadıklarına bakalım.

Tüm süreç dediğimiz gibi aslında en dışarıdan bakıldığında bu kadar. Ama bu sürecin kendisi tamamen kendini tekrar eden bir süreç. Bunu unutmamak lazım. Yani bir Machine Learning Projesi, hiçbir zaman tam olarak bitti diyemezsiniz, çünkü yeni veri akışı oldukça, sizin data setleriniz değiştikçe, uyguladığınız algoritmalar, bulduğunuz benzerlikler ve nihayetinde ortaya çıkan modeller değiştikçe tum bu yukardıdaki resimdeki süreç aslında başa sarıp sarıp duracaktır. Yani sürecin kendisi tamamen iterative – kendini tekrar eden- bir süreçtir.

Günümüzde artık İş adamlarıi yazılımcılar, datascientist ler vs neredeyse herkes bu ML işine, işinin bir kısmında bir takım sorunlarına çözüm bulmak için ihtiyaç duyuyor.

Bu kadar çok adı geçmiş ken bir de şuna bakalım. Kim bu data scientist?

Data Scientist olmanız için 3 temel şart vardır diyebiliriz.

  • İstatistik ve haliyle Matematik Bilgisi
  • Programlama Bilgisi
  • Belli bi Domain de (örn; bankacılık) iş anlamında yüksek derecede bilgi.

ilk iki madde olsa bile kendinize ben her konuda data scientist im demek, bir api yazıp, html editleyip, kendinize ben fullstack developer ım demek gibi olabilir. Çünkü veriyi yorumlayacığınız alanda bilgi sahibi olmak size, hangi algoritmaları seçmeniz gerektğinden hangi data setleri ile çalışmanız gerektiğine kadar birçok alanda fayda sağlayacaktır.

Peki, yazının başında dediğimiz gibi bir data Scientist olmadan da ML çözümleri üretemez miyiz üretebiliriz. Cloud ortamlarda bunu bize servis olarak sunan büyük küçük birçok vendor var.

Capture.PNG

Yukarıda gördüğünüz gibi Clouda olarak bu hizmeti veren en büyük iki firma Microsoft ve Amazon. Biz bunlarda Azure üzerinde neler yapabileceğimize bakacağız.

Bir ML projesinin temel düzeyde ne aşamalardan oluştuğunu gördüğümüze göre Azure tarafında büyük resimde işler nasıl işliyor buna bakalım.

Capture.PNG

Birçok farklı kaynaktan datanızı Machine Learning Studio üzerinde sürükle bırak (evet yanlış okumadınız baya sürükle bırak=)) yöntemiyle belli algoritmalar üzerinden geçirip, ortaya bir iki tıkla bir web service çıkartıyorsunuz. Ve bu servisi Web- mobil, yada diğer ihtiyaç duyduğunuz yerlerde kullanmaya başlıyorsunuz.

Bu yazıyı burada noktalıyorum. Bir sonraki yazımda Machine Learning in biraz da terimsel taraflarına bakıp. Azure tarafına giriş yapalım.

Bir sonraki yazımda görüşmek üzere.