透明小地图&列表点击切换&海拔图头像位移

This commit is contained in:
lishuo committed 2022-03-29 18:41:32 +08:00
1 parent bf48989a9c
commit 6ec4c0101b
114 files changed
+12787 -6517

No files matched your search

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fab03186a79d74df99ffa7147294ea61
folderAsset: yes
timeCreated: 1579763866
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,129 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using UnityEngine.UI;
using UnityEditor.AnimatedValues;
using UnityEditor;
using UnityEngine;
namespace PolyAndCode.UI
{
[CustomEditor(typeof(RecyclableScrollRect), true)]
[CanEditMultipleObjects]
/// <summary>
/// Custom Editor for the Recyclable Scroll Rect Component which is derived from Scroll Rect.
/// </summary>
public class RecyclableScrollRectEditor : Editor
{
SerializedProperty m_Content;
SerializedProperty m_MovementType;
SerializedProperty m_Elasticity;
SerializedProperty m_Inertia;
SerializedProperty m_DecelerationRate;
SerializedProperty m_ScrollSensitivity;
SerializedProperty m_Viewport;
SerializedProperty m_OnValueChanged;
//inherited
SerializedProperty _protoTypeCell;
SerializedProperty _selfInitialize;
SerializedProperty _direction;
SerializedProperty _type;
AnimBool m_ShowElasticity;
AnimBool m_ShowDecelerationRate;
RecyclableScrollRect _script;
protected virtual void OnEnable()
{
_script = (RecyclableScrollRect)target;
m_Content = serializedObject.FindProperty("m_Content");
m_MovementType = serializedObject.FindProperty("m_MovementType");
m_Elasticity = serializedObject.FindProperty("m_Elasticity");
m_Inertia = serializedObject.FindProperty("m_Inertia");
m_DecelerationRate = serializedObject.FindProperty("m_DecelerationRate");
m_ScrollSensitivity = serializedObject.FindProperty("m_ScrollSensitivity");
m_Viewport = serializedObject.FindProperty("m_Viewport");
m_OnValueChanged = serializedObject.FindProperty("m_OnValueChanged");
//Inherited
_protoTypeCell = serializedObject.FindProperty("PrototypeCell");
_selfInitialize = serializedObject.FindProperty("SelfInitialize");
_direction = serializedObject.FindProperty("Direction");
_type = serializedObject.FindProperty("IsGrid");
m_ShowElasticity = new AnimBool(Repaint);
m_ShowDecelerationRate = new AnimBool(Repaint);
SetAnimBools(true);
}
protected virtual void OnDisable()
{
m_ShowElasticity.valueChanged.RemoveListener(Repaint);
m_ShowDecelerationRate.valueChanged.RemoveListener(Repaint);
}
void SetAnimBools(bool instant)
{
SetAnimBool(m_ShowElasticity, !m_MovementType.hasMultipleDifferentValues && m_MovementType.enumValueIndex == (int)ScrollRect.MovementType.Elastic, instant);
SetAnimBool(m_ShowDecelerationRate, !m_Inertia.hasMultipleDifferentValues && m_Inertia.boolValue, instant);
}
void SetAnimBool(AnimBool a, bool value, bool instant)
{
if (instant)
a.value = value;
else
a.target = value;
}
public override void OnInspectorGUI()
{
SetAnimBools(false);
serializedObject.Update();
EditorGUILayout.PropertyField(_direction);
EditorGUILayout.PropertyField(_type, new GUIContent("Grid"));
if (_type.boolValue)
{
string title = _direction.enumValueIndex == (int)RecyclableScrollRect.DirectionType.Vertical ? "Coloumns" : "Rows";
_script.Segments = EditorGUILayout.IntField(title, _script.Segments);
}
EditorGUILayout.PropertyField(_selfInitialize);
EditorGUILayout.PropertyField(m_Viewport);
EditorGUILayout.PropertyField(m_Content);
EditorGUILayout.PropertyField(_protoTypeCell);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_MovementType);
if (EditorGUILayout.BeginFadeGroup(m_ShowElasticity.faded))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_Elasticity);
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFadeGroup();
EditorGUILayout.PropertyField(m_Inertia);
if (EditorGUILayout.BeginFadeGroup(m_ShowDecelerationRate.faded))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_DecelerationRate);
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFadeGroup();
EditorGUILayout.PropertyField(m_ScrollSensitivity);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_OnValueChanged);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 74168ba3b38714896bfecd0206751ad1
timeCreated: 1579677840
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
using UnityEngine;
using UnityEditor;
namespace PolyAndCode.UI
{
[ExecuteInEditMode]
public static class RecyclableScrollViewEditorTool
{
const string PrefabPath = "Assets/Recyclable Scroll Rect/Main/Prefab/Recyclable Scroll View.prefab";
[MenuItem("GameObject/UI/Recyclable Scroll View")]
private static void CreateRecyclableScrollView()
{
GameObject selected = Selection.activeGameObject;
//If selected isn't a UI gameobject then find a Canvas
if (!selected || !(selected.transform is RectTransform))
{
selected = GameObject.FindObjectOfType<Canvas>().gameObject;
}
if (!selected) return;
GameObject asset = AssetDatabase.LoadAssetAtPath(PrefabPath, typeof(GameObject)) as GameObject;
GameObject item = Object.Instantiate(asset);
item.name = "Recyclable Scroll View";
item.transform.SetParent(selected.transform);
item.transform.localPosition = Vector3.zero;
Selection.activeGameObject = item;
Undo.RegisterCreatedObjectUndo(item, "Create Recycalable Scroll view");
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: cf7388e878b9648048580d3339aa57e6
timeCreated: 1580287998
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9c857a3657dfd4bcb98387044af3ae7d
folderAsset: yes
timeCreated: 1580416784
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

@@ -0,0 +1,107 @@
fileFormatVersion: 2
guid: 6b7325ceb7a524d43bf38b93ad0c7118
timeCreated: 1580416733
licenseType: Store
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 4
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -1
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: Standalone
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: iPhone
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: Android
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: dfa1633e450e04d51b559a09b45e4efd
folderAsset: yes
timeCreated: 1580414924
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,234 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1527163063724600}
m_IsPrefabParent: 1
--- !u!1 &1253332298834758
GameObject:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224953711503076072}
m_Layer: 5
m_Name: Content
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1527163063724600
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224141822819573504}
- component: {fileID: 222122610330645636}
- component: {fileID: 114310977074674660}
- component: {fileID: 114035338358051486}
m_Layer: 5
m_Name: Recyclable Scroll View
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1740122973338780
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224516841076241162}
- component: {fileID: 114664610104649842}
- component: {fileID: 222300018684681582}
- component: {fileID: 114086589109956998}
m_Layer: 5
m_Name: Viewport
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &114035338358051486
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1527163063724600}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114086589109956998
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1740122973338780}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114310977074674660
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1527163063724600}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 248d9fa993d3e44daa165a75b0063088, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Content: {fileID: 224953711503076072}
m_Horizontal: 0
m_Vertical: 1
m_MovementType: 1
m_Elasticity: 0.1
m_Inertia: 1
m_DecelerationRate: 0.135
m_ScrollSensitivity: 1
m_Viewport: {fileID: 224516841076241162}
m_HorizontalScrollbar: {fileID: 0}
m_VerticalScrollbar: {fileID: 0}
m_HorizontalScrollbarVisibility: 0
m_VerticalScrollbarVisibility: 0
m_HorizontalScrollbarSpacing: 0
m_VerticalScrollbarSpacing: 0
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.ScrollRect+ScrollRectEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
IsGrid: 0
PrototypeCell: {fileID: 0}
SelfInitialize: 1
Direction: 0
_segments: 0
--- !u!114 &114664610104649842
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1740122973338780}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1200242548, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ShowMaskGraphic: 0
--- !u!222 &222122610330645636
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1527163063724600}
--- !u!222 &222300018684681582
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1740122973338780}
--- !u!224 &224141822819573504
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1527163063724600}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224516841076241162}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -122.19763, y: -9.611036}
m_SizeDelta: {x: 200, y: 200}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224516841076241162
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1740122973338780}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224953711503076072}
m_Father: {fileID: 224141822819573504}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!224 &224953711503076072
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1253332298834758}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224516841076241162}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0.000021875792}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 1}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3c01abf790e1a42ca8d10525f571cab5
timeCreated: 1580414917
licenseType: Store
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ede93dd89924c46df91d86c4d59f5e62
folderAsset: yes
timeCreated: 1580017437
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 954ddfcde675c304baf014480514e599
folderAsset: yes
timeCreated: 1598985910
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
/// <summary>
/// Interface for creating a Cell.
/// Prototype Cell must have a monobeviour inheriting from ICell
/// </summary>
namespace PolyAndCode.UI
{
public interface ICell
{
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: d4ad8716ee240469ca93bc22214fddfe
timeCreated: 1577787384
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
/// <summary>
/// Interface for creating DataSource
/// Recyclable Scroll Rect must be provided a Data source which must inherit from this.
/// </summary>
namespace PolyAndCode.UI
{
public interface IRecyclableScrollRectDataSource
{
int GetItemCount();
void SetCell(ICell cell, int index);
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 7ef29d4c7afcd43079f3cfc4d4e1ecbf
timeCreated: 1580410346
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,154 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using System;
using UnityEngine;
using UnityEngine.UI;
namespace PolyAndCode.UI
{
/// <summary>
/// Entry for the recycling system. Extends Unity's inbuilt ScrollRect.
/// </summary>
public class RecyclableScrollRect : ScrollRect
{
[HideInInspector]
public IRecyclableScrollRectDataSource DataSource;
public bool IsGrid;
//Prototype cell can either be a prefab or present as a child to the content(will automatically be disabled in runtime)
public RectTransform PrototypeCell;
//If true the intiziation happens at Start. Controller must assign the datasource in Awake.
//Set to false if self init is not required and use public init API.
public bool SelfInitialize = true;
public enum DirectionType
{
Vertical,
Horizontal
}
public DirectionType Direction;
//Segments : coloums for vertical and rows for horizontal.
public int Segments
{
set
{
_segments = Math.Max(value, 2);
}
get
{
return _segments;
}
}
[SerializeField]
private int _segments;
private RecyclingSystem _recyclingSystem;
private Vector2 _prevAnchoredPos;
protected override void Start()
{
//defafult(built-in) in scroll rect can have both directions enabled, Recyclable scroll rect can be scrolled in only one direction.
//setting default as vertical, Initialize() will set this again.
vertical = true;
horizontal = false;
if (!Application.isPlaying) return;
if (SelfInitialize) Initialize();
}
/// <summary>
/// Initialization when selfInitalize is true. Assumes that data source is set in controller's Awake.
/// </summary>
private void Initialize()
{
//Contruct the recycling system.
if (Direction == DirectionType.Vertical)
{
_recyclingSystem = new VerticalRecyclingSystem(PrototypeCell, viewport, content, DataSource, IsGrid, Segments);
}
else if (Direction == DirectionType.Horizontal)
{
_recyclingSystem = new HorizontalRecyclingSystem(PrototypeCell, viewport, content, DataSource, IsGrid, Segments);
}
vertical = Direction == DirectionType.Vertical;
horizontal = Direction == DirectionType.Horizontal;
_prevAnchoredPos = content.anchoredPosition;
onValueChanged.RemoveListener(OnValueChangedListener);
//Adding listener after pool creation to avoid any unwanted recycling behaviour.(rare scenerio)
StartCoroutine(_recyclingSystem.InitCoroutine(() =>
onValueChanged.AddListener(OnValueChangedListener)
));
}
/// <summary>
/// public API for Initializing when datasource is not set in controller's Awake. Make sure selfInitalize is set to false.
/// </summary>
public void Initialize(IRecyclableScrollRectDataSource dataSource)
{
DataSource = dataSource;
Initialize();
}
/// <summary>
/// Added as a listener to the OnValueChanged event of Scroll rect.
/// Recycling entry point for recyling systems.
/// </summary>
/// <param name="direction">scroll direction</param>
public void OnValueChangedListener(Vector2 normalizedPos)
{
Vector2 dir = content.anchoredPosition - _prevAnchoredPos;
m_ContentStartPosition += _recyclingSystem.OnValueChangedListener(dir);
_prevAnchoredPos = content.anchoredPosition;
}
/// <summary>
///Reloads the data. Call this if a new datasource is assigned.
/// </summary>
public void ReloadData()
{
ReloadData(DataSource);
}
/// <summary>
/// Overloaded ReloadData with dataSource param
///Reloads the data. Call this if a new datasource is assigned.
/// </summary>
public void ReloadData(IRecyclableScrollRectDataSource dataSource)
{
if (_recyclingSystem != null)
{
StopMovement();
onValueChanged.RemoveListener(OnValueChangedListener);
_recyclingSystem.DataSource = dataSource;
StartCoroutine(_recyclingSystem.InitCoroutine(() =>
onValueChanged.AddListener(OnValueChangedListener)
));
_prevAnchoredPos = content.anchoredPosition;
}
}
/*
#region Testing
private void OnDrawGizmos()
{
if (_recyclableScrollRect is VerticalRecyclingSystem)
{
((VerticalRecyclingSystem)_recyclableScrollRect).OnDrawGizmos();
}
if (_recyclableScrollRect is HorizontalRecyclingSystem)
{
((HorizontalRecyclingSystem)_recyclableScrollRect).OnDrawGizmos();
}
}
#endregion
*/
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 248d9fa993d3e44daa165a75b0063088
timeCreated: 1580416764
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 6b7325ceb7a524d43bf38b93ad0c7118, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d1b6a61b2a8a958489c9e8598a56feda
folderAsset: yes
timeCreated: 1598985947
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,383 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PolyAndCode.UI
{
/// <summary>
/// Recyling system for horizontal type.
/// </summary>
public class HorizontalRecyclingSystem : RecyclingSystem
{
//Assigned by constructor
private readonly int _rows;
//Cell dimensions
private float _cellWidth, _cellHeight;
//Pool Generation
private List<RectTransform> _cellPool;
private List<ICell> _cachedCells;
private Bounds _recyclableViewBounds;
//Temps, Flags
private readonly Vector3[] _corners = new Vector3[4];
private bool _recycling;
//Trackers
private int currentItemCount; //item count corresponding to the datasource.
private int leftMostCellIndex, rightMostCellIndex; //Topmost and bottommost cell in the List
private int _leftMostCellRow, _RightMostCellRow; // used for recyling in Grid layout. leftmost and rightmost row
//Cached zero vector
private Vector2 zeroVector = Vector2.zero;
#region INIT
public HorizontalRecyclingSystem(RectTransform prototypeCell, RectTransform viewport, RectTransform content, IRecyclableScrollRectDataSource dataSource, bool isGrid, int rows)
{
PrototypeCell = prototypeCell;
Viewport = viewport;
Content = content;
DataSource = dataSource;
IsGrid = isGrid;
_rows = isGrid ? rows : 1;
_recyclableViewBounds = new Bounds();
}
/// <summary>
/// Corotuine for initiazation.
/// Using coroutine for init because few UI stuff requires a frame to update
/// </summary>
/// <param name="onInitialized">callback when init done</param>
/// <returns></returns>
public override IEnumerator InitCoroutine(Action onInitialized)
{
//Setting up container and bounds
SetLeftAnchor(Content);
Content.anchoredPosition = Vector3.zero;
yield return null;
SetRecyclingBounds();
//Cell Poool
CreateCellPool();
currentItemCount = _cellPool.Count;
leftMostCellIndex = 0;
rightMostCellIndex = _cellPool.Count - 1;
//Set content width according to no of coloums
int coloums = Mathf.CeilToInt((float)_cellPool.Count / _rows);
float contentXSize = coloums * _cellWidth;
Content.sizeDelta = new Vector2(contentXSize, Content.sizeDelta.y);
SetLeftAnchor(Content);
if (onInitialized != null) onInitialized();
}
/// <summary>
/// Sets the uppper and lower bounds for recycling cells.
/// </summary>
private void SetRecyclingBounds()
{
Viewport.GetWorldCorners(_corners);
float threshHold = RecyclingThreshold * (_corners[2].x - _corners[0].x);
_recyclableViewBounds.min = new Vector3(_corners[0].x - threshHold, _corners[0].y);
_recyclableViewBounds.max = new Vector3(_corners[2].x + threshHold, _corners[2].y);
}
/// <summary>
/// Creates cell Pool for recycling, Caches ICells
/// </summary>
private void CreateCellPool()
{
//Reseting Pool
if (_cellPool != null)
{
_cellPool.ForEach((RectTransform item) => UnityEngine.Object.Destroy(item.gameObject));
_cellPool.Clear();
_cachedCells.Clear();
}
else
{
_cachedCells = new List<ICell>();
_cellPool = new List<RectTransform>();
}
//Set the prototype cell active and set cell anchor as top
PrototypeCell.gameObject.SetActive(true);
SetLeftAnchor(PrototypeCell);
//set new cell size according to its aspect ratio
_cellHeight = Content.rect.height / _rows;
_cellWidth = PrototypeCell.sizeDelta.x / PrototypeCell.sizeDelta.y * _cellHeight;
//Reset
_leftMostCellRow = _RightMostCellRow = 0;
//Temps
float currentPoolCoverage = 0;
int poolSize = 0;
float posX = 0;
float posY = 0;
//Get the required pool coverage and mininum size for the Cell pool
float requriedCoverage = MinPoolCoverage * Viewport.rect.width;
int minPoolSize = Math.Min(MinPoolSize, DataSource.GetItemCount());
//create cells untill the Pool area is covered and pool size is the minimum required
while ((poolSize < minPoolSize || currentPoolCoverage < requriedCoverage) && poolSize < DataSource.GetItemCount())
{
//Instantiate and add to Pool
RectTransform item = (UnityEngine.Object.Instantiate(PrototypeCell.gameObject)).GetComponent<RectTransform>();
item.name = "Cell";
item.sizeDelta = new Vector2(_cellWidth, _cellHeight);
_cellPool.Add(item);
item.SetParent(Content, false);
if (IsGrid)
{
posY = -_RightMostCellRow * _cellHeight;
item.anchoredPosition = new Vector2(posX, posY);
if (++_RightMostCellRow >= _rows)
{
_RightMostCellRow = 0;
posX += _cellWidth;
currentPoolCoverage += item.rect.width;
}
}
else
{
item.anchoredPosition = new Vector2(posX, 0);
posX = item.anchoredPosition.x + item.rect.width;
currentPoolCoverage += item.rect.width;
}
//Setting data for Cell
_cachedCells.Add(item.GetComponent<ICell>());
DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize);
//Update the Pool size
poolSize++;
}
if (IsGrid)
{
_RightMostCellRow = (_RightMostCellRow - 1 + _rows) % _rows;
}
//Deactivate prototype cell if it is not a prefab(i.e it's present in scene)
if (PrototypeCell.gameObject.scene.IsValid())
{
PrototypeCell.gameObject.SetActive(false);
}
}
#endregion
#region RECYCLING
/// <summary>
/// Recyling entry point
/// </summary>
/// <param name="direction">scroll direction </param>
/// <returns></returns>
public override Vector2 OnValueChangedListener(Vector2 direction)
{
if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector;
//Updating Recyclable view bounds since it can change with resolution changes.
SetRecyclingBounds();
if (direction.x < 0 && _cellPool[rightMostCellIndex].MinX() < _recyclableViewBounds.max.x)
{
return RecycleLeftToRight();
}
else if (direction.x > 0 && _cellPool[leftMostCellIndex].MaxX() > _recyclableViewBounds.min.x)
{
return RecycleRightToleft();
}
return zeroVector;
}
/// <summary>
/// Recycles cells from Left to Right in the List heirarchy
/// </summary>
private Vector2 RecycleLeftToRight()
{
_recycling = true;
int n = 0;
float posX = IsGrid ? _cellPool[rightMostCellIndex].anchoredPosition.x : 0;
float posY = 0;
//to determine if content size needs to be updated
int additionalColoums = 0;
//Recycle until cell at left is avaiable and current item count smaller than datasource
while (_cellPool[leftMostCellIndex].MaxX() < _recyclableViewBounds.min.x && currentItemCount < DataSource.GetItemCount())
{
if (IsGrid)
{
if (++_RightMostCellRow >= _rows)
{
n++;
_RightMostCellRow = 0;
posX = _cellPool[rightMostCellIndex].anchoredPosition.x + _cellWidth;
additionalColoums++;
}
//Move Left most cell to right
posY = -_RightMostCellRow * _cellHeight;
_cellPool[leftMostCellIndex].anchoredPosition = new Vector2(posX, posY);
if (++_leftMostCellRow >= _rows)
{
_leftMostCellRow = 0;
additionalColoums--;
}
}
else
{
//Move Left most cell to right
posX = _cellPool[rightMostCellIndex].anchoredPosition.x + _cellPool[rightMostCellIndex].sizeDelta.x;
_cellPool[leftMostCellIndex].anchoredPosition = new Vector2(posX, _cellPool[leftMostCellIndex].anchoredPosition.y);
}
//Cell for row at
DataSource.SetCell(_cachedCells[leftMostCellIndex], currentItemCount);
//set new indices
rightMostCellIndex = leftMostCellIndex;
leftMostCellIndex = (leftMostCellIndex + 1) % _cellPool.Count;
currentItemCount++;
if (!IsGrid) n++;
}
//Content size adjustment
if (IsGrid)
{
Content.sizeDelta += additionalColoums * Vector2.right * _cellWidth;
if (additionalColoums > 0)
{
n -= additionalColoums;
}
}
//Content anchor position adjustment.
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x);
Content.anchoredPosition += n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
_recycling = false;
return n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
}
/// <summary>
/// Recycles cells from Right to Left in the List heirarchy
/// </summary>
private Vector2 RecycleRightToleft()
{
_recycling = true;
int n = 0;
float posX = IsGrid ? _cellPool[leftMostCellIndex].anchoredPosition.x : 0;
float posY = 0;
//to determine if content size needs to be updated
int additionalColoums = 0;
//Recycle until cell at Right end is avaiable and current item count is greater than cellpool size
while (_cellPool[rightMostCellIndex].MinX() > _recyclableViewBounds.max.x && currentItemCount > _cellPool.Count)
{
if (IsGrid)
{
if (--_leftMostCellRow < 0)
{
n++;
_leftMostCellRow = _rows - 1;
posX = _cellPool[leftMostCellIndex].anchoredPosition.x - _cellWidth;
additionalColoums++;
}
//Move Right most cell to left
posY = -_leftMostCellRow * _cellHeight;
_cellPool[rightMostCellIndex].anchoredPosition = new Vector2(posX, posY);
if (--_RightMostCellRow < 0)
{
_RightMostCellRow = _rows - 1;
additionalColoums--;
}
}
else
{
//Move Right most cell to left
posX = _cellPool[leftMostCellIndex].anchoredPosition.x - _cellPool[leftMostCellIndex].sizeDelta.x;
_cellPool[rightMostCellIndex].anchoredPosition = new Vector2(posX, _cellPool[rightMostCellIndex].anchoredPosition.y);
n++;
}
currentItemCount--;
//Cell for row at
DataSource.SetCell(_cachedCells[rightMostCellIndex], currentItemCount - _cellPool.Count);
//set new indices
leftMostCellIndex = rightMostCellIndex;
rightMostCellIndex = (rightMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count;
}
//Content size adjustment
if (IsGrid)
{
Content.sizeDelta += additionalColoums * Vector2.right * _cellWidth;
if (additionalColoums > 0)
{
n -= additionalColoums;
}
}
//Content anchor position adjustment.
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x);
Content.anchoredPosition -= n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
_recycling = false;
return -n * Vector2.right * _cellPool[leftMostCellIndex].sizeDelta.x;
}
#endregion
#region HELPERS
/// <summary>
/// Anchoring cell and content rect transforms to top preset. Makes repositioning easy.
/// </summary>
/// <param name="rectTransform"></param>
private void SetLeftAnchor(RectTransform rectTransform)
{
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
float width = rectTransform.rect.width;
float height = rectTransform.rect.height;
Vector2 pos = IsGrid ? new Vector2(0, 1) : new Vector2(0, 0.5f);
//Setting top anchor
rectTransform.anchorMin = pos;
rectTransform.anchorMax = pos;
rectTransform.pivot = pos;
//Reapply size
rectTransform.sizeDelta = new Vector2(width, height);
}
#endregion
#region TESTING
public void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawLine(_recyclableViewBounds.min - new Vector3(0, 2000), _recyclableViewBounds.min + new Vector3(0, 2000));
Gizmos.color = Color.red;
Gizmos.DrawLine(_recyclableViewBounds.max - new Vector3(0, 2000), _recyclableViewBounds.max + new Vector3(0, 2000));
}
#endregion
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 18e7b2b6055ccc94590a39b5401147a8
timeCreated: 1595684755
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using System.Collections;
using UnityEngine;
namespace PolyAndCode.UI
{
/// <summary>
/// Absract Class for creating a Recycling system.
/// </summary>
public abstract class RecyclingSystem
{
public IRecyclableScrollRectDataSource DataSource;
protected RectTransform Viewport, Content;
protected RectTransform PrototypeCell;
protected bool IsGrid;
protected float MinPoolCoverage = 1.5f; // The recyclable pool must cover (viewPort * _poolCoverage) area.
protected int MinPoolSize = 10; // Cell pool must have a min size
protected float RecyclingThreshold = .2f; //Threshold for recycling above and below viewport
public abstract IEnumerator InitCoroutine(System.Action onInitialized = null);
public abstract Vector2 OnValueChangedListener(Vector2 direction);
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 52518c8e74351d0469a587c411d4438f
timeCreated: 1595684222
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,404 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PolyAndCode.UI
{
/// <summary>
/// Recyling system for Vertical type.
/// </summary>
public class VerticalRecyclingSystem : RecyclingSystem
{
//Assigned by constructor
private readonly int _coloumns;
//Cell dimensions
private float _cellWidth, _cellHeight;
//Pool Generation
private List<RectTransform> _cellPool;
private List<ICell> _cachedCells;
private Bounds _recyclableViewBounds;
//Temps, Flags
private readonly Vector3[] _corners = new Vector3[4];
private bool _recycling;
//Trackers
private int currentItemCount; //item count corresponding to the datasource.
private int topMostCellIndex, bottomMostCellIndex; //Topmost and bottommost cell in the heirarchy
private int _topMostCellColoumn, _bottomMostCellColoumn; // used for recyling in Grid layout. top-most and bottom-most coloumn
//Cached zero vector
private Vector2 zeroVector = Vector2.zero;
#region INIT
public VerticalRecyclingSystem(RectTransform prototypeCell, RectTransform viewport, RectTransform content, IRecyclableScrollRectDataSource dataSource, bool isGrid, int coloumns)
{
PrototypeCell = prototypeCell;
Viewport = viewport;
Content = content;
DataSource = dataSource;
IsGrid = isGrid;
_coloumns = isGrid ? coloumns : 1;
_recyclableViewBounds = new Bounds();
}
/// <summary>
/// Corotuine for initiazation.
/// Using coroutine for init because few UI stuff requires a frame to update
/// </summary>
/// <param name="onInitialized">callback when init done</param>
/// <returns></returns>>
public override IEnumerator InitCoroutine(System.Action onInitialized)
{
SetTopAnchor(Content);
Content.anchoredPosition = Vector3.zero;
yield return null;
SetRecyclingBounds();
//Cell Poool
CreateCellPool();
currentItemCount = _cellPool.Count;
topMostCellIndex = 0;
bottomMostCellIndex = _cellPool.Count - 1;
//Set content height according to no of rows
int noOfRows = (int)Mathf.Ceil((float)_cellPool.Count / (float)_coloumns);
float contentYSize = noOfRows * _cellHeight;
Content.sizeDelta = new Vector2(Content.sizeDelta.x, contentYSize);
SetTopAnchor(Content);
if (onInitialized != null) onInitialized();
}
/// <summary>
/// Sets the uppper and lower bounds for recycling cells.
/// </summary>
private void SetRecyclingBounds()
{
Viewport.GetWorldCorners(_corners);
float threshHold = RecyclingThreshold * (_corners[2].y - _corners[0].y);
_recyclableViewBounds.min = new Vector3(_corners[0].x, _corners[0].y - threshHold);
_recyclableViewBounds.max = new Vector3(_corners[2].x, _corners[2].y + threshHold);
}
/// <summary>
/// Creates cell Pool for recycling, Caches ICells
/// </summary>
private void CreateCellPool()
{
//Reseting Pool
if (_cellPool != null)
{
_cellPool.ForEach((RectTransform item) => UnityEngine.Object.Destroy(item.gameObject));
_cellPool.Clear();
_cachedCells.Clear();
}
else
{
_cachedCells = new List<ICell>();
_cellPool = new List<RectTransform>();
}
//Set the prototype cell active and set cell anchor as top
PrototypeCell.gameObject.SetActive(true);
if (IsGrid)
{
SetTopLeftAnchor(PrototypeCell);
}
else
{
SetTopAnchor(PrototypeCell);
}
//Reset
_topMostCellColoumn = _bottomMostCellColoumn = 0;
//Temps
float currentPoolCoverage = 0;
int poolSize = 0;
float posX = 0;
float posY = 0;
//set new cell size according to its aspect ratio
_cellWidth = Content.rect.width / _coloumns;
_cellHeight = PrototypeCell.sizeDelta.y / PrototypeCell.sizeDelta.x * _cellWidth;
//Get the required pool coverage and mininum size for the Cell pool
float requriedCoverage = MinPoolCoverage * Viewport.rect.height;
int minPoolSize = Math.Min(MinPoolSize, DataSource.GetItemCount());
//create cells untill the Pool area is covered and pool size is the minimum required
while ((poolSize < minPoolSize || currentPoolCoverage < requriedCoverage) && poolSize < DataSource.GetItemCount())
{
//Instantiate and add to Pool
RectTransform item = (UnityEngine.Object.Instantiate(PrototypeCell.gameObject)).GetComponent<RectTransform>();
item.name = "Cell";
item.sizeDelta = new Vector2(_cellWidth, _cellHeight);
_cellPool.Add(item);
item.SetParent(Content, false);
if (IsGrid)
{
posX = _bottomMostCellColoumn * _cellWidth;
item.anchoredPosition = new Vector2(posX, posY);
if (++_bottomMostCellColoumn >= _coloumns)
{
_bottomMostCellColoumn = 0;
posY -= _cellHeight;
currentPoolCoverage += item.rect.height;
}
}
else
{
item.anchoredPosition = new Vector2(0, posY);
posY = item.anchoredPosition.y - item.rect.height;
currentPoolCoverage += item.rect.height;
}
//Setting data for Cell
_cachedCells.Add(item.GetComponent<ICell>());
DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize);
//Update the Pool size
poolSize++;
}
//TODO : you alrady have a _currentColoumn varaiable. Why this calculation?????
if (IsGrid)
{
_bottomMostCellColoumn = (_bottomMostCellColoumn - 1 + _coloumns) % _coloumns;
}
//Deactivate prototype cell if it is not a prefab(i.e it's present in scene)
if (PrototypeCell.gameObject.scene.IsValid())
{
PrototypeCell.gameObject.SetActive(false);
}
}
#endregion
#region RECYCLING
/// <summary>
/// Recyling entry point
/// </summary>
/// <param name="direction">scroll direction </param>
/// <returns></returns>
public override Vector2 OnValueChangedListener(Vector2 direction)
{
if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector;
//Updating Recyclable view bounds since it can change with resolution changes.
SetRecyclingBounds();
if (direction.y > 0 && _cellPool[bottomMostCellIndex].MaxY() > _recyclableViewBounds.min.y)
{
return RecycleTopToBottom();
}
else if (direction.y < 0 && _cellPool[topMostCellIndex].MinY() < _recyclableViewBounds.max.y)
{
return RecycleBottomToTop();
}
return zeroVector;
}
/// <summary>
/// Recycles cells from top to bottom in the List heirarchy
/// </summary>
private Vector2 RecycleTopToBottom()
{
_recycling = true;
int n = 0;
float posY = IsGrid ? _cellPool[bottomMostCellIndex].anchoredPosition.y : 0;
float posX = 0;
//to determine if content size needs to be updated
int additionalRows = 0;
//Recycle until cell at Top is avaiable and current item count smaller than datasource
while (_cellPool[topMostCellIndex].MinY() > _recyclableViewBounds.max.y && currentItemCount < DataSource.GetItemCount())
{
if (IsGrid)
{
if (++_bottomMostCellColoumn >= _coloumns)
{
n++;
_bottomMostCellColoumn = 0;
posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellHeight;
additionalRows++;
}
//Move top cell to bottom
posX = _bottomMostCellColoumn * _cellWidth;
_cellPool[topMostCellIndex].anchoredPosition = new Vector2(posX, posY);
if (++_topMostCellColoumn >= _coloumns)
{
_topMostCellColoumn = 0;
additionalRows--;
}
}
else
{
//Move top cell to bottom
posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellPool[bottomMostCellIndex].sizeDelta.y;
_cellPool[topMostCellIndex].anchoredPosition = new Vector2(_cellPool[topMostCellIndex].anchoredPosition.x, posY);
}
//Cell for row at
DataSource.SetCell(_cachedCells[topMostCellIndex], currentItemCount);
//set new indices
bottomMostCellIndex = topMostCellIndex;
topMostCellIndex = (topMostCellIndex + 1) % _cellPool.Count;
currentItemCount++;
if (!IsGrid) n++;
}
//Content size adjustment
if (IsGrid)
{
Content.sizeDelta += additionalRows * Vector2.up * _cellHeight;
//TODO : check if it is supposed to be done only when > 0
if (additionalRows > 0)
{
n -= additionalRows;
}
}
//Content anchor position adjustment.
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
Content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
_recycling = false;
return -new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
}
/// <summary>
/// Recycles cells from bottom to top in the List heirarchy
/// </summary>
private Vector2 RecycleBottomToTop()
{
_recycling = true;
int n = 0;
float posY = IsGrid ? _cellPool[topMostCellIndex].anchoredPosition.y : 0;
float posX = 0;
//to determine if content size needs to be updated
int additionalRows = 0;
//Recycle until cell at bottom is avaiable and current item count is greater than cellpool size
while (_cellPool[bottomMostCellIndex].MaxY() < _recyclableViewBounds.min.y && currentItemCount > _cellPool.Count)
{
if (IsGrid)
{
if (--_topMostCellColoumn < 0)
{
n++;
_topMostCellColoumn = _coloumns - 1;
posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellHeight;
additionalRows++;
}
//Move bottom cell to top
posX = _topMostCellColoumn * _cellWidth;
_cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(posX, posY);
if (--_bottomMostCellColoumn < 0)
{
_bottomMostCellColoumn = _coloumns - 1;
additionalRows--;
}
}
else
{
//Move bottom cell to top
posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellPool[topMostCellIndex].sizeDelta.y;
_cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(_cellPool[bottomMostCellIndex].anchoredPosition.x, posY);
n++;
}
currentItemCount--;
//Cell for row at
DataSource.SetCell(_cachedCells[bottomMostCellIndex], currentItemCount - _cellPool.Count);
//set new indices
topMostCellIndex = bottomMostCellIndex;
bottomMostCellIndex = (bottomMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count;
}
if (IsGrid)
{
Content.sizeDelta += additionalRows * Vector2.up * _cellHeight;
//TODOL : check if it is supposed to be done only when > 0
if (additionalRows > 0)
{
n -= additionalRows;
}
}
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
Content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
_recycling = false;
return new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
}
#endregion
#region HELPERS
/// <summary>
/// Anchoring cell and content rect transforms to top preset. Makes repositioning easy.
/// </summary>
/// <param name="rectTransform"></param>
private void SetTopAnchor(RectTransform rectTransform)
{
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
float width = rectTransform.rect.width;
float height = rectTransform.rect.height;
//Setting top anchor
rectTransform.anchorMin = new Vector2(0.5f, 1);
rectTransform.anchorMax = new Vector2(0.5f, 1);
rectTransform.pivot = new Vector2(0.5f, 1);
//Reapply size
rectTransform.sizeDelta = new Vector2(width, height);
}
private void SetTopLeftAnchor(RectTransform rectTransform)
{
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
float width = rectTransform.rect.width;
float height = rectTransform.rect.height;
//Setting top anchor
rectTransform.anchorMin = new Vector2(0, 1);
rectTransform.anchorMax = new Vector2(0, 1);
rectTransform.pivot = new Vector2(0, 1);
//Reapply size
rectTransform.sizeDelta = new Vector2(width, height);
}
#endregion
#region TESTING
public void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawLine(_recyclableViewBounds.min - new Vector3(2000, 0), _recyclableViewBounds.min + new Vector3(2000, 0));
Gizmos.color = Color.red;
Gizmos.DrawLine(_recyclableViewBounds.max - new Vector3(2000, 0), _recyclableViewBounds.max + new Vector3(2000, 0));
}
#endregion
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 360d60b04d7e6a64ab1104c09a3335b0
timeCreated: 1595684786
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 50cfd769583befc46a5b6c78d9d6f4b8
folderAsset: yes
timeCreated: 1598985967
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,38 @@
//MIT License
//Copyright (c) 2020 Mohammed Iqubal Hussain
//Website : Polyandcode.com
using UnityEngine;
/// <summary>
/// Extension methods for Rect Transform
/// </summary>
public static class UIExtension
{
public static Vector3[] GetCorners(this RectTransform rectTransform)
{
Vector3[] corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
return corners;
}
public static float MaxY(this RectTransform rectTransform)
{
return rectTransform.GetCorners()[1].y;
}
public static float MinY(this RectTransform rectTransform)
{
return rectTransform.GetCorners()[0].y;
}
public static float MaxX(this RectTransform rectTransform)
{
return rectTransform.GetCorners()[2].x;
}
public static float MinX(this RectTransform rectTransform)
{
return rectTransform.GetCorners()[0].x;
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 12216d152b4b943e88a3d40b029c3e3e
timeCreated: 1580410439
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: