- Home /
Possibility of scroll list with fixed headers?
Hello all,
I was wondering how possible it is to make a scroll list with a header at the top of the scroll list. The point of the header is essentially to act like a catergory or grouping for the scroll list objects under the header. The only example I know of is in the iPhone. If you look in the contacts area at the top there is a header with a letter('A', 'B', 'C', etc, etc) The top letter, which should be 'A', gets bumped up when you enter the 'B' section of the scroll list so then the A disappears and now 'B' is the header and fixed at the top of the scroll list. Then as 'C' starts to come into view and as it scrolls to the top 'C' will replace the 'B' and of course if you start scrolling up (if you were at c) 'C' will be bumped down and 'B' will scroll in from the top of the scroll view and 'C' just scrolls down like a regular scroll list object.
I tried starting to implement something like this but I only got as far as the scroll list registering that a new header scroll list object is in the header position(doesnt do anything but print a debug statement stating that the object is at the top of the list) and determining which direction the list is being dragged. (I'd imagine I would need that for the diffecnce of 'B' bumping up 'A' and 'B' bumping down 'C') I think my biggest problem is having the header scroll object fixed at the top of the scroll list. I'm about to attempt to write some code to do that but I'm not exactly sure how.
I should mention I am using the EZGUI UIScrollList as a base for this. I already figured out I would have to inherit the UIScrollList into a new scroll list class to get this certain functionality.
Thanks for any suggestions and help! And if I wrote anything that isn't clear let me know and I'll clear it up. Or if you need more explanation please let me know and I'll try to clarify.
-Ethan
EDIT: Here's a link to a video showing the scroll list functionality I want. https://www.youtube.com/watch?v=eikTReAyXBc
EDIT2: Here's my current issue (http://answers.unity3d.com/questions/464741/speedvelocity-of-scroll-list.html) and blocking me from moving forward with this implementation. I'm really close I can taste it! I just need the speed.
EDIT3: I have successfully made a header scroll list with the exception of 1 problem. If a user was to swipe too fast when there are two headers at the top, the top header box collider couldn't register that a box collider left, leaving the top header section empty(which is a major issue with a header based scroll list). I will now attempt to implemented robertbu's answer.
You might have better asking the question (or searching the BB) here:
The path of least resistance for this functionality would be to set 'snap' for the list. Then you can evaluate the 'SnappedItem' at each frame and update the header. The header could be SpriteText (Label) or if the heading are some sort of list and you want to get fancy with the graphical presentation, use a PackedSprite and change the frame based on the 'SnappedItem'.
I should have mentioned that also. I already posted a forum post in the ezgui how to forums. Here is the link if anyone is interested: http://forum.anbsoft.com/viewtopic.php?f=9&t=4771
So are you trying to do something more complicated than I describe? I'm trying to understand the key problem here.
I'm sorry I'm a bit distracted I moved on to another part of the program for now but I'm probably going back to this scroll list sometime today. I believe my key problem is having a header stay at the top of the scroll list but still be part of the scroll list and then having the none header scroll list objects act regularly. Does that make sense?
I'm puzzled by why you want the header as part of the scroll list. Why not just position a header game object above the scroll viewport area and change the contents of the header based on scroll information? I think there is some problem here you are trying to solve that I'm not seeing.
Answer by robertbu · May 29, 2013 at 06:04 AM
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','\n'},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[i][0] != ch) { // If the first letter changed, insert a new one header
ch = arst[i][0];
InsertHeader(ch);
}
// Insert a new entry.
GameObject go = Instantiate(goPrefabEntry) as GameObject;
UIListButton uilb = go.GetComponent<UIListButton>();
uilb.Text = arst[i];
uilb.Data = arst[i];
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<UIListItemContainer>();
GameObject go = Instantiate(goPrefabHeader) as GameObject;
UIListButton uilb = go.GetComponent<UIListButton>();
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[i].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);
}
}
mmm, I'll give this a shot. Just so you know I did get very far making this type of list using BoxColliders. $$anonymous$$y only problem was if there were two headers in the topHeader BoxCollider and if you swiped fast enough the topHeader BoxCollider wouldn't/couldn't register the OnTriggerExit function. Thus leaving the list header-less.
I forgot to give instructions for setup:
Create and size a scroll list (UIScrollList script).
Create a prefab for your normal entries with the UIListButtion script.
Create a prefab for your header entries with the UIListButton script.
Created a sorted list of strings (capital alpha letters only for the first character), and import it into Unity as a text asset. For my test, I grabbed 200 baby names.
Attach this script to an empty game object.
Drag and drop the text, prefabs, and scroll list onto the appropriate variables in the inspector.
Setup the script execution order so this script executes last (if necessary).
Note the headers for this demo are just the letter of the alphabet for the strings.
For the header prefab did you mean to write UIListItemContainer with a UIListButton as a child? That's what you said it would be at the beginning of your answer.
No, all you need is a UIListButton. You could even use the same prefab for both entries and headers The UIListItemContainer is added dynamically (line 81).
Robertbu, so I got the scroll list to populate but it's doesn't have any header functionality working. I'm not exactly sure where I am going wrong. Let me show you what I have: First off my script execution order is this: UIScrollList -1, Default Time, ScrollListHeaderController 1 (image of script execution order: http://i.imgur.com/jSdGIrH.png). Second, my script is a child of the scroll view see linked image: http://i.imgur.com/6Bzu$$anonymous$$m6.png . The 2 "New Game Object"s under the controller are the child transforms of the boundary transforms we added line 35 and 67 in your script. Also, the beginning and end transform are at the top most level in the hierarchy, not sure if they should be there or if it even matters. Third, I can post my script if you want but I won't do that unless you want to see it, so let me know and I'll post it. Fourth, I don't think I missed anything else. Let me know what your thoughts are.
Answer by Docboy · May 09, 2020 at 05:53 AM
I find a creative soution, put a scrollview as a header and anther scrollview for your content and syncronize the scrollview setting the value of the scrollbar of the header from the scrollbar of the content
Your answer
Follow this Question
Related Questions
Speed/velocity of scroll list? 1 Answer
Are double response headers supported in unity? 1 Answer
problem using EZGUI progress bar 1 Answer
ez gui panel transition 1 Answer
Suddenly File Headers size ballooned 0 Answers