Выпадающий список / Popup list [Unity UI]

Для тех, кто начинает изучать Unity и элементы UI в частности, такая, казалось бы, простая вещь как выпадающий список, может оказаться проблемой. Как сделать список и область прокрутки? Мы не будем заморачиваться одним лишь списком, а сразу будем прикручивать скролл окошко, для нашего списка. Более того, напишем скрипт, который и будет создавать меню на основе заданных шаблонов. То есть, сделаем отдельные элементы выпадающего меню/списка: окно, скроллбар, кнопка. В зависимости от количества нужных элементов, скрипт автоматически рассчитает размеры и позиции объектов, плюс, он будет хранить информацию о текущем элементе.

Чтож, переключаем окошко редактора в режим 2D и переходим к редактированию элементов UI.

Добавляем на сцену кнопку, настраиваем внешний вид и надпись по умолчанию, это будет кнопка вызова списка, переименуем ее в Element. Теперь, добавляем объект картинку, переименуем в ScrollRect, по ширине ее делаем такой-же как и кнопка, по высоте на усмотрение, это окошко списка, разместим его ниже кнопки, чтобы список появлялся под ней. Добавляем скроллбар в нем переключаем Direction > Bottom To Top, получается вертикальный скролл, по высоте делаем его таким же как окошко и размещаем рядом. Примерно вот так получается:

Выпадающий список / Popup list [Unity UI]

Далее, нужно сделать пустой UI объект, его можно сделать из любого другого, просто удалите всё, кроме Rect Transform. Назовем этот объект Cuntent, делаем дочерним к ScrollRect, и сбрасываем параметры (кнопка в верхнем правом углу > Reset).

Продолжаем редактировать ScrollRect. На него нужно прицепить дополнительные компоненты Add Component > UI, а именно Scroll Rect и Mask. В Mask убираем галочку Show Graphic, чтобы маска была прозрачной. А Scroll Rect настраиваем как показано на скриншоте:


То есть, тут настройки для вертикального скролла. Не забываем указать объекты Cuntent и Scrollbar.

Создаем еще один пустой UI, под именем PopupList, делаем его по размеру кнопки и ставим на туже позицию, копируем Rect Transform проще говоря. Выделаем наши заготовки - Element, ScrollRect, Scrollbar - и делаем дочерним к PopupList. Выключаем объекты ScrollRect и Scrollbar. Теперь, на сцене только PopupList, можно без проблем перетаскивать на любое место, где нужен список.

На PopupList вешаем скрипт с таким же именем:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;

public class PopupList : MonoBehaviour {

	public RectTransform element;
	public RectTransform content;
	public RectTransform scrollbar;
	public RectTransform scrollRect;

	public string[] list;
	
	private Vector2 delta;
	private Vector2 e_Pos;
	private bool open;
	private static int id = -1; // по умолчанию, не один из элементов - не выбран

	public static int currentID
	{
		get{return id;}
	}
	
	void Awake()
	{
		delta = element.sizeDelta;
		scrollRect.gameObject.SetActive(true);
		scrollbar.gameObject.SetActive(true);
	}

	void Start()
	{
		BuildList(); // запуск создания списка
	}

	public void SetElement(int e)
	{
		open = !open;
		scrollRect.gameObject.SetActive(open);
		scrollbar.gameObject.SetActive(open);
		if(e != id && e >= 0)
		{
			id = e;
			element.GetComponentInChildren<Text>().text = list[e];
		}
	}

	void ButtonSetup(Button button, int value) 
	{
		button.onclick.AddListener(() => SetElement(value)); // установка функции onclick кнопки
	}

	void LateUpdate()
	{
		if(open)
		{
			if(!EventSystem.current.currentSelectedGameObject)
			{
				SetElement(-1);
			}
			else if(!EventSystem.current.currentSelectedGameObject.GetComponentInParent<PopupList>())
			{
				SetElement(-1);
			}
		}
	}

	void BuildList()
	{
		BuildWindow();
		float tmp = delta.y;
		bool start = true;

		for(int j = 0; j < list.Length; j++)
		{
			RectTransform clone = Instantiate(element) as RectTransform;
			clone.SetParent(content);
			if(start)
			{
				clone.anchoredPosition = new Vector2(e_Pos.x, e_Pos.y);
				start = false;
			}
			else
			{
				clone.anchoredPosition = new Vector2(e_Pos.x, e_Pos.y - tmp);
				tmp += delta.y;
			}
			clone.GetComponentInChildren<Text>().text = list[j];
			ButtonSetup(clone.GetComponent<Button>(), j);
		}

		ButtonSetup(element.GetComponent<Button>(), id);
		scrollRect.gameObject.SetActive(false);
		scrollbar.gameObject.SetActive(false);
	}

	void BuildWindow()
	{
		float height = delta.y * list.Length;
		content.sizeDelta = new Vector2(delta.x, height);
		content.anchoredPosition = new Vector2(0, -height / 2);
		e_Pos = new Vector2(0, (height / 2) - (delta.y / 2));
	}
}

С помощью простых формул просчитывается размер окна контента и позиции кнопок, которые внутри него.

list - массив списка, на его основе идет расчет.

currentID - публичная переменная, содержит id выбранного элемента массива.

В функции LateUpdate идет проверка, если список развернут и - клик по пустой области экрана или по другому объекту - то, список будет закрыт.

Если нужно по умолчанию назначить какой-либо элимент списка, то после создания менюшки, добавляем пару строк, например:

void Start()
{
	BuildList(); 
	open = !open;
        SetElement(10);
}


Скачать и пощупать, можно тут:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Комментариев 3

Офлайн
Ark
Ark 5 марта 2017
На юнити 5.5.1f1 и при режиме канваса на камеру данный скрипт ведет себя не адекватно к сожалению(
Офлайн
Light 5 марта 2017
Ark, в чем суть проблемы, какие настройки были сделаны, в чем выражена неадекватность?
Офлайн
Falkonio 9 сентября 2018
При переносе скрипта и принципа (я переделал на панель с GridLayoutGroup вместо скрола, чтобы выбирать цвета) на другой канвас может появиться проблема: клоны кнопок получают масштаб и расположение совершенно неадекватные. Чтобы этого избежать, надо использовать перегруженную версию инстантиэйта:

RectTransform clone = Instantiate(element, content) as RectTransform;
//clone.SetParent(content);

При создании сразу в родителе такого не происходит.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика