Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / RecyclerView / RecyclerView.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI.BaseComponents;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20
21 namespace Tizen.NUI.Components
22 {
23     /// <summary>
24     /// [Draft] This class provides a View that can recycle items to improve performance.
25     /// </summary>
26     /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
27     [EditorBrowsable(EditorBrowsableState.Never)]
28     public class RecyclerView : ScrollableBase
29     {
30         private RecycleAdapter adapter;
31         private RecycleLayoutManager layoutManager;
32         private int totalItemCount = 15;
33         private List<PropertyNotification> notifications = new List<PropertyNotification>();
34
35         public RecyclerView() : base()
36         {
37             Initialize(new RecycleAdapter(), new RecycleLayoutManager());
38         }
39
40         /// <summary>
41         /// Default constructor.
42         /// </summary>
43         /// <param name="adapter">Recycle adapter of RecyclerView.</param>
44         /// <param name="layoutManager">Recycle layoutManager of RecyclerView.</param>
45         /// <since_tizen> 8 </since_tizen>
46         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
47         [EditorBrowsable(EditorBrowsableState.Never)]
48         public RecyclerView(RecycleAdapter adapter, RecycleLayoutManager layoutManager)
49         {
50             Initialize(adapter, layoutManager);
51         }
52
53         private void Initialize(RecycleAdapter adapter, RecycleLayoutManager layoutManager)
54         {
55             Scrolling += OnScrolling;
56
57             this.adapter = adapter;
58             this.adapter.OnDataChanged += OnAdapterDataChanged;
59
60             this.layoutManager = layoutManager;
61             this.layoutManager.Container = ContentContainer;
62             this.layoutManager.ItemSize = this.adapter.CreateRecycleItem().Size;
63             this.layoutManager.DataCount = this.adapter.Data.Count;
64
65             InitializeItems();
66         }
67
68         private void OnItemSizeChanged(object source, PropertyNotification.NotifyEventArgs args)
69         {
70             layoutManager.Layout(ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
71         }
72         
73         public int TotalItemCount 
74         {
75             get
76             {
77                 return totalItemCount;
78             }
79             set
80             {
81                 totalItemCount = value;
82                 InitializeItems();
83             }
84         }
85
86         private void InitializeItems()
87         {
88             for(int i = Children.Count -1 ; i > -1 ; i--)
89             {
90                 Children[i].Unparent();
91                 notifications[i].Notified -= OnItemSizeChanged;
92                 notifications.RemoveAt(i);
93             }
94
95             for (int i = 0; i < totalItemCount; i++)
96             {
97                 RecycleItem item = adapter.CreateRecycleItem();
98                 item.DataIndex = i;
99                 item.Name = "[" + i + "] recycle";
100
101                 if (i < adapter.Data.Count)
102                 {
103                     adapter.BindData(item);
104                 }
105                 Add(item);
106
107                 PropertyNotification noti = item.AddPropertyNotification("size", PropertyCondition.Step(0.1f));
108                 noti.Notified += OnItemSizeChanged;
109                 notifications.Add(noti);
110             }
111
112             layoutManager.Layout(0.0f);
113
114             if (ScrollingDirection == Direction.Horizontal)
115             {
116                 ContentContainer.SizeWidth = layoutManager.CalculateLayoutOrientationSize();
117             }
118             else
119             {
120                 ContentContainer.SizeHeight = layoutManager.CalculateLayoutOrientationSize();
121             }
122         }
123
124
125         public new Direction ScrollingDirection
126         {
127             get
128             {
129                 return base.ScrollingDirection;
130             }
131             set
132             {
133                 base.ScrollingDirection = value;
134
135                 if (ScrollingDirection == Direction.Horizontal)
136                 {
137                     ContentContainer.SizeWidth = layoutManager.CalculateLayoutOrientationSize();
138                 }
139                 else
140                 {
141                     ContentContainer.SizeHeight = layoutManager.CalculateLayoutOrientationSize();
142                 }
143             }
144         }
145
146         /// <summary>
147         /// Recycler adpater.
148         /// </summary>
149         /// <since_tizen> 8 </since_tizen>
150         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
151         [EditorBrowsable(EditorBrowsableState.Never)]
152         public RecycleAdapter Adapter
153         {
154             get
155             {
156                 return adapter;
157             }
158             set
159             {
160                 if (adapter != null)
161                 {
162                     adapter.OnDataChanged -= OnAdapterDataChanged;
163                 }
164
165                 adapter = value;
166                 adapter.OnDataChanged += OnAdapterDataChanged;
167                 layoutManager.ItemSize = adapter.CreateRecycleItem().Size;
168                 layoutManager.DataCount = adapter.Data.Count;
169                 InitializeItems();
170             }
171         }
172
173         /// <summary>
174         /// Recycler layoutManager.
175         /// </summary>
176         /// <since_tizen> 8 </since_tizen>
177         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public RecycleLayoutManager LayoutManager
180         {
181             get
182             {
183                 return layoutManager;
184             }
185             protected set
186             {
187                 LayoutManager = value;
188                 LayoutManager.Container = ContentContainer;
189                 LayoutManager.ItemSize = Adapter.CreateRecycleItem().Size;
190                 LayoutManager.DataCount = Adapter.Data.Count;
191                 InitializeItems();
192             }
193         }
194
195         private void OnScrolling(object source, ScrollEventArgs args)
196         {
197             layoutManager.Layout(ScrollingDirection == Direction.Horizontal ? args.Position.X : args.Position.Y);
198             List<RecycleItem> recycledItemList = layoutManager.Recycle(ScrollingDirection == Direction.Horizontal ? args.Position.X : args.Position.Y);
199             BindData(recycledItemList);
200         }
201
202         private void OnAdapterDataChanged(object source, EventArgs args)
203         {
204             List<RecycleItem> changedData = new List<RecycleItem>();
205
206             foreach (RecycleItem item in Children)
207             {
208                 changedData.Add(item);
209             }
210
211             BindData(changedData);
212         }
213
214         private void BindData(List<RecycleItem> changedData)
215         {
216             foreach (RecycleItem item in changedData)
217             {
218                 if (item.DataIndex > -1 && item.DataIndex < adapter.Data.Count)
219                 {
220                     item.Show();
221                     item.Name = "["+item.DataIndex+"]";
222                     adapter.BindData(item);
223                 }
224                 else
225                 {
226                     item.Hide();
227                 }
228             }
229         }
230
231         /// <summary>
232         /// Adjust scrolling position by own scrolling rules.
233         /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
234         /// </summary>
235         /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
236         /// <returns>Adjusted scroll destination</returns>
237         /// <since_tizen> 8 </since_tizen>
238         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
239         [EditorBrowsable(EditorBrowsableState.Never)]
240         protected override float AdjustTargetPositionOfScrollAnimation(float position)
241         {
242             // Destination is depending on implementation of layout manager.
243             // Get destination from layout manager.
244             return layoutManager.CalculateCandidateScrollPosition(position);
245         }
246     }
247 }