Add RecycleView to MainView
authorLukasz Stanislawski <l.stanislaws@samsung.com>
Fri, 30 Oct 2020 10:43:01 +0000 (11:43 +0100)
committerPiotr Czaja <p.czaja@samsung.com>
Tue, 14 Sep 2021 11:01:34 +0000 (13:01 +0200)
Fitness/Controls/BindableRecyclerView.cs [new file with mode: 0644]
Fitness/ViewModels/MainViewModel.cs
Fitness/ViewModels/WorkoutViewModel.cs
Fitness/Views/FitnessItemView.cs [new file with mode: 0644]
Fitness/Views/FitnessItemViewRecycleAdapter.cs [new file with mode: 0644]
Fitness/Views/MainView.xaml.cs
Fitness/res/layout/FitnessItemView.xaml [new file with mode: 0644]
Fitness/res/layout/MainView.xaml

diff --git a/Fitness/Controls/BindableRecyclerView.cs b/Fitness/Controls/BindableRecyclerView.cs
new file mode 100644 (file)
index 0000000..1d3f61b
--- /dev/null
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using Fitness.Services;
+using Tizen.NUI;
+using Tizen.NUI.Binding;
+using Tizen.NUI.Components;
+
+namespace Fitness.Controls
+{
+    public class BindableRecyclerView : RecyclerView
+    {
+        public static readonly BindableProperty ItemsSourceProperty =
+            BindableProperty.Create(nameof(ItemsSource), typeof(List<object>), typeof(BindableRecyclerView), null, propertyChanged: OnItemsSourceChanged);
+
+        public static readonly BindableProperty SelectedItemProperty =
+            BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(BindableRecyclerView), null, propertyChanged: OnSelectedItemChanged);
+
+        public BindableRecyclerView()
+            : base(new RecycleAdapter(), new LinearRecycleLayoutManager())
+        {
+            // TODO workaround orientation set bug
+            LayoutManager.LayoutOrientation = RecycleLayoutManager.Orientation.Horizontal;
+        }
+
+        public List<object> ItemsSource
+        {
+            get => (List<object>)GetValue(ItemsSourceProperty);
+            set
+            {
+                SetValue(ItemsSourceProperty, value);
+            }
+        }
+
+        public object SelectedItem
+        {
+            get => (object)GetValue(SelectedItemProperty);
+            set
+            {
+                SetValue(SelectedItemProperty, value);
+            }
+        }
+
+        public static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
+        {
+            var recycler = bindable as BindableRecyclerView;
+            if (recycler != null && recycler.Adapter != null)
+            {
+                var newList = newValue as List<object>;
+                recycler.Adapter.Data = newList;
+
+                // Set selected item by default on first item
+                if (newList.Count > 0)
+                {
+                    recycler.SelectedItem = newList[0];
+                }
+            }
+        }
+
+        public static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
+        {
+            var recycler = bindable as BindableRecyclerView;
+            if (recycler != null && recycler.Adapter != null)
+            {
+                recycler.SelectedItem = newValue;
+            }
+        }
+    }
+}
index a0c9ff5..f6a46f1 100644 (file)
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Windows.Input;
 using Fitness.Models;
 using Fitness.Services;
 using Tizen.Applications;
@@ -10,8 +9,6 @@ namespace Fitness.ViewModels
 {
     public class MainViewModel : BaseViewModel
     {
-        private WorkoutViewModel selectedWorkout;
-
         public MainViewModel()
         {
             Workouts = new List<object>(new[]
@@ -137,41 +134,11 @@ namespace Fitness.ViewModels
                 ThumbnailUrl = Application.Current.DirectoryInfo.Resource + "media/video-0002.jpeg",
               },
             });
-
-            WatchPreview = new Command(() => { NavigationService.Instance.NavigateToExercisePreviewView(); });
-            StartWorkout = new Command(() => { NavigationService.Instance.NavigateToExercisingView(); });
-            SelectedWorkout = Workouts[0] as WorkoutViewModel;
         }
 
         /// <summary>
         /// List of all available workouts
         /// </summary>
         public List<object> Workouts { get; private set; }
-
-        /// <summary>
-        /// Currently selected workout
-        /// </summary>
-        public WorkoutViewModel SelectedWorkout
-        {
-            get => selectedWorkout;
-            set
-            {
-                if (value != selectedWorkout)
-                {
-                    selectedWorkout = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Stop workout
-        /// </summary>
-        public ICommand WatchPreview { get; private set; }
-
-        /// <summary>
-        /// Start selected workout.
-        /// </summary>
-        public ICommand StartWorkout { get; private set; }
     }
 }
index 5b8c9c9..48c0f5d 100644 (file)
@@ -1,15 +1,23 @@
 using System;
 using System.Windows.Input;
 using Fitness.Models;
+using Fitness.Services;
 using Fitness.ViewModels;
+using Tizen.NUI.Binding;
 
-namespace Fitness.Models
+namespace Fitness.ViewModels
 {
     /// <summary>
     /// WorkoutViewModel's information.
     /// </summary>
     public class WorkoutViewModel : BaseViewModel
     {
+        public WorkoutViewModel()
+        {
+            WatchPreview = new Command(() => { NavigationService.Instance.NavigateToExercisePreviewView(); });
+            StartWorkout = new Command(() => { NavigationService.Instance.NavigateToScanningView(); });
+        }
+
         /// <summary>
         /// Current score
         /// </summary>
@@ -59,5 +67,15 @@ namespace Fitness.Models
         /// Url to workout video
         /// </summary>
         public string VideoUrl { get; set; }
+
+        /// <summary>
+        /// Stop workout
+        /// </summary>
+        public ICommand WatchPreview { get; private set; }
+
+        /// <summary>
+        /// Start selected workout.
+        /// </summary>
+        public ICommand StartWorkout { get; private set; }
     }
 }
diff --git a/Fitness/Views/FitnessItemView.cs b/Fitness/Views/FitnessItemView.cs
new file mode 100644 (file)
index 0000000..b4689c6
--- /dev/null
@@ -0,0 +1,15 @@
+using Fitness.Services;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+namespace Fitness.Views
+{
+    public partial class FitnessItemView : RecycleItem
+    {
+        public FitnessItemView()
+        {
+            InitializeComponent();
+        }
+    }
+}
diff --git a/Fitness/Views/FitnessItemViewRecycleAdapter.cs b/Fitness/Views/FitnessItemViewRecycleAdapter.cs
new file mode 100644 (file)
index 0000000..be7f200
--- /dev/null
@@ -0,0 +1,29 @@
+using Fitness.Services;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+namespace Fitness.Views
+{
+    public class FitnessItemViewRecycleAdapter : RecycleAdapter
+    {
+        public FitnessItemViewRecycleAdapter()
+        {
+        }
+
+        public override RecycleItem CreateRecycleItem()
+        {
+            return new FitnessItemView();
+        }
+
+        public override void BindData(RecycleItem item)
+        {
+            item.BindingContext = Data[item.DataIndex];
+            if (item is FitnessItemView fitnessItem)
+            {
+                fitnessItem.image.BindingContext = item.BindingContext;
+                fitnessItem.label.BindingContext = item.BindingContext;
+                fitnessItem.favourite.BindingContext = item.BindingContext;
+            }
+        }
+    }
+}
index 2a7fc33..b10ed98 100644 (file)
@@ -1,6 +1,7 @@
 using Fitness.Controls;
 using Tizen.NUI;
 using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
 
 namespace Fitness.Views
 {
@@ -12,6 +13,7 @@ namespace Fitness.Views
 
             this.button1.ApplyStyle(Styles.Buttons.Inverse);
             this.button2.ApplyStyle(Styles.Buttons.Regular);
+            this.scroller.ScrollingDirection = ScrollableBase.Direction.Horizontal;
         }
     }
 }
diff --git a/Fitness/res/layout/FitnessItemView.xaml b/Fitness/res/layout/FitnessItemView.xaml
new file mode 100644 (file)
index 0000000..9106d9b
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<nui:RecycleItem x:Class="Fitness.Views.FitnessItemView"
+  xmlns="http://tizen.org/Tizen.NUI/2018/XAML"
+  xmlns:nui="clr-namespace:Tizen.NUI.Components"
+  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+  xmlns:views="clr-namespace:Fitness.Views"
+  xmlns:converters="clr-namespace:Fitness.Views.Converters"
+  Size="{views:SizeInUnits Width=82,Height=44}">
+  <View Size="{views:SizeInUnits Width=78,Height=44}" >
+         <ImageView x:Name="image" HeightResizePolicy="FillToParent" WidthResizePolicy="FillToParent" ResourceUrl="{Binding ThumbnailUrl}"/>
+         <TextLabel x:Name="label" PositionUsesPivotPoint="true" PivotPoint="0.0,1.0" ParentOrigin="0.0,1.0" PointSize="24" Text="{Binding Title}"/>
+         <ImageView x:Name="favourite" Size="30,30" PositionUsesPivotPoint="true" PivotPoint="1.0,0.0" ParentOrigin="1.0,0.0" ResourceUrl="{Binding Favourite, Converter={StaticResource favouriteToIconConverter}}"/>
+  </View>
+</nui:RecycleItem>
index 755da91..fef3fa4 100644 (file)
       <converters:FavouriteToIconConverter x:Key="favouriteToIconConverter"/>
   </View.XamlResources>
   <View.BindingContext>
-    <vm:MainViewModel x:Name="mainViewModel">
-    </vm:MainViewModel>
+      <vm:MainViewModel/>
   </View.BindingContext>
   <View.Layout>
       <LinearLayout LinearOrientation="Vertical" LinearAlignment="Top"/>
   </View.Layout>
-  <View BindingContext="{x:Reference mainViewModel}" Margin="{views:ExtentsInUnits Start=16,End=16}" Weight="1.0" WidthResizePolicy="FillToParent" >
+  <View Margin="{views:ExtentsInUnits Start=16,End=16}" Weight="1.0" WidthResizePolicy="FillToParent" >
       <View.Layout>
           <LinearLayout LinearOrientation="Horizontal" LinearAlignment="End"/>
       </View.Layout>
-      <ImageView BindingContext="{Binding SelectedWorkout}" Weight="1.0" HeightResizePolicy="FillToParent" ResourceUrl="{Binding ThumbnailUrl}">
+      <ImageView BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" Weight="1.0" HeightResizePolicy="FillToParent" ResourceUrl="{Binding ThumbnailUrl}">
           <ImageView.Layout>
               <LinearLayout LinearOrientation="Vertical" LinearAlignment="Center"/>
           </ImageView.Layout>
-          <nui:Button BindingContext="{x:Reference mainViewModel}" BackgroundColor="Red" PositionUsesPivotPoint="true" ParentOrigin="0.5,0.5" PivotPoint="0.5,0.5" Text="Let's try!" Command="{Binding StartWorkout}" x:Name="button1" Size="{views:SizeInUnits Width=112,Height=24}"/>
-          <nui:Button BindingContext="{x:Reference mainViewModel}" PositionUsesPivotPoint="true" ParentOrigin="0.5,0.5" PivotPoint="0.5,0.5" Text="Watch preview" Command="{Binding WatchPreview}" x:Name="button2" Size="{views:SizeInUnits Width=112,Height=24}"/>
+          <nui:Button BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" PositionUsesPivotPoint="true" ParentOrigin="0.5,0.5" PivotPoint="0.5,0.5" Text="Let's try!" Command="{Binding StartWorkout}" x:Name="button1" Size="{views:SizeInUnits Width=112,Height=24}"/>
+          <nui:Button BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" PositionUsesPivotPoint="true" ParentOrigin="0.5,0.5" PivotPoint="0.5,0.5" Text="Watch preview" Command="{Binding WatchPreview}" x:Name="button2" Size="{views:SizeInUnits Width=112,Height=24}"/>
       </ImageView>
-      <View Size="{views:SizeInUnits Width=107}" Margin="{views:ExtentsInUnits Start=8}" HeightResizePolicy="FillToParent" BindingContext="{x:Reference mainViewModel}">
-          <View HeightResizePolicy="FillToParent" WidthResizePolicy="FillToParent" BindingContext="{x:Reference mainViewModel}">
+      <View Size="{views:SizeInUnits Width=107}" Margin="{views:ExtentsInUnits Start=8}" HeightResizePolicy="FillToParent">
+          <View HeightResizePolicy="FillToParent" WidthResizePolicy="FillToParent">
               <View.Layout>
                   <LinearLayout LinearOrientation="Vertical" LinearAlignment="Begin"/>
               </View.Layout>
-              <View BindingContext="{x:Reference mainViewModel}">
+              <View>
                   <View.Layout>
                       <LinearLayout LinearOrientation="Horizontal" LinearAlignment="Begin"/>
                   </View.Layout>
-                  <TextLabel BindingContext="{Binding SelectedWorkout}" Text="{Binding Path=Title}" PointSize="40" VerticalAlignment="Center" TextColor="#000C2B"/>
-                  <ImageView BindingContext="{Binding SelectedWorkout}" Size="36,48" Margin="30,30,0,0" ResourceUrl="{Binding Difficulty, Converter={StaticResource difficultyLevelConverter}}"/>
+                  <TextLabel BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" Text="{Binding Path=Title}" PointSize="40" VerticalAlignment="Center" TextColor="#000C2B"/>
+                  <ImageView BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" Size="36,48" Margin="30,30,0,0" ResourceUrl="{Binding Difficulty, Converter={StaticResource difficultyLevelConverter}}"/>
               </View>
-              <View BindingContext="{x:Reference mainViewModel}">
+              <View>
                   <View.Layout>
                       <LinearLayout LinearOrientation="Horizontal" LinearAlignment="Begin"/>
                   </View.Layout>
                   <ImageView Size="28,28" ResourceUrl="*Resource*/layout/images/icon_time.png"/>
-                  <TextLabel BindingContext="{Binding SelectedWorkout}" Text="{Binding Duration,StringFormat=\{0:h\\:mm\\:ss\}}" PointSize="28" Margin="8,0,0,0" VerticalAlignment="Center" TextColor="#000C2B"/>
+                  <TextLabel BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" Text="{Binding Duration,StringFormat=\{0:h\\:mm\\:ss\}}" PointSize="28" Margin="8,0,0,0" VerticalAlignment="Center" TextColor="#000C2B"/>
               </View>
-              <TextLabel BindingContext="{Binding SelectedWorkout}" Size="{views:SizeInUnits Width=107}" Text="{Binding Description}" PointSize="28" LineWrapMode="Word" Weight="1.0" VerticalAlignment="Bottom" Ellipsis="false" MultiLine="true" TextColor="#000C2B" EnableAutoScroll="true"/>
+              <TextLabel BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" Size="{views:SizeInUnits Width=107}" Text="{Binding Description}" PointSize="28" LineWrapMode="Word" Weight="1.0" VerticalAlignment="Bottom" Ellipsis="false" MultiLine="true" TextColor="#000C2B" EnableAutoScroll="true"/>
           </View>
-          <ImageView PositionUsesPivotPoint="true" PivotPoint="1.0,0.0" ParentOrigin="1.0, 0.0" Size="30,30" BindingContext="{Binding SelectedWorkout}" ResourceUrl="{Binding Favourite, Converter={StaticResource favouriteToIconConverter}}"/>
+          <ImageView PositionUsesPivotPoint="true" PivotPoint="1.0,0.0" ParentOrigin="1.0, 0.0" Size="30,30" BindingContext="{Binding Source={x:Reference scroller}, Path=SelectedItem}" ResourceUrl="{Binding Favourite, Converter={StaticResource favouriteToIconConverter}}"/>
       </View>
   </View>
-  <View Size="{views:SizeInUnits Height=44}" Margin="{views:ExtentsInUnits Top=10, Bottom=10}" WidthResizePolicy="FillToParent">
-  </View>
+  <ctrl:BindableRecyclerView Size="{views:SizeInUnits Height=44}" Margin="{views:ExtentsInUnits Top=10, Bottom=10}" WidthResizePolicy="FillToParent" ItemsSource="{Binding Workouts}" x:Name="scroller" Padding="{views:ExtentsInUnits Start=16, End=16}">
+      <ctrl:BindableRecyclerView.Adapter>
+          <views:FitnessItemViewRecycleAdapter/>
+      </ctrl:BindableRecyclerView.Adapter>
+  </ctrl:BindableRecyclerView>
 </ctrl:Page>