Pinch-zoom On A Listview
I have a ListView which contains a few TextView's arranged using a custom adapter. What I would like to do, is implement pinch-to-zoom on this ListView, so that when the user pinch
Solution 1:
You should be able to follow the Pinch zoom for custom view and then do something like this when overriding the onTouchEvent. This will allow you to listen for a scale event, as well as all other touch events in the listview.
@OverridepublicbooleanonTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
returnsuper.onTouchEvent(ev);
}
Solution 2:
Yes we can do this.
/**
* Zooming view.
*/publicclassZoomViewextendsFrameLayout {
/**
* Zooming view listener interface.
*
* @author karooolek
*
*/publicinterfaceZoomViewListener {
voidonZoomStarted(float zoom, float zoomx, float zoomy);
voidonZooming(float zoom, float zoomx, float zoomy);
voidonZoomEnded(float zoom, float zoomx, float zoomy);
}
// zoomingfloatzoom=1.0f;
floatmaxZoom=2.0f;
floatsmoothZoom=1.0f;
float zoomX, zoomY;
float smoothZoomX, smoothZoomY;
privateboolean scrolling; // NOPMD by karooolek on 29.06.11 11:45// minimap variablesprivatebooleanshowMinimap=false;
privateintminiMapColor= Color.BLACK;
privateintminiMapHeight= -1;
private String miniMapCaption;
privatefloatminiMapCaptionSize=10.0f;
privateintminiMapCaptionColor= Color.WHITE;
// touching variablesprivatelong lastTapTime;
privatefloat touchStartX, touchStartY;
privatefloat touchLastX, touchLastY;
privatefloat startd;
privateboolean pinching;
privatefloat lastd;
privatefloat lastdx1, lastdy1;
privatefloat lastdx2, lastdy2;
// drawingprivatefinalMatrixm=newMatrix();
privatefinalPaintp=newPaint();
// listener
ZoomViewListener listener;
private Bitmap ch;
publicZoomView(final Context context) {
super(context);
}
publicfloatgetZoom() {
return zoom;
}
publicfloatgetMaxZoom() {
return maxZoom;
}
publicvoidsetMaxZoom(finalfloat maxZoom) {
if (maxZoom < 1.0f) {
return;
}
this.maxZoom = maxZoom;
}
publicvoidsetMiniMapEnabled(finalboolean showMiniMap) {
this.showMinimap = showMiniMap;
}
publicbooleanisMiniMapEnabled() {
return showMinimap;
}
publicvoidsetMiniMapHeight(finalint miniMapHeight) {
if (miniMapHeight < 0) {
return;
}
this.miniMapHeight = miniMapHeight;
}
publicintgetMiniMapHeight() {
return miniMapHeight;
}
publicvoidsetMiniMapColor(finalint color) {
miniMapColor = color;
}
publicintgetMiniMapColor() {
return miniMapColor;
}
public String getMiniMapCaption() {
return miniMapCaption;
}
publicvoidsetMiniMapCaption(final String miniMapCaption) {
this.miniMapCaption = miniMapCaption;
}
publicfloatgetMiniMapCaptionSize() {
return miniMapCaptionSize;
}
publicvoidsetMiniMapCaptionSize(finalfloat size) {
miniMapCaptionSize = size;
}
publicintgetMiniMapCaptionColor() {
return miniMapCaptionColor;
}
publicvoidsetMiniMapCaptionColor(finalint color) {
miniMapCaptionColor = color;
}
publicvoidzoomTo(finalfloat zoom, finalfloat x, finalfloat y) {
this.zoom = Math.min(zoom, maxZoom);
zoomX = x;
zoomY = y;
smoothZoomTo(this.zoom, x, y);
}
publicvoidsmoothZoomTo(finalfloat zoom, finalfloat x, finalfloat y) {
smoothZoom = clamp(1.0f, zoom, maxZoom);
smoothZoomX = x;
smoothZoomY = y;
if (listener != null) {
listener.onZoomStarted(smoothZoom, x, y);
}
}
public ZoomViewListener getListener() {
return listener;
}
publicvoidsetListner(final ZoomViewListener listener) {
this.listener = listener;
}
publicfloatgetZoomFocusX() {
return zoomX * zoom;
}
publicfloatgetZoomFocusY() {
return zoomY * zoom;
}
@OverridepublicbooleandispatchTouchEvent(final MotionEvent ev) {
// single touchif (ev.getPointerCount() == 1) {
processSingleTouchEvent(ev);
}
// // double touchif (ev.getPointerCount() == 2) {
processDoubleTouchEvent(ev);
}
// redraw
getRootView().invalidate();
invalidate();
returntrue;
}
privatevoidprocessSingleTouchEvent(final MotionEvent ev) {
finalfloatx= ev.getX();
finalfloaty= ev.getY();
finalfloatw= miniMapHeight * (float) getWidth() / getHeight();
finalfloath= miniMapHeight;
finalbooleantouchingMiniMap= x >= 10.0f && x <= 10.0f + w
&& y >= 10.0f && y <= 10.0f + h;
if (showMinimap && smoothZoom > 1.0f && touchingMiniMap) {
processSingleTouchOnMinimap(ev);
} else {
processSingleTouchOutsideMinimap(ev);
}
}
privatevoidprocessSingleTouchOnMinimap(final MotionEvent ev) {
finalfloatx= ev.getX();
finalfloaty= ev.getY();
finalfloatw= miniMapHeight * (float) getWidth() / getHeight();
finalfloath= miniMapHeight;
finalfloatzx= (x - 10.0f) / w * getWidth();
finalfloatzy= (y - 10.0f) / h * getHeight();
smoothZoomTo(smoothZoom, zx, zy);
}
privatevoidprocessSingleTouchOutsideMinimap(final MotionEvent ev) {
finalfloatx= ev.getX();
finalfloaty= ev.getY();
floatlx= x - touchStartX;
floatly= y - touchStartY;
finalfloatl= (float) Math.hypot(lx, ly);
floatdx= x - touchLastX;
floatdy= y - touchLastY;
touchLastX = x;
touchLastY = y;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStartX = x;
touchStartY = y;
touchLastX = x;
touchLastY = y;
dx = 0;
dy = 0;
lx = 0;
ly = 0;
scrolling = false;
break;
case MotionEvent.ACTION_MOVE:
if (scrolling || (smoothZoom > 1.0f && l > 30.0f)) {
if (!scrolling) {
scrolling = true;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
smoothZoomX -= dx / zoom;
smoothZoomY -= dy / zoom;
return;
}
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
// tapif (l < 30.0f) {
// check double tapif (System.currentTimeMillis() - lastTapTime < 500) {
if (smoothZoom == 1.0f) {
smoothZoomTo(maxZoom, x, y);
} else {
smoothZoomTo(1.0f, getWidth() / 2.0f,
getHeight() / 2.0f);
}
lastTapTime = 0;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
return;
}
lastTapTime = System.currentTimeMillis();
performClick();
}
break;
default:
break;
}
ev.setLocation(zoomX + (x - 0.5f * getWidth()) / zoom, zoomY
+ (y - 0.5f * getHeight()) / zoom);
ev.getX();
ev.getY();
super.dispatchTouchEvent(ev);
}
privatevoidprocessDoubleTouchEvent(final MotionEvent ev) {
finalfloatx1= ev.getX(0);
finalfloatdx1= x1 - lastdx1;
lastdx1 = x1;
finalfloaty1= ev.getY(0);
finalfloatdy1= y1 - lastdy1;
lastdy1 = y1;
finalfloatx2= ev.getX(1);
finalfloatdx2= x2 - lastdx2;
lastdx2 = x2;
finalfloaty2= ev.getY(1);
finalfloatdy2= y2 - lastdy2;
lastdy2 = y2;
// pointers distancefinalfloatd= (float) Math.hypot(x2 - x1, y2 - y1);
finalfloatdd= d - lastd;
lastd = d;
finalfloatld= Math.abs(d - startd);
Math.atan2(y2 - y1, x2 - x1);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startd = d;
pinching = false;
break;
case MotionEvent.ACTION_MOVE:
if (pinching || ld > 30.0f) {
pinching = true;
finalfloatdxk=0.5f * (dx1 + dx2);
finalfloatdyk=0.5f * (dy1 + dy2);
smoothZoomTo(Math.max(1.0f, zoom * d / (d - dd)), zoomX - dxk
/ zoom, zoomY - dyk / zoom);
}
break;
case MotionEvent.ACTION_UP:
default:
pinching = false;
break;
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
privatefloatclamp(finalfloat min, finalfloat value, finalfloat max) {
return Math.max(min, Math.min(value, max));
}
privatefloatlerp(finalfloat a, finalfloat b, finalfloat k) {
return a + (b - a) * k;
}
privatefloatbias(finalfloat a, finalfloat b, finalfloat k) {
return Math.abs(b - a) >= k ? a + k * Math.signum(b - a) : b;
}
@OverrideprotectedvoiddispatchDraw(final Canvas canvas) {
// do zoom
zoom = lerp(bias(zoom, smoothZoom, 0.05f), smoothZoom, 0.2f);
smoothZoomX = clamp(0.5f * getWidth() / smoothZoom, smoothZoomX,
getWidth() - 0.5f * getWidth() / smoothZoom);
smoothZoomY = clamp(0.5f * getHeight() / smoothZoom, smoothZoomY,
getHeight() - 0.5f * getHeight() / smoothZoom);
zoomX = lerp(bias(zoomX, smoothZoomX, 0.1f), smoothZoomX, 0.35f);
zoomY = lerp(bias(zoomY, smoothZoomY, 0.1f), smoothZoomY, 0.35f);
if (zoom != smoothZoom && listener != null) {
listener.onZooming(zoom, zoomX, zoomY);
}
finalbooleananimating= Math.abs(zoom - smoothZoom) > 0.0000001f
|| Math.abs(zoomX - smoothZoomX) > 0.0000001f
|| Math.abs(zoomY - smoothZoomY) > 0.0000001f;
// nothing to drawif (getChildCount() == 0) {
return;
}
// prepare matrix
m.setTranslate(0.5f * getWidth(), 0.5f * getHeight());
m.preScale(zoom, zoom);
m.preTranslate(
-clamp(0.5f * getWidth() / zoom, zoomX, getWidth() - 0.5f
* getWidth() / zoom),
-clamp(0.5f * getHeight() / zoom, zoomY, getHeight() - 0.5f
* getHeight() / zoom));
// get viewfinalViewv= getChildAt(0);
m.preTranslate(v.getLeft(), v.getTop());
// get drawing cache if availableif (animating && ch == null && isAnimationCacheEnabled()) {
v.setDrawingCacheEnabled(true);
ch = v.getDrawingCache();
}
// draw using cache while animatingif (animating && isAnimationCacheEnabled() && ch != null) {
p.setColor(0xffffffff);
canvas.drawBitmap(ch, m, p);
} else { // zoomed or cache unavailable
ch = null;
canvas.save();
canvas.concat(m);
v.draw(canvas);
canvas.restore();
}
// draw minimapif (showMinimap) {
if (miniMapHeight < 0) {
miniMapHeight = getHeight() / 4;
}
canvas.translate(10.0f, 10.0f);
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
finalfloatw= miniMapHeight * (float) getWidth() / getHeight();
finalfloath= miniMapHeight;
canvas.drawRect(0.0f, 0.0f, w, h, p);
if (miniMapCaption != null && miniMapCaption.length() > 0) {
p.setTextSize(miniMapCaptionSize);
p.setColor(miniMapCaptionColor);
p.setAntiAlias(true);
canvas.drawText(miniMapCaption, 10.0f,
10.0f + miniMapCaptionSize, p);
p.setAntiAlias(false);
}
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
finalfloatdx= w * zoomX / getWidth();
finalfloatdy= h * zoomY / getHeight();
canvas.drawRect(dx - 0.5f * w / zoom, dy - 0.5f * h / zoom, dx
+ 0.5f * w / zoom, dy + 0.5f * h / zoom, p);
canvas.translate(-10.0f, -10.0f);
}
// redraw// if (animating) {
getRootView().invalidate();
invalidate();
// }
}
}
when you want to use it make your listview dynamically and add it to zoomview like this.
bodyContainer = (LinearLayout) findViewById(R.id.container);
lvDateList = newListView(ListDateActivity.this);
lvDateList.setLayoutParams(newLinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
ZoomViewzoomView=newZoomView(ListDateActivity.this);
zoomView.setLayoutParams(newLinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
zoomView.addView(lvDateList);
container.addView(zoomView);
Post a Comment for "Pinch-zoom On A Listview"