[NUI] Add GridRecycleLayotManager (#1685)
[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         protected RecycleAdapter mAdapter;
31         protected View mContainer;
32         protected RecycleLayoutManager mLayoutManager;
33         protected int mTotalItemCount = 15;
34         private List<PropertyNotification> notifications = new List<PropertyNotification>();
35
36         public RecyclerView()
37         {
38             Initialize(new RecycleAdapter(), new RecycleLayoutManager());
39         }
40
41         /// <summary>
42         /// Default constructor.
43         /// </summary>
44         /// <param name="adapter">Recycle adapter of RecyclerView.</param>
45         /// <param name="layoutManager">Recycle layoutManager of RecyclerView.</param>
46         /// <since_tizen> 8 </since_tizen>
47         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public RecyclerView(RecycleAdapter adapter, RecycleLayoutManager layoutManager)
50         {
51             Initialize(adapter, layoutManager);
52         }
53
54         private void Initialize(RecycleAdapter adapter, RecycleLayoutManager layoutManager)
55         {
56             Name = "[List]";
57             mContainer = new View()
58             {
59                 WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
60                 HeightSpecification = ScrollingDirection == Direction.Horizontal ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
61                 Layout = new AbsoluteLayout()
62                 {
63                     SetPositionByLayout = false,
64                 },
65                 Name = "Container",
66             };
67
68             Add(mContainer);
69             ScrollEvent += OnScroll;
70
71             mAdapter = adapter;
72             mAdapter.OnDataChanged += OnAdapterDataChanged;
73
74             mLayoutManager = layoutManager;
75             mLayoutManager.Container = mContainer;
76             mLayoutManager.ItemSize = mAdapter.CreateRecycleItem().Size;
77             mLayoutManager.DataCount = mAdapter.Data.Count;
78
79             InitializeItems();
80         }
81
82         private void OnItemSizeChanged(object source, PropertyNotification.NotifyEventArgs args)
83         {
84             mLayoutManager.Layout(ScrollingDirection == Direction.Horizontal ? mContainer.CurrentPosition.X : mContainer.CurrentPosition.Y);
85         }
86         
87         public int TotalItemCount 
88         {
89             get
90             {
91                 return mTotalItemCount;
92             }
93             set
94             {
95                 mTotalItemCount = value;
96                 InitializeItems();
97             }
98         }
99
100         private void InitializeItems()
101         {
102             for(int i = mContainer.Children.Count -1 ; i > -1 ; i--)
103             {
104                 mContainer.Children[i].Unparent();
105                 notifications[i].Notified -= OnItemSizeChanged;
106                 notifications.RemoveAt(i);
107             }
108
109             for (int i = 0; i < mTotalItemCount; i++)
110             {
111                 RecycleItem item = mAdapter.CreateRecycleItem();
112                 item.DataIndex = i;
113                 item.Name = "[" + i + "] recycle";
114
115                 if (i < mAdapter.Data.Count)
116                 {
117                     mAdapter.BindData(item);
118                 }
119                 mContainer.Add(item);
120
121                 PropertyNotification noti = item.AddPropertyNotification("size", PropertyCondition.Step(0.1f));
122                 noti.Notified += OnItemSizeChanged;
123                 notifications.Add(noti);
124             }
125
126             mLayoutManager.Layout(0.0f);
127
128             if (ScrollingDirection == Direction.Horizontal)
129             {
130                 mContainer.SizeWidth = mLayoutManager.CalculateLayoutOrientationSize();
131             }
132             else
133             {
134                 mContainer.SizeHeight = mLayoutManager.CalculateLayoutOrientationSize();
135             }
136         }
137
138
139         public new Direction ScrollingDirection
140         {
141             get
142             {
143                 return base.ScrollingDirection;
144             }
145             set
146             {
147                 base.ScrollingDirection = value;
148
149                 if (ScrollingDirection == Direction.Horizontal)
150                 {
151                     mContainer.SizeWidth = mLayoutManager.CalculateLayoutOrientationSize();
152                 }
153                 else
154                 {
155                     mContainer.SizeHeight = mLayoutManager.CalculateLayoutOrientationSize();
156                 }
157             }
158         }
159
160         /// <summary>
161         /// Recycler adpater.
162         /// </summary>
163         /// <since_tizen> 8 </since_tizen>
164         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
165         [EditorBrowsable(EditorBrowsableState.Never)]
166         public RecycleAdapter Adapter
167         {
168             get
169             {
170                 return mAdapter;
171             }
172             set
173             {
174                 if(mAdapter != null)
175                 {
176                     mAdapter.OnDataChanged -= OnAdapterDataChanged;
177                 }
178
179                 mAdapter = value;
180                 mAdapter.OnDataChanged += OnAdapterDataChanged;
181                 mLayoutManager.ItemSize = mAdapter.CreateRecycleItem().Size;
182                 mLayoutManager.DataCount = mAdapter.Data.Count;
183                 InitializeItems();
184             }
185         }
186
187         /// <summary>
188         /// Recycler layoutManager.
189         /// </summary>
190         /// <since_tizen> 8 </since_tizen>
191         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
192         [EditorBrowsable(EditorBrowsableState.Never)]
193         public RecycleLayoutManager LayoutManager
194         {
195             get
196             {
197                 return mLayoutManager;
198             }
199             set
200             {
201                 mLayoutManager = value;
202                 mLayoutManager.Container = mContainer;
203                 mLayoutManager.ItemSize = mAdapter.CreateRecycleItem().Size;
204                 mLayoutManager.DataCount = mAdapter.Data.Count;
205                 InitializeItems();
206             }
207         }
208
209         private void OnScroll(object source, ScrollableBase.ScrollEventArgs args)
210         {
211             mLayoutManager.Layout(ScrollingDirection == Direction.Horizontal ? args.Position.X : args.Position.Y);
212             List<RecycleItem> recycledItemList = mLayoutManager.Recycle(ScrollingDirection == Direction.Horizontal ? args.Position.X : args.Position.Y);
213             BindData(recycledItemList);
214         }
215
216         private void OnAdapterDataChanged(object source, EventArgs args)
217         {
218             List<RecycleItem> changedData = new List<RecycleItem>();
219
220             foreach (RecycleItem item in mContainer.Children)
221             {
222                 changedData.Add(item);
223             }
224
225             BindData(changedData);
226         }
227
228         private void BindData(List<RecycleItem> changedData)
229         {
230             foreach (RecycleItem item in changedData)
231             {
232                 if (item.DataIndex > -1 && item.DataIndex < mAdapter.Data.Count)
233                 {
234                     item.Show();
235                     item.Name = "["+item.DataIndex+"]";
236                     mAdapter.BindData(item);
237                 }
238                 else
239                 {
240                     item.Hide();
241                 }
242             }
243         }
244
245         /// <summary>
246         /// Adjust scrolling position by own scrolling rules.
247         /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
248         /// </summary>
249         /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
250         /// <returns>Adjusted scroll destination</returns>
251         /// <since_tizen> 8 </since_tizen>
252         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
253         [EditorBrowsable(EditorBrowsableState.Never)]
254         protected override float AdjustTargetPositionOfScrollAnimation(float position)
255         {
256             // Destination is depending on implementation of layout manager.
257             // Get destination from layout manager.
258             return mLayoutManager.CalculateCandidateScrollPosition(position);
259         }
260     }
261 }