如何在Unity中过滤/搜索列表

时间:2018-06-13 00:09:43

标签: c# unity3d filtering

我正在尝试根据从SQlite DB循环的值来过滤我动态创建的按钮列表的方法。每个按钮都标有学生姓名,可能有大量按钮,因此需要过滤按钮。

我按钮创建我的按钮:

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;
    }       
}

我希望简单地遍历所有列表项,并隐藏那些与搜索查询不匹配的项。但是,目前,我的循环是错误的,因为我得到一个越界异常(我认为这与在搜索时动态添加新的子元素有关)。我在控制台日志中看到了这一点,然而,程序按预期执行(所以这是一个问题,但目前不是最大的问题)。

我隐藏原始列表并根据匹配条件显示新列表。

然而,我只返回一个可能的名字,而不是一堆可能的选择。例如,如果我的名字是Ben Jones和Bob Dylan,根据我的代码,我只能返回Ben Jones而不是Bob Dylan。

我觉得我这样做是错误的,因为我想要类似于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 :(得分:1)

由于您已使用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)
    }
}

我对变量的类型并不完全有信心,但我认为结构应该足够清晰。如果不是,请随意再询问一下。

答案 1 :(得分:0)

最终管理自己解决这个问题,虽然它远非一个优雅的解决方案,但它确实有效。我现在正致力于改进代码本身,但对于那些可能需要起点的人,我想我会分享我所做的。

正如我在问题中所提到的,我将一堆按钮循环到我的主要未经过滤的可滚动列表上。

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);

    }

}