I gave you problem a hard think this afternoon during a long drive, and came up with a method that’s not too bad. The normal entries in the list would be UIListButtons. The Headers would be UIListContainers with each having a UIListButton as a child. This would allow the code to move the header UIListButtons through their localPosition while leaving the parents in place. This solution does not require parallel graphics as I suggest you use above. Here is a lightly tested body of code that implements the idea. I’m sure there are improvements to be made, but it is a proof of concept. Note most of the code is setting up the list. The code that handles the header movement (or non-movement) is small.
// Note: This script must execute after EZGUI List movement code.
// Use Edit/Project Settings/Script Execution Order if you need it.
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HeaderList : MonoBehaviour {
public TextAsset ta; // Text file with a sorted list of strings
public UIScrollList uisl; // Scroll list
public GameObject goPrefabEntry; // Prefab for an entry
public GameObject goPrefabHeader; // Prefab for a header
private Vector3 v3Header; // Position of the header of the list
private List<Transform> artrHeader = new List<Transform>();
private int iCurr = 0; // Index of the current header being used.
private float fHeight; // Height of a header
void Start() {
BuildList();
uisl.clipContents = false; // Doesn't work with clipping turned on
v3Header = uisl.transform.position; // Figure the position of the header
v3Header.y = v3Header.y + uisl.viewableArea.y / 2.0f - fHeight / 2.0f;
v3Header.z -= 0.2f;
}
// Builds all the headers and entries for the scroll list
void BuildList() {
Transform tr = new GameObject().transform; // Insert a blank/dummy header in the
Transform trChild = new GameObject().transform; // beginning of the header list to
trChild.parent = tr; // remove a boundry condition
Vector3 v3 = Vector3.zero;
v3.y = 10000;
tr.position = v3;
artrHeader.Add(trChild);
// Parse the text file of sorted strings into a string array
string[] arst = ta.text.Split(new char[] {'\r','
'},StringSplitOptions.RemoveEmptyEntries);
// Insert the top header
char ch = 'A';
InsertHeader (ch);
// Process the sorted list of strings
for (int i = 0; i < arst.Length; i++) {
if (arst*[0] != ch) { // If the first letter changed, insert a new one header*
_ ch = arst*[0];_
_ InsertHeader(ch);_
_ }*_
* // Insert a new entry.*
* GameObject go = Instantiate(goPrefabEntry) as GameObject;*
* UIListButton uilb = go.GetComponent();*
_ uilb.Text = arst*;
uilb.Data = arst;
uilb.scriptWithMethodToInvoke = this;
uilb.methodToInvoke = “DoClick”;
uisl.AddItem (go);
}*_
* tr = new GameObject().transform; // Insert a blank/dummy header at the*
* trChild = new GameObject().transform; // end of the header list to*
* trChild.parent = tr; // remove a boundry condition*
* v3 = Vector3.zero;*
* v3.y = -10000;*
* tr.position = v3;*
* artrHeader.Add(trChild);*
* }*
* // Inserts a header into the list. Note a header consists of an*
* // empty game object witha UIListItemContainer component and the*
* // actual UIListButton is a child of that object (with a*
* // localPosition of (0,0,0).*
* void InsertHeader(char ch) {*
* GameObject goParent = new GameObject();*
* goParent.AddComponent();*
* GameObject go = Instantiate(goPrefabHeader) as GameObject;*
* UIListButton uilb = go.GetComponent();*
* fHeight = uilb.height;*
* uilb.Text = ch.ToString();*
* go.transform.parent = goParent.transform;*
* uisl.AddItem (goParent);*
* artrHeader.Add (go.transform);*
* }*
* // This code needs to be executed last. You may need to modify the*
* // order of the script executions to make sure this executes last*
* void LateUpdate() {*
* // Put the header we modified last frame back in place*
* artrHeader[iCurr].localPosition = Vector3.zero;*
* // Find the header we should use. Note I rescan the*
* // list because a fast swipe might go past more than one*
* // header in a frame.*
* iCurr = 0;*
* for (int i = 0; i <= artrHeader.Count; i++) {*
_ if (artrHeader*.parent.position.y < v3Header.y)
break;
iCurr++;
}
iCurr–;*_
* // Put the header that we should use in the header position.*
* artrHeader[iCurr].localPosition = artrHeader[iCurr].InverseTransformPoint(v3Header);*
* // If the next header is pushing the current header, adjust the current header’s*
* // position.*
* if (artrHeader[iCurr+1].parent.position.y + fHeight >= v3Header.y) {*
* Vector3 v3 = artrHeader[iCurr].localPosition;*
* v3.y += (artrHeader[iCurr+1].parent.position.y + fHeight) - v3Header.y;*
* artrHeader[iCurr].localPosition = v3;*
* }*
* }*
* // Process a mouse click on an entry*
* void DoClick() {*
* string st = (string)uisl.LastClickedControl.Data;*
* Debug.Log (st);*
* }*
}