Zooming

As we are rendering a box for each TV program and its size is directly determined by the TV program duration, it might happen that TV program titles will be larger than its rendered box. In those cases, we might want to read some more parts of the title, or, at other times, we may like to compress things a bit so we can have an overall picture of what will be on TV later that day.

To solve this, we can implement a zooming mechanism by pinching on our device screen on top of our EPG widget. We can apply this zooming directly to the timeScale variable, and, as we have used it everywhere for all calculations, it'll keep everything synchronized:

 

scaleDetector = new ScaleGestureDetector(context,  
    new ScaleGestureDetector.SimpleOnScaleGestureListener() {  
 
    ... 
     
    }); 
 

To simplify it, let's use the SimpleOnScaleGestureListener, which allows us to override only the methods we'd like to use.

Now, we need to modify the onTouchEvent to let the scaleDetector instance process the event as well:

 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    scaleDetector.onTouchEvent(event); 
 
    if (zooming) { 
        zooming = false; 
        return true; 
    } 
 
    ... 
 
} 
 

We've also added a check to see if we are zooming. We'll update this variable in the ScaleDetector implementation, but the concept is to avoid scrolling the view, or processing drag events, if we are already zooming.

Let's now implement the ScaleDetector:

scaleDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { 
    private long focusTime; 
    private float scrollCorrection = 0.f; 
    @Override 
    public boolean onScaleBegin(ScaleGestureDetector detector) { 
        zooming = true; 
        focusTime = getHorizontalPositionTime(scrollXTarget +
detector.getFocusX() - frChNameWidth); scrollCorrection = getTimeHorizontalPosition((focusTime)) -
scrollXTarget; return true; } public boolean onScale(ScaleGestureDetector detector) { timeScale *= detector.getScaleFactor(); timeScale = Math.max(DEFAULT_TIME_SCALE * screenDensity / 2, Math.min(timeScale, DEFAULT_TIME_SCALE *
screenDensity * 4)); // correct scroll position otherwise will move too much when
zooming float current = getTimeHorizontalPosition((focusTime)) -
scrollXTarget; float scrollDifference = current - scrollCorrection; scrollXTarget += scrollDifference; zooming = true; invalidate(); return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { zooming = true; } });

We're basically doing two different things. First, we adjust the timeScale variable from half the default value to four times the default value:

timeScale *= detector.getScaleFactor(); 
timeScale = Math.max(DEFAULT_TIME_SCALE * screenDensity / 2,  
                Math.min(timeScale, DEFAULT_TIME_SCALE * screenDensity
* 4));

Also, we adjust the scroll position to avoid an unpleasant effect when scaling. By adjusting the scroll position, we are trying to keep the focus of the pinch at the same position, even after zooming in or out.

float current = getTimeHorizontalPosition((focusTime)) - scrollXTarget; 
float scrollDifference = current - scrollCorrection; 
scrollXTarget += scrollDifference; 
 

For more information about the ScaleDetector and gestures, check out the official Android documentation.