This can be handled in many different ways and, depending on the complexity of the overall application, it can be as simple as a super-loop, or as complex as a multitasking based application with several independent tasks each doing their own thing (e.g., one doing key press detection, another dealing with serial comms, another updating the [G]LCD, etc.).
Your particular example can easily be handled with the super-loop approach, although a multitasker can also be used for (IMO) simplicity in coding.
For example, with the super-loop approach, each time through the loop you call a key press detection routine which checks if a key is pressed and counts time up to some maximum as long as the key press is still present. It does not block, it exits immediately. When the count reaches a minimum to accept the key (e.g., corresponding to about 50-100 msec) you return the key pressed and zero the counter (for auto key repeat), or save the key in a temporary buffer and return it only when the key is eventually released (if no auto key repeat is desired).
The display works in a similar way. The current screen is updated depending on which state the device is in. When the UP/DOWN key (for example) is detected, the index of the scrolling item changes up or down and the screen is redrawn with the new state.
There are certain situations that a multitasker is the only reasonable way to solve such problems if you don't want your app to become a un-debuggable mess of flags, and ifs. Dealing concurrently (and smoothly) with multiple interfaces (e.g., GPS, GSM, user terminal, key/LCD) is one such example.
BTW, interrupts for key presses are IMO an overkill unless you are in some battery saving sleep mode and need a hardware way to wake up. Human key presses are always too slow by comparison to CPU speeds and can be detected reliably by simple polling.