Как отфильтровать / найти список в Unity - PullRequest
0 голосов
/ 13 июня 2018

Я пытаюсь придумать способ фильтрации списка кнопок, которые я динамически создаю, на основе значений, зацикленных в базе данных SQlite.Каждая кнопка помечена именем студента, и может быть большое количество кнопок, следовательно, необходимо отфильтровать кнопки.

Я создаю свои кнопки так:

public RectTransform GridWithNameElements;
public GameObject StudentNamePrefabButton;

while (reader.Read())
            {
                //create a new button object and use the prefab button to make sure spacing etc is correct
                goButton = (GameObject)Instantiate(StudentNamePrefabButton);
                //set the parent of the button
                goButton.transform.SetParent(GridWithNameElements, false);
                goButton.transform.localScale = new Vector3(1, 1, 1);
                //set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
                goButton.GetComponentsInChildren<Text>()[0].text = reader["fullName"].ToString()+" | "+ reader["studentNumber"].ToString();
                goButton.name = reader["studentID"].ToString();

                Button tempButton = goButton.GetComponent<Button>();
                int tempInt = i;

                tempButton.onClick.AddListener(() => ButtonClicked(tempInt));

                i++;

                Debug.Log(goButton.name);
            }

Затем ясоздал поле ввода и прикрепил скрипт к onValueChanged в поле ввода и попытался написать скрипт.

//This method is called when the student attempts to search for their own name when taking the individual quiz
public void SearchExistingStudentName()
{

    //Get the name entered from the text field
    string student_name = searchExistingStudentNameInput.text;

    //Add names that exist to the new list
    List<string> names = new List<string>();
    names.Clear();
    //sets an id to the onclick listener of newly created buttons
    int x = 0;

    //if a new button object exists and the name was entered and then removed, clear it from the list and remove the created button
    if (student_name == "")
    {
        //Clear the filtered names List
        names.Clear();
        x = 0;
        //Destroy the create filter buttons if the user clears the text area
        GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
        //loop through the buttons that share the tag "filterButton"
        for (int count = 0; count < objects.Length; count++)
        {
            Destroy(objects[count]);

            Debug.Log("Number of objects to be deleted " + objects.Length);
        }

        //Loop through and show all children 
        foreach (Transform child in GridWithNameElements)
        {
            child.gameObject.SetActive(true);
        } 
    }
    else if (student_name != "")
    {
        int count = 0;
        int filteredNameCount = 0;
        int filteredIDCount = 1;
        //loop through the list of buttons with student names
        for (int i = 0; i < GridWithNameElements.childCount; i++)
        {
            Debug.Log("Children of grid "+GridWithNameElements.childCount);
            //Check if the user has typed their name with capitals or lowercase etc, and check the inputted value against names already in the list
            //If true, proceed 
            if (GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.Contains(student_name) || GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.ToLower().Contains(student_name) || GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.ToUpper().Contains(student_name))
            {

                //If the name entered contains letters found in the parent GridWithNameElements, then add them to the list array and their name value (unique id)
                names.Add(GridWithNameElements.GetComponentsInChildren<Text>()[i].text.Replace(@"[", string.Empty).Replace(@"]", string.Empty));
                names.Add(GridWithNameElements.GetChild(i).name); //this is the unique id of the student

                Debug.Log("Number of items in filtered names list " + names.Count);

                //Loop through and hide all children and hide them
                if (count == 0)
                {
                    foreach (Transform child in GridWithNameElements)
                    {
                        child.gameObject.SetActive(false);
                    }
                }

                count++;

                //Then create a button that represents a name added to the names List
                newButton = (GameObject)Instantiate(StudentNamePrefabButton);
                //set the parent of the button
                newButton.transform.SetParent(GridWithNameElements, false);
                newButton.transform.localScale = new Vector3(1, 1, 1);
                //set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
                newButton.GetComponentsInChildren<Text>()[filteredNameCount].text = names[filteredNameCount].ToString();
                newButton.name = names[filteredIDCount].ToString().Trim();
                newButton.tag = "filterButton";

                filteredNameCount++;
                filteredIDCount++;


                //Then add a click listener to the button
                Button tempButton = newButton.GetComponent<Button>();
                int tempInt = x;

                tempButton.onClick.AddListener(() => ButtonClicked(tempInt));

                x++;

                // Debug.Log("Student Unique ID " + newButton.name);


            }   
        }
        count = 0;
    }       
}

Я надеялся просто перебрать все элементы списка и скрыть те, которые нене соответствует поисковому запросу.Однако в настоящий момент мой цикл неверен, так как я получаю исключение за пределами границ (что, я думаю, связано с динамическим добавлением нового дочернего элемента при выполнении поиска).Я вижу это в журнале консоли, однако, программа выполняется должным образом (так что это проблема, но на данный момент не самая большая).

Я скрываю исходный список и показываю новый список на основе критериев соответствия.

Однако я всегда возвращаю только одно возможное имя, а не кучу возможных вариантов.Например, если бы у меня были имена Бен Джонс и Боб Дилан, основываясь на моем коде на данный момент, я только смогу вернуть Бена Джонса, а не Боба Дилана.

Я чувствую, что поступаю неправильноЯ хочу что-то похожее на this и не могу его воссоздать.Однако я пытаюсь выяснить, движусь ли я в правильном направлении или нет.

ОБНОВЛЕНИЕ

Я думаю, что нашел причину IndexOutOfRangeException: Array index is out of range. Это потому, что всякий раз, когда явведите букву, она вызывает метод SearchExistingStudentName().Это означает, что если введенная буква находится в одном из имен, она добавляется в список - только буква.Вот почему я могу вернуть только одно имя, а не список возможных имен.Поэтому я думаю, что оператор if нуждается в изменении, которое я сейчас пытаюсь рассмотреть.

Мне удалось сузить исключение массива в коде до следующего раздела:

//Loop through and hide all children and hide them
if (count == 0)
{
foreach (Transform child in GridWithNameElements)
  {
child.gameObject.SetActive(false);
  }
}

count++;

//Then create a button that represents a name added to the names List
newButton = (GameObject)Instantiate(StudentNamePrefabButton);
//set the parent of the button
newButton.transform.SetParent(GridWithNameElements, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
//set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
newButton.GetComponentsInChildren<Text>()[filteredNameCount].text = names[filteredNameCount].ToString();
newButton.name = names[filteredIDCount].ToString().Trim();
                    newButton.tag = "filterButton";

filteredNameCount++;
filteredIDCount++;

Ответы [ 2 ]

0 голосов
/ 15 июня 2018

В конце концов мне удалось решить это самостоятельно, и хотя это далеко не элегантное решение, оно работает.Сейчас я собираюсь поработать над улучшением самого кода, но для тех, кому может потребоваться отправная точка, я решил поделиться тем, что я сделал.

Как уже упоминалось в моем вопросе, я выкручиваю кучу кнопокна мой основной, нефильтрованный прокручиваемый список следующим образом.

while (reader.Read())
            {
                //create a new button object and use the prefab button to make sure spacing etc is correct
                GameObject goButton = (GameObject)Instantiate(StudentNamePrefabButton);
                //set the parent of the button
                goButton.transform.SetParent(GridWithNameElements, false);
                goButton.transform.localScale = new Vector3(1, 1, 1);
                //set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
                goButton.GetComponentsInChildren<Text>()[0].text = reader["fullName"].ToString() + " | " + reader["studentNumber"].ToString();
                goButton.name = reader["studentID"].ToString();

                Button tempButton = goButton.GetComponent<Button>();
                int tempInt = i;

                tempButton.onClick.AddListener(() => ButtonClicked(tempInt));

                i++;

                Debug.Log(goButton.name);
            }

Затем я создал пользователя inputfield в моем пользовательском интерфейсе, чтобы пользователь мог искать его имя.Затем прикрепил слушателя.Для этого перейдите в редактор Unity и присоедините ваш скрипт к методу On Value Changed.

Каждый раз, когда пользователь вводит ваш inputfield, вызывается ваш прикрепленный скрипт.

Затем я закончил тем, что сделал следующее - опять же, подчеркиваю, у меня только что получилось так, что это не идеальный пример, но, по крайней мере, отправная точка, и еще может быть больше ошибок, чтобы разобраться.Я надеюсь, что это кому-то поможет, так как я не нашел полных примеров, когда искал.

//This method is called when the student attempts to search for their own name when taking the individual quiz
public void SearchExistingStudentName()
{

    //Get the name entered from the text field
    string student_name = searchExistingStudentNameInput.text;

    //Create a list to store all the user names
    List<string> filteredNamesList = new List<string>();
    //Add another list to store the user unique ids
    List<string> filteredNameIDList = new List<string>();

    //Clear both lists before using them
    filteredNamesList.Clear();
    filteredNameIDList.Clear();
    //sets an id to the onclick listener of newly created buttons
    int x = 0;

    //if a new button object exists and the name was entered and then removed, clear it from the list and remove the created button
    if (student_name == "")
    {
        //Clear the filtered filteredNamesList List
        filteredNamesList.Clear();
        filteredNameIDList.Clear();
        x = 0;
        //Destroy the create filter buttons if the user clears the text area
        GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
        //loop through the buttons that share the tag "filterButton"
        for (int count = 0; count < objects.Length; count++)
        {
            Destroy(objects[count]);

            Debug.Log("Number of objects to be deleted " + objects.Length);
        }

        //Loop through and show all children 
        foreach (Transform child in GridWithNameElements)
        {
            child.gameObject.SetActive(true);
        }

        GridWithNameElements.gameObject.SetActive(true);
        GridWithNameElements2.gameObject.SetActive(false);
    }
    else if (student_name != "")
    {

        //loop through the list of buttons in the main/unfiltered list with student filteredNamesList
        for (int i = 0; i < GridWithNameElements.childCount; i++)
        {
            //Check if the user has typed their name with capitals or lowercase etc, and check the inputted value against filteredNamesList already in the list
            //If true, proceed 

            //Get the name value that contains entered character
            if (GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.Contains(student_name) || GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.ToLower().Contains(student_name) || GridWithNameElements
               .GetComponentsInChildren<Text>()[i].text.ToUpper().Contains(student_name))
            {

                //Do not allow duplicates to the list
                if (filteredNamesList.Distinct().Count() == filteredNamesList.Count())
                {
                    //If the name entered contains letters found in the parent GridWithNameElements, then add them to the list array and their name value (unique id)
                    filteredNamesList.Add(GridWithNameElements.GetComponentsInChildren<Text>()[i].text.Replace(@"[", string.Empty).Replace(@"]", string.Empty));
                    filteredNameIDList.Add(GridWithNameElements.GetChild(i).name); //this is the unique id of the student
                }

                //end of if statement   
            }
            //end of main loop
        }
        //hide original list
        GridWithNameElements.gameObject.SetActive(false);
        //show filtered list
        GridWithNameElements2.gameObject.SetActive(true);

        //Destroy the created filter buttons if the user presses another button, as previously 
        //added filteredNamesList might need to be ruled out. 
        GameObject[] objects = GameObject.FindGameObjectsWithTag("filterButton");
        //loop through the buttons that share the tag "filterButton"
        for (int count = 0; count < objects.Length; count++)
        {
            Destroy(objects[count]);

            Debug.Log("Number of objects to be deleted " + objects.Length);
        }

        int filteredNameIDCount = 0;

        foreach (string nameInList in filteredNamesList)
        {


            //Then create a button that represents a name added to the filteredNamesList List
            newButton = Instantiate(StudentNamePrefabButton);
            //set the parent of the button
            newButton.transform.SetParent(GridWithNameElements2, false);
            newButton.transform.localScale = new Vector3(1, 1, 1);
            //set the text of the button. Array value is 0 as the student name is always at position 0 on each iteration
            newButton.GetComponentsInChildren<Text>()[0].text = nameInList.ToString();
            newButton.name = filteredNameIDList[filteredNameIDCount].ToString().Trim();
            newButton.tag = "filterButton";

            Debug.Log("Filtered Name " + nameInList.ToString() + " Their ID " + filteredNameIDList[filteredNameIDCount].ToString().Trim());

            filteredNameIDCount++;

        }
        //end of loop


        //Then add a click listener to the button
        Button tempButton = newButton.GetComponent<Button>();
        int tempInt = x;

        tempButton.onClick.AddListener(() => ButtonClicked(tempInt));

        x++;

        // Debug.Log("Student Unique ID " + newButton.name);

    }

}
0 голосов
/ 13 июня 2018

Поскольку вы пометили это Unity3D, я предполагаю, что вы собираетесь использовать этот код в игре.Если это так, я бы не рекомендовал использовать LINQ, если тот же результат можно легко получить более традиционным способом.При этом я покажу оба способа сделать это.

List<string> names = GridWithNameElements.Where(nameInList => nameInList.Contains(input))

Выше приведены все имена, где nameInList.Contains(input) равно true.

В цикле вы бы сделали следующее.

List<string> names = new List<string>();
foreach (string nameInList in GridWithNameElements)
{
    if (nameInList.Contains(input)
    {
        names.Add(nameInList)
    }
}

Я не совсем уверен в типах переменных, но я думаю, что структура должна быть достаточно ясной.Не стесняйтесь спрашивать еще, если это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...