├── Editor
└── ScrollViewEditor.cs
├── GDropDown.cs
├── README.md
├── Samples.unity
├── Samples.unity.meta
├── Screenshot
├── 1.png
├── 2.png
├── 3.png
└── 4.png
└── ScrollView.cs
/Editor/ScrollViewEditor.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Unity Module By Garson(https://github.com/garsonlab)
3 | * -------------------------------------------------------------------
4 | * FileName: ScrollViewEditor
5 | * Date : 2018/08/11
6 | * Version : v1.0
7 | * Describe:
8 | */
9 | using UnityEditor;
10 | using UnityEditor.AnimatedValues;
11 | using UnityEngine;
12 | using UnityEngine.UI;
13 |
14 | [CustomEditor(typeof(ScrollView), true)]
15 | [CanEditMultipleObjects]
16 | public class ScrollViewEditor : Editor
17 | {
18 | SerializedProperty m_ViewPort;
19 | SerializedProperty m_Content;
20 | SerializedProperty m_Prefab;
21 |
22 | SerializedProperty m_MotionType;
23 | SerializedProperty m_MovementType;
24 | SerializedProperty m_Elasticity;
25 | SerializedProperty m_Inertia;
26 | SerializedProperty m_DecelerationRate;
27 | SerializedProperty m_ScrollSensitivity;
28 |
29 | SerializedProperty m_Margin;
30 | SerializedProperty m_ItemSize;
31 | SerializedProperty m_Space;
32 | SerializedProperty m_FixedCount;
33 | SerializedProperty m_NumItems;
34 | SerializedProperty m_Loop;
35 | SerializedProperty m_AutoAttach;
36 | SerializedProperty m_AttachSnap;
37 | SerializedProperty m_ScaleCurve;
38 | SerializedProperty m_ScaleAnimation;
39 |
40 | SerializedProperty m_HorizontalScrollbar;
41 | SerializedProperty m_VerticalScrollbar;
42 | SerializedProperty m_HorizontalScrollbarVisibility;
43 | SerializedProperty m_VerticalScrollbarVisibility;
44 | SerializedProperty m_HorizontalScrollbarSpacing;
45 | SerializedProperty m_VerticalScrollbarSpacing;
46 | SerializedProperty m_OnValueChanged;
47 | SerializedProperty m_OnItemRender;
48 | AnimBool m_ShowElasticity;
49 | AnimBool m_ShowDecelerationRate;
50 | AnimBool m_ShowAutoAttach;
51 | AnimBool m_ShowScaleCurve;
52 | bool m_ViewportIsNotChild, m_HScrollbarIsNotChild, m_VScrollbarIsNotChild;
53 | static string s_HError = "For this visibility mode, the Viewport property and the Horizontal Scrollbar property both needs to be set to a Rect Transform that is a child to the Scroll View.";
54 | static string s_VError = "For this visibility mode, the Viewport property and the Vertical Scrollbar property both needs to be set to a Rect Transform that is a child to the Scroll View.";
55 |
56 | bool m_ScrollSetting;
57 | bool m_ScrollbarSetting;
58 | bool m_EventSetting;
59 |
60 | static string s_ScrollSettingLabel = "Scroll Setting";
61 | static string s_ScrollbarSettingLabel = "Scrollbar Setting";
62 | static string s_EventSettingLabel = "Event Setting";
63 | static string s_WelcomInfo = "Welcom to use Infinite and Loop List";
64 | static string s_ViewportNull = "Infinite and Loop List Error:\n Viewport property is Null";
65 | static string s_ContentNull = "Infinite and Loop List Error:\n Content property is Null";
66 | static string s_PrefabtNull = "Infinite and Loop List Warning:\n Prefab property is Null";
67 |
68 | protected virtual void OnEnable()
69 | {
70 | m_Content = serializedObject.FindProperty("m_Content");
71 | m_ViewPort = serializedObject.FindProperty("m_ViewPort");
72 | m_Prefab = serializedObject.FindProperty("m_Prefab");
73 | m_MotionType = serializedObject.FindProperty("m_MotionType");
74 |
75 | m_MovementType = serializedObject.FindProperty("m_MovementType");
76 | m_Elasticity = serializedObject.FindProperty("m_Elasticity");
77 | m_Inertia = serializedObject.FindProperty("m_Inertia");
78 | m_DecelerationRate = serializedObject.FindProperty("m_DecelerationRate");
79 | m_ScrollSensitivity = serializedObject.FindProperty("m_ScrollSensitivity");
80 |
81 | m_Margin = serializedObject.FindProperty("m_Margin");
82 | m_FixedCount = serializedObject.FindProperty("m_FixedCount");
83 | m_ItemSize = serializedObject.FindProperty("m_ItemSize");
84 | m_Space = serializedObject.FindProperty("m_Space");
85 | m_NumItems = serializedObject.FindProperty("m_NumItems");
86 | m_Loop = serializedObject.FindProperty("m_Loop");
87 | m_AutoAttach = serializedObject.FindProperty("m_AutoAttach");
88 | m_AttachSnap = serializedObject.FindProperty("m_AttachSnap");
89 | m_ScaleCurve = serializedObject.FindProperty("m_ScaleCurve");
90 | m_ScaleAnimation = serializedObject.FindProperty("m_ScaleAnimation");
91 |
92 | m_HorizontalScrollbar = serializedObject.FindProperty("m_HorizontalScrollbar");
93 | m_VerticalScrollbar = serializedObject.FindProperty("m_VerticalScrollbar");
94 | m_HorizontalScrollbarVisibility = serializedObject.FindProperty("m_HorizontalScrollbarVisibility");
95 | m_VerticalScrollbarVisibility = serializedObject.FindProperty("m_VerticalScrollbarVisibility");
96 | m_HorizontalScrollbarSpacing = serializedObject.FindProperty("m_HorizontalScrollbarSpacing");
97 | m_VerticalScrollbarSpacing = serializedObject.FindProperty("m_VerticalScrollbarSpacing");
98 | m_OnValueChanged = serializedObject.FindProperty("m_OnValueChanged");
99 | this.m_OnItemRender = serializedObject.FindProperty("m_OnItemRender");
100 |
101 | m_ShowElasticity = new AnimBool(Repaint);
102 | m_ShowDecelerationRate = new AnimBool(Repaint);
103 | m_ShowAutoAttach = new AnimBool(Repaint);
104 | m_ShowScaleCurve = new AnimBool(Repaint);
105 | SetAnimBools(true);
106 |
107 | m_ScrollSetting = true;
108 | m_ScrollbarSetting = true;
109 | m_EventSetting = true;
110 | }
111 |
112 | protected virtual void OnDisable()
113 | {
114 | m_ShowElasticity.valueChanged.RemoveListener(Repaint);
115 | m_ShowDecelerationRate.valueChanged.RemoveListener(Repaint);
116 | m_ShowAutoAttach.valueChanged.RemoveListener(Repaint);
117 | m_ShowScaleCurve.valueChanged.RemoveListener(Repaint);
118 | }
119 |
120 | void SetAnimBools(bool instant)
121 | {
122 | SetAnimBool(m_ShowElasticity, !m_MovementType.hasMultipleDifferentValues && m_MovementType.enumValueIndex == (int)ScrollView.MovementType.Elastic, instant);
123 | SetAnimBool(m_ShowDecelerationRate, !m_Inertia.hasMultipleDifferentValues && m_Inertia.boolValue == true, instant);
124 |
125 | SetAnimBool(m_ShowAutoAttach, m_AutoAttach.boolValue, instant);
126 | SetAnimBool(m_ShowScaleCurve, m_ScaleCurve.boolValue, instant);
127 | }
128 |
129 | void SetAnimBool(AnimBool a, bool value, bool instant)
130 | {
131 | if (instant)
132 | a.value = value;
133 | else
134 | a.target = value;
135 | }
136 |
137 | void CalculateCachedValues()
138 | {
139 | m_ViewportIsNotChild = false;
140 | m_HScrollbarIsNotChild = false;
141 | m_VScrollbarIsNotChild = false;
142 | if (targets.Length == 1)
143 | {
144 | Transform transform = ((ScrollView)target).transform;
145 | if (m_ViewPort.objectReferenceValue == null || ((RectTransform)m_ViewPort.objectReferenceValue).transform.parent != transform)
146 | m_ViewportIsNotChild = true;
147 | if (m_HorizontalScrollbar.objectReferenceValue == null || ((Scrollbar)m_HorizontalScrollbar.objectReferenceValue).transform.parent != transform)
148 | m_HScrollbarIsNotChild = true;
149 | if (m_VerticalScrollbar.objectReferenceValue == null || ((Scrollbar)m_VerticalScrollbar.objectReferenceValue).transform.parent != transform)
150 | m_VScrollbarIsNotChild = true;
151 | }
152 | }
153 |
154 | public override void OnInspectorGUI()
155 | {
156 | SetAnimBools(false);
157 |
158 | serializedObject.Update();
159 | // Once we have a reliable way to know if the object changed, only re-cache in that case.
160 | CalculateCachedValues();
161 |
162 | if(m_ViewPort.objectReferenceValue == null )
163 | EditorGUILayout.HelpBox(s_ViewportNull, MessageType.Error);
164 | else if (m_Content.objectReferenceValue == null)
165 | EditorGUILayout.HelpBox(s_ContentNull, MessageType.Error);
166 | else if (m_Prefab.objectReferenceValue == null)
167 | EditorGUILayout.HelpBox(s_PrefabtNull, MessageType.Warning);
168 | else
169 | EditorGUILayout.HelpBox(s_WelcomInfo, MessageType.Info);
170 |
171 |
172 | EditorGUILayout.PropertyField(m_ViewPort);
173 | EditorGUILayout.PropertyField(m_Content);
174 | EditorGUILayout.PropertyField(m_Prefab);
175 | EditorGUILayout.Space();
176 |
177 | #region ScrollSetting
178 | m_ScrollSetting = EditorGUILayout.Foldout(m_ScrollSetting, s_ScrollSettingLabel);
179 | if (m_ScrollSetting)
180 | {
181 | EditorGUILayout.PropertyField(m_MotionType);
182 | EditorGUILayout.PropertyField(m_MovementType);
183 | if (EditorGUILayout.BeginFadeGroup(m_ShowElasticity.faded))
184 | {
185 | EditorGUI.indentLevel++;
186 | EditorGUILayout.PropertyField(m_Elasticity);
187 | EditorGUI.indentLevel--;
188 | }
189 | EditorGUILayout.EndFadeGroup();
190 |
191 | EditorGUILayout.PropertyField(m_Inertia);
192 | if (EditorGUILayout.BeginFadeGroup(m_ShowDecelerationRate.faded))
193 | {
194 | EditorGUI.indentLevel++;
195 | EditorGUILayout.PropertyField(m_DecelerationRate);
196 | EditorGUI.indentLevel--;
197 | }
198 | EditorGUILayout.EndFadeGroup();
199 |
200 | EditorGUILayout.PropertyField(m_ScrollSensitivity);
201 | }
202 | #endregion
203 |
204 | EditorGUILayout.Space();
205 |
206 | EditorGUILayout.PropertyField(m_Margin, true);
207 | EditorGUILayout.PropertyField(m_ItemSize, true);
208 | EditorGUILayout.PropertyField(m_Space, true);
209 | EditorGUILayout.Space();
210 |
211 | EditorGUILayout.PropertyField(m_FixedCount);
212 | EditorGUILayout.PropertyField(m_NumItems);
213 | EditorGUILayout.Space();
214 |
215 | EditorGUILayout.PropertyField(m_Loop);
216 | EditorGUILayout.PropertyField(m_AutoAttach);
217 | if (EditorGUILayout.BeginFadeGroup(m_ShowAutoAttach.faded))
218 | {
219 | EditorGUILayout.PropertyField(m_AttachSnap, true);
220 | }
221 | EditorGUILayout.EndFadeGroup();
222 |
223 |
224 | EditorGUILayout.PropertyField(m_ScaleCurve);
225 | if (EditorGUILayout.BeginFadeGroup(m_ShowScaleCurve.faded))
226 | {
227 | EditorGUILayout.PropertyField(m_ScaleAnimation, true);
228 | }
229 | EditorGUILayout.EndFadeGroup();
230 |
231 | #region ScrollBar
232 | EditorGUILayout.Space();
233 | m_ScrollbarSetting = EditorGUILayout.Foldout(m_ScrollbarSetting, s_ScrollbarSettingLabel);
234 | if (m_ScrollbarSetting)
235 | {
236 | EditorGUILayout.PropertyField(m_HorizontalScrollbar);
237 | if (m_HorizontalScrollbar.objectReferenceValue && !m_HorizontalScrollbar.hasMultipleDifferentValues)
238 | {
239 | EditorGUI.indentLevel++;
240 | EditorGUILayout.PropertyField(m_HorizontalScrollbarVisibility, new GUIContent("Visibility"));
241 |
242 | if ((ScrollView.ScrollbarVisibility) m_HorizontalScrollbarVisibility.enumValueIndex ==
243 | ScrollView.ScrollbarVisibility.AutoHideAndExpandViewport
244 | && !m_HorizontalScrollbarVisibility.hasMultipleDifferentValues)
245 | {
246 | if (m_ViewportIsNotChild || m_HScrollbarIsNotChild)
247 | EditorGUILayout.HelpBox(s_HError, MessageType.Error);
248 | EditorGUILayout.PropertyField(m_HorizontalScrollbarSpacing, new GUIContent("Spacing"));
249 | }
250 |
251 | EditorGUI.indentLevel--;
252 | }
253 |
254 | EditorGUILayout.PropertyField(m_VerticalScrollbar);
255 | if (m_VerticalScrollbar.objectReferenceValue && !m_VerticalScrollbar.hasMultipleDifferentValues)
256 | {
257 | EditorGUI.indentLevel++;
258 | EditorGUILayout.PropertyField(m_VerticalScrollbarVisibility, new GUIContent("Visibility"));
259 |
260 | if ((ScrollView.ScrollbarVisibility) m_VerticalScrollbarVisibility.enumValueIndex ==
261 | ScrollView.ScrollbarVisibility.AutoHideAndExpandViewport
262 | && !m_VerticalScrollbarVisibility.hasMultipleDifferentValues)
263 | {
264 | if (m_ViewportIsNotChild || m_VScrollbarIsNotChild)
265 | EditorGUILayout.HelpBox(s_VError, MessageType.Error);
266 | EditorGUILayout.PropertyField(m_VerticalScrollbarSpacing, new GUIContent("Spacing"));
267 | }
268 |
269 | EditorGUI.indentLevel--;
270 | }
271 | }
272 |
273 | #endregion
274 |
275 | #region Event
276 | EditorGUILayout.Space();
277 | m_EventSetting = EditorGUILayout.Foldout(m_EventSetting, s_EventSettingLabel);
278 | if (m_EventSetting)
279 | {
280 | EditorGUILayout.PropertyField(m_OnValueChanged, true);
281 | EditorGUILayout.PropertyField(this.m_OnItemRender, true);
282 | }
283 |
284 | #endregion
285 |
286 | serializedObject.ApplyModifiedProperties();
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/GDropDown.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Unity Module By Garson(https://github.com/garsonlab)
3 | * -------------------------------------------------------------------
4 | * FileName: GDropDown
5 | * Date : 2018/08/11
6 | * Version : v1.0
7 | * Describe: 下拉列表, 支持无限循环滚动列表
8 | */
9 | using System;
10 | using System.Collections.Generic;
11 | using UnityEngine.Events;
12 |
13 | namespace UnityEngine.UI
14 | {
15 | [AddComponentMenu("UI/GDropdown", 100)]
16 | [RequireComponent(typeof (RectTransform))]
17 | [DisallowMultipleComponent]
18 | public class GDropDown : MonoBehaviour
19 | {
20 | #region Template
21 | [SerializeField]
22 | private Toggle m_CaptionToggle;
23 | ///
24 | /// Button of Whole Component
25 | ///
26 | public Toggle CaptionToggle { get { return m_CaptionToggle; } set { SetCaptionButton(value);} }
27 |
28 | [Tooltip("Display Text of Selected Item")]
29 | [SerializeField]
30 | private Text m_CaptionText;
31 | ///
32 | /// Display Text of Selected Item
33 | ///
34 | public Text CaptionText { get { return m_CaptionText; } set { m_CaptionText = value; } }
35 |
36 | [Tooltip("Display Image of Selected Item")]
37 | [SerializeField]
38 | private Image m_CaptionImage;
39 | ///
40 | /// Display Image of Selected Item
41 | ///
42 | public Image CaptionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; } }
43 | [Tooltip("Flag is Open List")]
44 | [SerializeField]
45 | private Transform m_Flag;
46 | private float m_defaultAngle;
47 | #endregion
48 | [Space]
49 | #region Other
50 | [SerializeField]
51 | private ScrollView m_ScrollView;
52 |
53 | [SerializeField]
54 | private int m_SelectIndex;
55 | ///
56 | /// Current Select Index
57 | ///
58 | public int selectIndex { get { return m_SelectIndex; } set { SetSelectIndex(value); }}
59 | ///
60 | /// Current Select Value
61 | ///
62 | public string selectValue
63 | {
64 | get { return m_DropData.Count > m_SelectIndex ? m_DropData[m_SelectIndex].text : ""; }
65 | set
66 | {
67 | int count = m_DropData.Count;
68 | for (int i = 0; i < count; i++)
69 | {
70 | if (m_DropData[i].text == value)
71 | {
72 | SetSelectIndex(i);
73 | break;
74 | }
75 | }
76 | }
77 | }
78 |
79 | ///
80 | /// Drop Down Value Changed, Used to Lua
81 | ///
82 | public Action OnValueChanged;
83 |
84 | [SerializeField]
85 | List m_DropData = new List();
86 |
87 | [Serializable]
88 | public class GDropDownEvent : UnityEvent { }
89 | public GDropDownEvent onValueChanged = new GDropDownEvent();
90 |
91 | private Dictionary m_Items = new Dictionary();
92 | private RectTransform m_PointerMask;
93 | private Transform m_Canvas;
94 | #endregion
95 |
96 | void Awake()
97 | {
98 | if (m_Flag)
99 | m_defaultAngle = m_Flag.localEulerAngles.z;
100 | if (m_ScrollView)
101 | m_ScrollView.onItemRender.AddListener(OnItemRender);
102 | SetCaptionButton(m_CaptionToggle);
103 | m_CaptionToggle.isOn = false;
104 | SetSelectIndex(m_SelectIndex);
105 | CloseMask();
106 | }
107 |
108 | public void AddOptions(GDropData[] options)
109 | {
110 | for (int i = 0; i < options.Length; i++)
111 | this.m_DropData.Add(options[i]);
112 |
113 | if (m_CaptionToggle.isOn)
114 | OpenMask();
115 | }
116 |
117 | public void AddOptions(Sprite[] options)
118 | {
119 | for (int i = 0; i < options.Length; i++)
120 | this.m_DropData.Add(new GDropData(options[i]));
121 |
122 | if (m_CaptionToggle.isOn)
123 | OpenMask();
124 | }
125 |
126 | public void AddOptions(string[] options)
127 | {
128 | for (int i = 0; i < options.Length; i++)
129 | this.m_DropData.Add(new GDropData(options[i]));
130 |
131 | if (m_CaptionToggle.isOn)
132 | OpenMask();
133 | }
134 |
135 | public void RemoveAt(int index)
136 | {
137 | if(index < 0 || index >= m_DropData.Count)
138 | return;
139 | m_DropData.RemoveAt(index);
140 | if (index == m_SelectIndex)
141 | {
142 | SetSelectIndex(index - 1);
143 | }
144 | }
145 |
146 | public void RemoveValue(string value)
147 | {
148 | int index = -1;
149 | int count = m_DropData.Count;
150 | for (int i = 0; i < count; i++)
151 | {
152 | if (m_DropData[i].text == value)
153 | {
154 | index = i;
155 | break;
156 | }
157 | }
158 | if (index >= 0)
159 | {
160 | RemoveAt(index);
161 | }
162 | }
163 |
164 | public void ClearOptions()
165 | {
166 | this.m_DropData.Clear();
167 | if (m_CaptionToggle.isOn)
168 | OpenMask();
169 | }
170 |
171 |
172 | ///
173 | /// Refresh Display View
174 | ///
175 | private void RefreshShowValue()
176 | {
177 | if (m_DropData.Count > m_SelectIndex)
178 | {
179 | GDropData data = m_DropData[m_SelectIndex];
180 | if (m_CaptionText)
181 | m_CaptionText.text = data.text;
182 | if (m_CaptionImage)
183 | m_CaptionImage.sprite = data.image;
184 | }
185 | else
186 | {
187 | if (m_CaptionText)
188 | m_CaptionText.text = "";
189 | if (m_CaptionImage)
190 | m_CaptionImage.sprite = null;
191 | }
192 | }
193 |
194 | ///
195 | /// Render Item in List
196 | ///
197 | ///
198 | ///
199 | private void OnItemRender(int index, Transform child)
200 | {
201 | GItem item;
202 | if (!m_Items.TryGetValue(child, out item))
203 | item = new GItem(this, child);
204 |
205 | if (m_DropData.Count > index)
206 | {
207 | item.Reset(m_DropData[index].text, m_DropData[index].image, index == m_SelectIndex);
208 | }
209 | }
210 |
211 | ///
212 | /// Set Cur Select Index When Click
213 | ///
214 | ///
215 | private void SetSelectIndex(int p)
216 | {
217 | p = Mathf.Clamp(p, 0, m_DropData.Count);
218 | m_SelectIndex = p;
219 | RefreshShowValue();
220 | onValueChanged.Invoke(p);
221 | if (OnValueChanged != null)
222 | OnValueChanged(p);
223 | m_CaptionToggle.isOn = false;
224 | }
225 |
226 | ///
227 | /// Open Mask to Poniters Out of List
228 | ///
229 | private void OpenMask()
230 | {
231 | if (m_PointerMask == null) //Create Mask
232 | {
233 | GameObject o = new GameObject("Pointer Mask");
234 | o.transform.SetParent(transform);
235 | Image mask = o.AddComponent();
236 | mask.color = new Color(1, 1, 1, 0);
237 | m_PointerMask = o.transform as RectTransform;
238 | m_PointerMask.sizeDelta = new Vector2(Screen.width, Screen.height);
239 | Button btnMask = o.AddComponent