I'm creating a Dropdown
with a searchbar on top. Basically, I put an Inputfield
on top of the Dropdown
.
When you click the Inputfield
, the Dropdown
opens and when you type something into it, the Dropdown
should update its list dynamically.
Here's the problem I'm having, the Dropdown
option changes in the inspector, however, it doesn't update the list in the scene.
When I do Dropdown.Hide()
and then Dropdown.Show()
, it updates but then I lose focus on the Inputfield
. I can always just do Inputfield.Select()
but then the whole Inputfield
gets highlighted and you have to click the position you were at to be able to edit from where you were.
Any suggestions?
Edit Here is the code below. I took out all the unimportant things to make it as slim as possible to show here
public class SearchbarInput : InputField
{
private GameObject[] m_Ordnance;
private Dropdown m_Dropdown;
private SearchbarInput m_InputField;
// Needed for basic inputfield functionality
private List<string> m_OrdnanceNames = new List<string>(); // Used to display our data
protected override void Start()
{
m_Ordnance = GetComponent<OrdnanceSelector>().m_Ordnance;
// Get necessary components
m_Dropdown = GetComponentInParent<Dropdown>();
m_InputField = GetComponent<SearchbarInput>();
m_InputField.gameObject.GetComponentInChildren<Text>().text = "Click here to select Ordnance";
// Set InputField onValueChange listener
m_InputField.onValueChanged.AddListener(OnInputValueChanged);
// Add each ordnance name to our string list
foreach (GameObject ordnance in m_Ordnance)
{
if (ordnance != null) m_OrdnanceNames.Add(ordnance.name);
}
if (m_OrdnanceNames.Count == 0) DisplayError("Ordnance were not added");
else
{
ChangeDropdownOptions(m_OrdnanceNames);
m_Dropdown.onValueChanged.AddListener((index) => OnDropdownItemClicked(index));
}
base.Start();
}
// When the InputField is selected
public override void OnSelect(BaseEventData eventData)
{
base.OnSelect(eventData);
Debug.Log("SearchbarInput selected");
Dropdown parentDropdown = GetComponentInParent<Dropdown>();
parentDropdown.Show();
}
// When the InputField is deselected
public override void OnDeselect(BaseEventData eventData)
{
base.OnDeselect(eventData);
Debug.Log("SearchbarInput deselected");
}
/// Displays items in list that are similar to what the user typed in the Input Field
private void OnInputValueChanged(string typedText)
{
List<string> results = GetResults(typedText);
ChangeDropdownOptions(results);
}
/// Get list of items that contains characters similar to input
private List<string> GetResults(string input)
{
return m_OrdnanceNames.FindAll((str) => str.IndexOf(input) >= 0);
}
///============================== Dropdown Methods===================================
/// Called when the dropdown menu is clicked. Is set inside of scripts Start function
public void OnDropdownItemClicked(int index)
{
// Get selected ordnance name
string ordnanceName = m_Dropdown.options[index].text;
m_InputField.text = ordnanceName;
// Change AndyGenerator Prefab
int indexOfOrdnance = m_OrdnanceNames.IndexOf(ordnanceName);
//m_AndyScript.AndyPrefab = m_Ordnance[indexOfOrdnance]; <- Took out for StackOverflow to make as short as possible
}
/// Clears the dropdown options and add's options set inside of the list
private void ChangeDropdownOptions(List<string> options)
{
m_Dropdown.ClearOptions();
m_Dropdown.AddOptions(options);
}
///============================== Error Method===================================
/// Displays error inside of our InputField.
private void DisplayError(string errorText)
{
Debug.Log("Searchbar.cs: " + errorText);
// Decided to make it more obvious since this is absolutely needed and
// it saves the headache of looking for an error
m_InputField.text = "Error: " + errorText;
}
}
To make sure the dropdown options list UI is up to date, you need to disable and enable back the component.
Unfortunately, this makes the dropdown "flicker". Look at the end for a "no-flicker" solution.
Here, I'm using a TextMeshPro dropdown.
Dropdown.ClearOptions();
Dropdown.AddOptions(options);
Dropdown.RefreshOptions();
InputField.Input.ActivateInputField();
With the following extension method:
/// <summary>
/// Call this after modifying options while the dropdown is displayed
/// to make sure the visual is up to date.
/// </summary>
public static void RefreshOptions(this TMPro.TMP_Dropdown dropdown)
{
dropdown.enabled = false;
dropdown.enabled = true;
dropdown.Show();
}
EDIT :
I found a way to achieve this without any flickering. This involves getting rid of the Dropdown
script and writing your own.
Basically, I took the same UI as for the dropdown, but I placed a VerticalLayoutGroup
in the content of the ScrollRect
.
I then wrote a custom script that:
ScrollRect
and fill the Layout with all the options when InputField.onSelect
is triggeredInputField.onValueChange
is triggered (with throttle)ScrollRect
when InputField.onEndEdit
is triggeredAnd there is some important changes that also need to be made:
ScrollRect
should have a Canvas script with a dedicated Sort Order to be on top of the UIScrollRect
should have a GraphicsRaycaster
component to be able to select the options in the LayoutScrollRect
on onEndEdit
should be done with a delay. Otherwise the click event won't be evaluated by the buttons in your Layout.I dont know that answer, but I have a similar problem... I got this search bar working, with a dropdown showing dynamic values, but this dropdown only updates on the odd chars (on the first, third... not on the second, fourth...)
Here:
//call whenever the input field changes, even OR odd, its working
public void newSearchFieldValueChanged()
{
//read the input field, ok...
searchText = newSearchField.text;
//return when empty...
if (string.IsNullOrEmpty(searchText)) return;
//I need to hide the dropdown
dropdown.Hide();
//clear its old options
dropdown.ClearOptions();
//this is a dictionary to fill the dropdown options, clear it
dicTemp.Clear();
//add a first empty value
dicTemp.Add("", "0");
//so I run for another dic, that dont change its original values
for (int i = 0; i < dic.Keys.Count; i++)
{
//if it contains in its keys the word typed in the search bar...
if (dic.Keys.ElementAt(i).ToLower().Contains(searchText.ToLower()))
{
//I add it to the cleared dicTemp that will fill the dropdown options
dicTemp.Add(dic.Keys.ElementAt(i), dic.Values.ElementAt(i));
}
}
//fill the dropdown options with the new dicTemp, each time something changes
dropdown.AddOptions(dicTemp.Keys.ToList());
//duh
dropdown.Show();
//keep the focus on input field to continue type (dropdown selected by mouse)
newSearchField.ActivateInputField();
}
Again, it works on first letter, and third... but not on second, and fourth, the dropdown DOES NOT SHOW UP (besides the function IS CALLED everytime)...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With