I'm implementing a canvas on a WebView and I need to have the canvas scale according to the zoom level of the WebView. Combining canvas.scale() with a SimpleOnScaleListener does the trick. For the double tap, I'm using a GestureDetector which detects when there's a double tap and sets the scale factor accordingly. I'm merely guessing how much it scales when there is a double tap: Setting the scale factor to 2.2f works on a 7in tablet but it scales too much for a 10in.
Is there anyway to programmatically figure out how much the WebView has zoomed in on double tap? I basically need the canvas to synchronize zooming with the WebView.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.scale(mScaleFactor, mScaleFactor, 0, 0);
canvas.drawPath(drawPath, drawPaint);
invalidate();
}
public class ScaleGestureListener extends SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
// Don't let the object get too small or too large.
if(Deal.on == false) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 30.0f));
System.out.println("mScaleFactor: " + mScaleFactor);
}
invalidate();
return true;
}
}
public class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDoubleTap(MotionEvent e) {
if (Deal.on == false) {
if (mScaleFactor > 1.0f) {
mScaleFactor = 1.0f;
}else {
mScaleFactor = 2.2f;
}
}
return true;
}
}
You can catch zoom change by extending WebViewClient
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
setWebViewClient(new WebViewClient() {
#Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
currentScale = newScale;
}
});
}
Related
I want to rotate & scale in same time, but it's flickering while rotating and scaling the layout. This is my code
public class CustomRelaytivelayout extends RelativeLayout implements RotationGestureDetector.OnRotationGestureListener {
private float scale = 1.0f;
private float touchX = 0.0f;
private float touchY = 0.0f;
public ScaleGestureDetector mScaleDetector;
public RotationGestureDetector mRotationDetector;
private CustomRelaytivelayout customRelaytivelayout_;
float mScaleFactor = 1;
float mPivotX;
float mPivotY;
public CustomRelaytivelayout(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this));
mRotationDetector = new RotationGestureDetector(this);
customRelaytivelayout_ = this;
}
public CustomRelaytivelayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this));
mRotationDetector = new RotationGestureDetector(this);
customRelaytivelayout_ = this;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private CustomRelaytivelayout customRelaytivelayout;
float startingSpan;
float endSpan;
float startFocusX;
float startFocusY;
public ScaleListener(CustomRelaytivelayout customRelaytivelayout_) {
customRelaytivelayout = customRelaytivelayout_;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
// Multiply scale factor
scale *= detector.getScaleFactor();
ViewHelper.setScaleX(customRelaytivelayout, scale);
ViewHelper.setScaleY(customRelaytivelayout, scale);
invalidate();
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
private float angle = 0;
#Override
public void OnRotation(RotationGestureDetector rotationDetector) {
angle = angle + rotationDetector.getAngle();
Log.d("TAG_Angle", "" + angle);
customRelaytivelayout_.setRotation(-angle);
}
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor, mPivotX, mPivotY);
super.dispatchDraw(canvas);
canvas.restore();
}
public void scale(float scaleFactor, float pivotX, float pivotY) {
mScaleFactor = scaleFactor;
mPivotX = pivotX;
mPivotY = pivotY;
this.invalidate();
}
public void restore() {
mScaleFactor = 1;
this.invalidate();
}
}
Its flickering while rotating and scaling the layout.If any one have solution please share
Thanks in advance
I'm creating View that can store, move, zoom and crop a picture. I'm aware about many projects which already done this, so link to other project wouldn't be answer, but i'm not sure that it's possible to achive my request.
So basically i'm using ScaleGestureDetector with ScaleGestureDetector.SimpleOnScaleGestureListener, to scale my Canvas, here's a code:
private float MIN_SCALE_FACTOR = 0.3f;
private float MAX_SCALE_FACTOR = 10.0f;
private float mScaleFactor = 1.f;
private ScaleGestureDetector mScaleDetector;
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(MIN_SCALE_FACTOR, Math.min(mScaleFactor, MAX_SCALE_FACTOR));
invalidate();
return super.onScale(detector);
}
};
#Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.save();
super.onDraw(canvas);
correctPositionValues();
canvas.scale(mScaleFactor, mScaleFactor, mPositionX, mPositionY );
canvas.drawBitmap(mBitmap, mPositionX, mPositionY, null);
canvas.restore();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
}
On my phone scale is happening almoust instantly and this looks bad.
Any suggestions highly appreciated
Scale on the previous scale factor:
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
private preScaleFactor = 1f;
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor = preScaleFactor*detector.getScaleFactor();
mScaleFactor = Math.max(MIN_SCALE_FACTOR, Math.min(mScaleFactor, MAX_SCALE_FACTOR));
invalidate();
return super.onScale(detector);
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
preScaleFactor = mScaleFactor;
return true;
}
};
Kotlin:
class GestureScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
var multiplierScaleFactor = 0.05f
var scaleFactor = 1f
override fun onScale(detector: ScaleGestureDetector?): Boolean {
val detectorScale = (detector?.scaleFactor ?: 1f)
val realScale = detectorScale - 1f
scaleFactor = (multiplierScaleFactor * realScale) + 1
return super.onScale(detector)
}
}
For the first, need to calculate the difference between detectorScale and 1f (default scale). After that use multiplierScaleFactor (change it for your issue) value for reduce zoom speed/sensivity and return 1f (default scale) to the back.
I'm using opengl to render an image as texture.I want to zoom the texture,so i use ScaleGestureDetector.
Here is my code:
public void scale(float scale, float focusX, float focusY) {
Matrix.setIdentityM(uMVPMatrix, 0);
Matrix.translateM(uMVPMatrix, 0, focusX, focusY, 0f);
Matrix.scaleM(uMVPMatrix, 0, scale, scale, 0);
Matrix.translateM(uMVPMatrix, 0, -focusX, -focusY, 0f);
}
private float scale = 1f;
private float focusX;
private float focusY;
#Override
public boolean onScale(ScaleGestureDetector detector) {
renderer.scale(detector.getScaleFactor() * scale,focusX,focusY);
return false;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
focusX = (detector.getFocusX() - ScreenDimensionHelper.getScreenWidth(context)/2) / ((float) ScreenDimensionHelper.getScreenWidth(context)/2);
focusY = (ScreenDimensionHelper.getScreenHeight(context)/2- detector.getFocusY()) / ((float) ScreenDimensionHelper.getScreenHeight(context)/2);
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
this.scale = detector.getScaleFactor() * scale;
}
I want to zoom around the position where my finger touch.But I found that while I pinch the image,the image moves as well.Is there some better way to do this?
I am trying to implement pinch zoom and drag using Android's gesture listener and scale listener. The problem is that when I perform pinch zoom, the image (which I am trying to zoom) bounces to a particular location. Also the zoom position is not centered.
The following code demonstrates what I am trying to achieve. Any idea why the image is jumping (and how to correct it) ?
public class CustomView extends View {
Bitmap image;
int screenHeight;
int screenWidth;
Paint paint;
GestureDetector gestures;
ScaleGestureDetector scaleGesture;
float scale = 1.0f;
float horizontalOffset, verticalOffset;
int NORMAL = 0;
int ZOOM = 1;
int DRAG = 2;
boolean isScaling = false;
float touchX, touchY;
int mode = NORMAL;
public CustomView(Context context) {
super(context);
//initializing variables
image = BitmapFactory.decodeResource(getResources(),
R.drawable.image_name);
//This is a full screen view
screenWidth = getResources().getDisplayMetrics().widthPixels;
screenHeight = getResources().getDisplayMetrics().heightPixels;
paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(Color.WHITE);
scaleGesture = new ScaleGestureDetector(getContext(),
new ScaleListener());
gestures = new GestureDetector(getContext(), new GestureListener());
mode = NORMAL;
initialize();
}
//Best fit image display on canvas
private void initialize() {
float imgPartRatio = image.getWidth() / (float) image.getHeight();
float screenRatio = (float) screenWidth / (float) screenHeight;
if (screenRatio > imgPartRatio) {
scale = ((float) screenHeight) / (float) (image.getHeight()); // fit height
horizontalOffset = ((float) screenWidth - scale
* (float) (image.getWidth())) / 2.0f;
verticalOffset = 0;
} else {
scale = ((float) screenWidth) / (float) (image.getWidth()); // fit width
horizontalOffset = 0;
verticalOffset = ((float) screenHeight - scale
* (float) (image.getHeight())) / 2.0f;
}
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.drawColor(0, Mode.CLEAR);
canvas.drawColor(Color.WHITE);
if(mode == DRAG || mode == NORMAL) {
//This works perfectly as expected
canvas.translate(horizontalOffset, verticalOffset);
canvas.scale(scale, scale);
canvas.drawBitmap(image, getMatrix(), paint);
}
else if (mode == ZOOM) {
//PROBLEM AREA - when applying pinch zoom,
//the image jumps to a position abruptly
canvas.scale(scale, scale, touchX, touchY);
canvas.drawBitmap(image, getMatrix(), paint);
}
canvas.restore();
}
public class ScaleListener implements OnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactorNew = detector.getScaleFactor();
if (detector.isInProgress()) {
touchX = detector.getFocusX();
touchY = detector.getFocusY();
scale *= scaleFactorNew;
invalidate(0, 0, screenWidth, screenHeight);
}
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
isScaling = true;
mode=ZOOM;
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
mode = NORMAL;
isScaling = false;
}
}
public class GestureListener implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener {
#Override
public boolean onDown(MotionEvent e) {
isScaling = false;
return true;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (!isScaling) {
mode = DRAG;
isScaling = false;
horizontalOffset -= distanceX;
verticalOffset -= distanceY;
invalidate(0, 0, screenWidth, screenHeight);
} else {
mode = ZOOM;
isScaling = true;
}
return true;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
scaleGesture.onTouchEvent(event);
gestures.onTouchEvent(event);
return true;
}
}
Thanks in advance.
I have implemented this behaviour, and I used a matrix to handle all the zooming and scrolling (and rotation, in my case). It makes for neat code and works like clockwork.
Store a Matrix as a class member:
Matrix drawMatrix;
Edit: Store old focus point, used to get the focus shift during scaling.
float lastFocusX;
float lastFocusY;
Edit: Set lastFocus variables in onScaleBegin
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastFocusX = detector.getFocusX();
lastFocusY = detector.getFocusY();
return true;
}
Replace your onScale:
#Override
public boolean onScale(ScaleGestureDetector detector) {
Matrix transformationMatrix = new Matrix();
float focusX = detector.getFocusX();
float focusY = detector.getFocusY();
//Zoom focus is where the fingers are centered,
transformationMatrix.postTranslate(-focusX, -focusY);
transformationMatrix.postScale(detector.getScaleFactor(), detector.getScaleFactor());
/* Adding focus shift to allow for scrolling with two pointers down. Remove it to skip this functionality. This could be done in fewer lines, but for clarity I do it this way here */
//Edited after comment by chochim
float focusShiftX = focusX - lastFocusX;
float focusShiftY = focusY - lastFocusY;
transformationMatrix.postTranslate(focusX + focusShiftX, focusY + focusShiftY);
drawMatrix.postConcat(transformationMatrix);
lastFocusX = focusX;
lastFocusY = focusY;
invalidate();
return true;
}
Similarly in onScroll:
#Override
public boolean onScroll(MotionEvent downEvent, MotionEvent currentEvent,
float distanceX, float distanceY) {
drawMatrix.postTranslate(-distanceX, -distanceY);
invalidate();
return true;
}
in onDraw; Draw with your drawMatrix:
canvas.drawBitmap(image, drawMatrix, paint);
Happy coding!
I am going to draw numerous rectangles on a canvas that will create a map. I want to be able to zoom in and out on different parts of the map. I've looked at the other examples and none have proven successful yet. Any ideas?
public class Int_Map extends View {
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public Int_Map(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public boolean onTouchEvent(MotionEvent event){
mScaleDetector.onTouchEvent(event);
Region region = new Region(0, 0, getWidth(), getHeight()/2);
float x = event.getX();
float y = event.getY();
if(region.contains((int)x, (int)y))
{
Intent intent = new Intent(getContext(), Exhibit1.class);
((Activity)getContext()).startActivity(intent);
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
Rect rect = new Rect();
rect.set(0,0, canvas.getWidth(), canvas.getHeight()/2);
Drawable drawable = getResources().getDrawable(R.drawable.exhibit1);
drawable.setBounds(rect);
drawable.draw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}