Skip to content

Instantly share code, notes, and snippets.

@kerimovscreations
Created October 7, 2017 19:21
Show Gist options
  • Save kerimovscreations/45da07aa05d5922fad854fc57d504fb4 to your computer and use it in GitHub Desktop.
Save kerimovscreations/45da07aa05d5922fad854fc57d504fb4 to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.FrameLayout;
/**
* Layout that provides pinch-zooming of content. This view should have exactly one child
* view containing the content.
*/
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
// private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private float maxZoom = 4.0f;
boolean zoomEnabled = true;
float upperLimit = 0.0f;
float deadZone = 0.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
// double tap variables
private float touchStartX, touchStartY;
private long lastTapTime;
private float lap;
public boolean isZoomEnabled() {
return zoomEnabled;
}
public void setZoomEnabled(boolean zoomEnabled) {
this.zoomEnabled = zoomEnabled;
}
public void setMaxZoom(final float maxZoom) {
if (maxZoom < 1.0f) {
return;
}
this.maxZoom = maxZoom;
}
public void setDeadZone(float zone) {
this.deadZone = zone;
this.upperLimit = (int) (getHeight() - zone) / 2;
if (getHeight() > deadZone * maxZoom) {
maxZoom = (float) getHeight() / deadZone;
}
}
public float getDeadZone() {
return this.deadZone;
}
public float getHalfScreenSize() {
return getHeight() / 2;
}
public void smoothZoomTo(final float zoom, final float x, final float y) {
scale = zoom;
dx = x * -1;
dy = y * scale;
applyScaleAndTranslation();
}
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final float x = motionEvent.getX();
final float y = motionEvent.getY();
float lx = x - touchStartX;
float ly = y - touchStartY;
lap = (float) Math.hypot(lx, ly);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Log.i(TAG, "DOWN");
touchStartX = x;
touchStartY = y;
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = x - prevDx;
startY = y - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = x - startX;
dy = y - startY;
}
break;
case MotionEvent.ACTION_UP:
// Log.i(TAG, "UP");
mode = Mode.NONE;
// tap
if (lap < 30.0f) {
// check double tap
if (System.currentTimeMillis() - lastTapTime < 300) {
if (scale == MIN_ZOOM) {
if (y < upperLimit) {
// CommonMethods.print("Upper");
scale = maxZoom;
dx = (getWidth() / 2 - x) * scale;
dy = (deadZone * scale - getHeight()) / 2;
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
applyScaleAndTranslation();
} else if (y > upperLimit + deadZone) {
// CommonMethods.print("Below");
scale = maxZoom;
dx = (getWidth() / 2 - x) * scale;
dy = (getHeight() - deadZone * scale) / 2;
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
applyScaleAndTranslation();
} else {
// CommonMethods.print("Inside");
scale = maxZoom;
dx = (getWidth() / 2 - x) * scale;
dy = (getHeight() / 2 - y) * scale;
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (deadZone * scale - getHeight()) / 2;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
applyScaleAndTranslation();
}
} else {
smoothZoomTo(MIN_ZOOM, 0, 0);
}
lastTapTime = 0;
}
lastTapTime = System.currentTimeMillis();
}
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (deadZone * scale - getHeight()) / 2;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
// Log.i(TAG, "onScaleBegin");
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
mode = Mode.NONE;
if (scaleGestureDetector.getScaleFactor() > 1.2) {
scale = maxZoom;
dx = 0;
dy = 0;
applyScaleAndTranslation();
} else {
scale = MIN_ZOOM;
dx = 0;
dy = 0;
applyScaleAndTranslation();
}
return false;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
// Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
public void zoomMax(){
scale = maxZoom;
dx = 0;
dy = 0;
applyScaleAndTranslation();
}
private View child() {
return getChildAt(0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment