In the previous post I show you how to create a simple Android client application in order to access our web service on the cloud.
This week I provide a more complete implementation of that app, focused on the Android API itself, in order to illustrate how to use the touch screen of the device so the user can pan, zoom in and zoom out of the picture.
In order to receive touch notifications, a specific view needs to call the “View.setOnTouchListener(OnTouchListener listener)” method and provides a listener object that implements the “OnTouchListener” interface.
In order to optimize code reusability, I like to create a specific manager class that implements that interface and provides its own interface to other objects in my application, so I have more flexibility over the touch events.
Here is the implementation of my TouchManager class:
//////////////////////////////////////////////////////////////////////////////////////////
// TouchManager Class handles user touch interaction on device screen
//
//////////////////////////////////////////////////////////////////////////////////////////
public class TouchManager
implements OnTouchListener
{
//////////////////////////////////////////////////////////////////////////////////////
// ITouchListener Interface
//
//////////////////////////////////////////////////////////////////////////////////////
private ArrayList<ITouchListener> _listeners =
new ArrayList<ITouchListener>();
public interface ITouchListener extends EventListener
{
public void OnPointerUp(MotionEvent event);
public void OnDrag(MotionEvent event);
public void OnDragInit(MotionEvent event);
public void OnZoom(MotionEvent event);
public void OnZoomInit(MotionEvent event);
}
private enum TouchState
{
kNone, kDrag, kZoom
}
private TouchState _touchState = TouchState.kNone;
public TouchManager(View view)
{
view.setOnTouchListener(this);
}
//////////////////////////////////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////////////////////
public void AddEventListener(ITouchListener listener)
{
_listeners.add(listener);
}
//////////////////////////////////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////////////////////
public void RemoveEventListener(ITouchListener listener)
{
_listeners.remove(listener);
}
//////////////////////////////////////////////////////////////////////////////////////
// User dragged finger on screen
//
//////////////////////////////////////////////////////////////////////////////////////
void OnDrag(MotionEvent event)
{
for (ITouchListener listener : _listeners)
{
listener.OnDrag(event);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// Drag init
//
//////////////////////////////////////////////////////////////////////////////////////
void OnDragInit(MotionEvent event)
{
for (ITouchListener listener : _listeners)
{
listener.OnDragInit(event);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// User pinched screen with two fingers
//
//////////////////////////////////////////////////////////////////////////////////////
void OnZoom(MotionEvent event)
{
for (ITouchListener listener : _listeners)
{
listener.OnZoom(event);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// Zoom init
//
//////////////////////////////////////////////////////////////////////////////////////
void OnZoomInit(MotionEvent event)
{
for (ITouchListener listener : _listeners)
{
listener.OnZoomInit(event);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// User removed finger from screen
//
//////////////////////////////////////////////////////////////////////////////////////
void OnPointerUp(MotionEvent event)
{
for (ITouchListener listener : _listeners)
{
listener.OnPointerUp(event);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// onTouch event notification
//
//////////////////////////////////////////////////////////////////////////////////////
public boolean onTouch(View arg0, MotionEvent event)
{
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
_touchState = TouchState.kDrag;
OnDragInit(event);
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
{
_touchState = TouchState.kZoom;
OnZoomInit(event);
break;
}
case MotionEvent.ACTION_MOVE:
{
switch(_touchState)
{
case kDrag:
{
OnDrag(event);
break;
}
case kZoom:
{
OnZoom(event);
break;
}
}
break;
}
case MotionEvent.ACTION_UP:
OnPointerUp(event);
_touchState = TouchState.kNone;
break;
case MotionEvent.ACTION_POINTER_UP:
OnPointerUp(event);
_touchState = TouchState.kDrag;
break;
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////////
// Returns distance between two fingers
//
//////////////////////////////////////////////////////////////////////////////////////
public float CalcDistance(MotionEvent event)
{
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
}
-
-
My main activity will instantiate that TouchManager and implement its interface in order to modify the ImageView accordingly to handle pan and zoom actions:
-
//////////////////////////////////////////////////////////////////////////////////
// ITouchListener Methods
//
//////////////////////////////////////////////////////////////////////////////////
@Override
public void OnDragInit(MotionEvent event)
{
ImageView imgView =
(ImageView)findViewById(R.id.imageView);
_matrix = imgView.getImageMatrix();
_startPoint.set(event.getX(), event.getY());
}
@Override
public void OnDrag(MotionEvent event)
{
Matrix matrix = new Matrix();
matrix.set(_matrix);
float dx = event.getX() - _startPoint.x;
float dy = event.getY() - _startPoint.y;
matrix.postTranslate(dx, dy);
_startPoint.set(event.getX(), event.getY());
ImageView imgView = (ImageView)findViewById(R.id.imageView);
imgView.setImageMatrix(matrix);
}
@Override
public void OnZoomInit(MotionEvent event)
{
ImageView imgView = (ImageView)findViewById(R.id.imageView);
_matrix = imgView.getImageMatrix();
SetMidPoint(_midPoint, event);
_previousZoom = _touchManager.CalcDistance(event);
}
@Override
public void OnZoom(MotionEvent event)
{
float newZoom = _touchManager.CalcDistance(event);
// setting the scaling of the matrix
// scale > 1 = zoom in
// scale < 1 = zoom out
float scale = newZoom / _previousZoom;
float totalScale = _totalScale * scale;
if(totalScale > 0.2f && totalScale < 2.0f)
{
_totalScale = totalScale;
Matrix matrix = new Matrix();
matrix.set(_matrix);
matrix.postScale(scale, scale, _midPoint.x, _midPoint.y);
ImageView imgView = (ImageView)findViewById(R.id.imageView);
imgView.setImageMatrix(matrix);
}
}
@Override
public void OnPointerUp(MotionEvent event)
{
ImageView imgView =
(ImageView)findViewById(R.id.imageView);
_matrix = imgView.getImageMatrix();
_startPoint.set(event.getX(), event.getY());
}
That’s it, you should now be able to zoom and pan the retrieved images from the cloud.
-
The only details I couldn’t tweak exactly as wished is that the ImageView doesn’t seem to reset to its initial position and scale, so I may update that project if I get it to work as I expect… If you have any suggestion, please feel free.
-
The complete project is attached below. Next time I will show you how to migrate our WCF service to be hosted on the Azure cloud instead of AWS, stay tuned!
-
Comments
You can follow this conversation by subscribing to the comment feed for this post.