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
Related
i have this code for rotation/magnification and move image view,now i need to apply these functions for text view,but i was not successful in this mission,can any one help me to do that whit editing this code or another code?thanks
public class MainActivity extends Activity implements OnTouchListener {
private EditText ed_text;
private ImageView view;
private LinearLayout toolbar_lay;
private FrameLayout frame1;
private Matrix mMatrix = new Matrix();
private float mScaleFactor = .4f;
private float mRotationDegrees = 0.f;
private float mFocusX = 0.f;
private float mFocusY = 0.f;
private int mAlpha = 255;
private int mImageHeight, mImageWidth;
private ImageView background;
private ScaleGestureDetector mScaleDetector;
private RotateGestureDetector mRotateDetector;
private MoveGestureDetector mMoveDetector;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frame3);
background=(ImageView) findViewById(R.id.frame_back);
Display display = getWindowManager().getDefaultDisplay();
mFocusX = display.getWidth()/2f;
mFocusY = display.getHeight()/2f;
// Set this class as touchListener to the ImageView
view = (ImageView) findViewById(R.id.id1);
Intent intent=getIntent();
String path=intent.getStringExtra("mybm");
Bitmap bm=BitmapFactory.decodeFile(path);
view.setImageBitmap(bm);
view.setOnTouchListener(this);
// Determine dimensions of 'earth' image
Drawable d = this.getResources().getDrawable(R.drawable.icon3);
mImageHeight = d.getIntrinsicHeight();
mImageWidth = d.getIntrinsicWidth();
float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postTranslate(mFocusX - scaledImageCenterX,
mFocusY - scaledImageCenterY);
view.setImageMatrix(mMatrix);
// Setup Gesture Detectors
mScaleDetector = new ScaleGestureDetector(getApplicationContext(),
new ScaleListener());
mRotateDetector = new RotateGestureDetector(getApplicationContext(),
new RotateListener());
mMoveDetector = new MoveGestureDetector(getApplicationContext(),
new MoveListener());
//mShoveDetector =
new ShoveGestureDetector(getApplicationContext(),
new ShoveListener());
}
#SuppressWarnings("deprecation")
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
// mShoveDetector.onTouchEvent(event);
float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;
mMatrix.reset();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postRotate(mRotationDegrees,
scaledImageCenterX, scaledImageCenterY);
mMatrix.postTranslate(mFocusX - scaledImageCenterX,
mFocusY - scaledImageCenterY);
ImageView view = (ImageView) v;
view.setImageMatrix(mMatrix);
view.setAlpha(mAlpha);
return true; // indicate event was handled
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
return true;
}
}
private class RotateListener
extends RotateGestureDetector.SimpleOnRotateGestureListener
{
#Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegrees -= detector.getRotationDegreesDelta();
return true;
}
}
private class MoveListener
extends MoveGestureDetector.SimpleOnMoveGestureListener {
#Override
public boolean onMove(MoveGestureDetector detector) {
PointF d = detector.getFocusDelta();
mFocusX += d.x;
mFocusY += d.y;
// mFocusX = detector.getFocusX();
// mFocusY = detector.getFocusY();
return true;
}
}
}
I think you have right idea going with treating the text as an
ImageView. But drop the conversion from a TextView. Instead treat the text image all along as an ImageView. Create a blank Canvas and a Paint, use Canvas.drawText to draw your text in the appropriate place in the Canvas's bitmap, and then use the image manipulation code above to expand/shrink/rotate your text.
Hi i am erasing bitmap that is draw at canvas with touch (fingers) that is working fine the problem i am facing is after rotate bitmap at canvas paths draw in opposition direction mean bitmap erase in opposition direction of my finger touch .
DrawingPane.class
public class DrawingPanel extends ImageView implements OnTouchListener {
private Matrix mMatrix = new Matrix();
private float mScaleFactor = 1f;
private float mRotationDegrees = 0.f;
private float mFocusX = 0.f;
private float mFocusY = 0.f;
private int mAlpha = 255;
private int mImageHeight, mImageWidth;
private ScaleGestureDetector mScaleDetector;
private RotateGestureDetector mRotateDetector;
private MoveGestureDetector mMoveDetector;
private ShoveGestureDetector mShoveDetector;
private boolean isMoving=false;
EditPhotoActivity editActivity;
Bitmap overlayDefault;
Bitmap overlay;
Bitmap bmp,bmp2;
Paint pTouch;
int whichTabSelected=0;
private Path mPath;
Display display ;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Float> xlist = new ArrayList<Float>();
private ArrayList<Float> ylist = new ArrayList<Float>();
#SuppressLint("NewApi")
public DrawingPanel(Context context, int colorPaint,Bitmap bmp) {
super(context);
if (Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
display = ((Activity)context).getWindowManager().getDefaultDisplay();
mFocusX = display.getWidth()/2f;
mFocusY = display.getHeight()/2f;
try {
overlayDefault=bmp;
overlay=bmp;
overlay=overlay.copy(Config.ARGB_8888, true);
overlay.setHasAlpha(true);
} catch (Exception e) {
e.printStackTrace();
}
mImageHeight = getHeight();
mImageWidth = getWidth();
// Setup Gesture Detectors
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mRotateDetector = new RotateGestureDetector(context, new RotateListener());
mMoveDetector = new MoveGestureDetector(context, new MoveListener());
mShoveDetector = new ShoveGestureDetector(context, new ShoveListener());
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
pTouch.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
pTouch.setColor(Color.TRANSPARENT);
//pTouch.setMaskFilter(new BlurMaskFilter(30, Blur.SOLID));
pTouch.setStyle(Paint.Style.STROKE);
pTouch.setStrokeJoin(Paint.Join.ROUND);
pTouch.setStrokeCap(Paint.Cap.ROUND);
pTouch.setStrokeWidth(50);
pTouch.setAntiAlias(true);
setFocusable(true);
setFocusableInTouchMode(true);
mPath = new Path();
paths.add(mPath);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mImageHeight=getHeight();
mImageWidth=getWidth();
bmp = Bitmap.createScaledBitmap(overlay, w, h, false);
bmp2 = Bitmap.createScaledBitmap(overlayDefault, w, h, false);
overlay = bmp.copy(Config.ARGB_8888, true);
overlayDefault = bmp2.copy(Config.ARGB_8888, true);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
// mCanvas.drawBitmap(overlayDefault,0, 0, null); //exclude this line to show all as you draw
// mCanvas.drawCircle(X, Y, 80, pTouch);
//draw the overlay over the background
float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;
mMatrix.reset();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postRotate(mRotationDegrees, scaledImageCenterX, scaledImageCenterY);
if(isMoving)
{
mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
}
else
{
mMatrix.postTranslate(0,0);
}
canvas.setMatrix(mMatrix);
canvas.drawBitmap(overlay,0,0, null);
for (Path p : paths) {
canvas.drawPath(p, pTouch);
}
super.onDraw(canvas);
}
public Bitmap getBitmap(){
Bitmap b = Bitmap.createScaledBitmap(overlay,display.getWidth() ,display.getWidth(), false);
overlay = b.copy(Config.ARGB_8888, true);
return overlay;
}
public void setBitmap(Bitmap bmp1){
overlay = bmp1;
invalidate();
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 0;
public void touch_start(float x, float y) {
if(xlist.size()>0 && ylist.size()>0){
xlist.clear();
ylist.clear();
}
xlist.add(x);
ylist.add(y);
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
mPath.transform(mMatrix, mPath);
invalidate();
}
public void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
mPath.transform(mMatrix, mPath);
}
xlist.add(x);
ylist.add(y);
invalidate();
}
public void touch_up() {
mPath.lineTo(mX, mY);
mPath = new Path();
mPath.transform(mMatrix, mPath);
paths.add(mPath);
invalidate();
}
public void OnTouchParent(MotionEvent event){
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
mShoveDetector.onTouchEvent(event);
float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;
mMatrix.reset();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postRotate(mRotationDegrees, scaledImageCenterX, scaledImageCenterY);
mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
float x = event.getX();
float y = event.getY();
/*switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(whichTabSelected==Constant.ERASE)
{
touch_start(x, y);
invalidate();
}
break;
case MotionEvent.ACTION_MOVE:
if(whichTabSelected==Constant.ERASE)
{
touch_move(x, y);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if(whichTabSelected==Constant.ERASE)
{
touch_up();
invalidate();
}
break;
}
if(whichTabSelected==Constant.ERASE)
{
return true;
}
else
{
return false;
}*/
invalidate();
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
if(getTabMode()==Constant.PANZOOM)
{
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
mShoveDetector.onTouchEvent(event);
float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;
}
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(getTabMode()==Constant.ERASE)
{
touch_start(x, y);
invalidate();
}
break;
case MotionEvent.ACTION_MOVE:
if(getTabMode()==Constant.ERASE)
{
touch_move(x, y);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if(getTabMode()==Constant.ERASE)
{
touch_up();
invalidate();
}
break;
}
invalidate();
return true;
}
public void setBottomTabMode(int mode)
{
whichTabSelected=mode;
}
public int getTabMode()
{
return whichTabSelected;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor(); // scale change since previous event
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
return true;
}
}
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
#Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegrees -= detector.getRotationDegreesDelta();
return true;
}
}
private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
#Override
public boolean onMove(MoveGestureDetector detector) {
PointF d = detector.getFocusDelta();
mFocusX += d.x;
mFocusY += d.y;
isMoving=true;
// mFocusX = detector.getFocusX();
// mFocusY = detector.getFocusY();
return true;
}
}
private class ShoveListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
#Override
public boolean onShove(ShoveGestureDetector detector) {
mAlpha += detector.getShovePixelsDelta();
if (mAlpha > 255)
mAlpha = 255;
else if (mAlpha < 0)
mAlpha = 0;
return true;
}
}
}
I fixed my problem. Actually when i rotate canvas the event.getX() and event.getY() were not map to current rotation of matrix so by adding this line in mMatrix.invert(tempMatrix); in OnDraw() and also map current x,y in OnTouch() by adding this in OnTouch() method .
float[] coords = new float[] { event.getX(), event.getY() };
tempMatrix.mapPoints(coords);
float x = coords[0];//event.getX();
float y = coords[1];//event.getY();
its working fine .
This effect is happening because you are applying the matrix twice to paths.
Once at touch_start/touch_move by doing mPath.transform(mMatrix, mPath);.
And then again at onDraw(Canvas canvas) by canvas.setMatrix(mMatrix); and then canvas.drawPath(p, pTouch);.
To fix, try to remove the mPath.transform(mMatrix, mPath); from touch_start/touch_move.
Also, I do not know if it is a good practice to set the matrix directly to the canvas. Instead of canvas.setMatrix(mMatrix);, I would prefer to do the following:
canvas.save();
canvas.concat(mMatrix);
//write the code....
canvas.restore();
I was working with the Custom RelativeLayout because I want to zoom scale and rotate whatever views present in that layout.
These features are working correctly but the problem is when I insert the button in layout and move the layout on screen the button doesn't respond but the position where it was originally or initially located responds.
Can someone tell me where I am doing wrong.
Please help.
Here is Custom RelativeLayout Code:
public class ZoomableRelativeLayout extends RelativeLayout implements
OnTouchListener {
// float mScaleFactor = 1;
float mPivotX;
float mPivotY;
/*
* New Declarations
*/
private float mScaleFactor = 0.6f;
private float mRotationDegrees = 0.f;
private float changeInRotationDegrees;
private float mFocusX;
private float mFocusY;
private float layoutCenterX, layoutCenterY;
private ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(
getContext(), new ScaleListener());
private RotateGestureDetector mRotateDetector = new RotateGestureDetector(
getContext(), new RotateListener());
private MoveGestureDetector mMoveDetector = new MoveGestureDetector(
getContext(), new MoveListener());
private boolean firstTym = true;
// private ShoveGestureDetector mShoveDetector;
/*
* End of New Declarations
*/
public ZoomableRelativeLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
// setOnTouchListener(this);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#SuppressWarnings("deprecation")
public ZoomableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
setOnTouchListener(this);
MyMainActivity mContext = (MyMainActivity) context;
Display defaultDisplay = mContext.getWindowManager()
.getDefaultDisplay();
mFocusX = defaultDisplay.getWidth() / 2f;
mFocusY = defaultDisplay.getHeight() / 2f;
}
public ZoomableRelativeLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
// setOnTouchListener(this);
}
protected void dispatchDraw(Canvas canvas) {
if (firstTym) {
layoutCenterX = (getWidth() * mScaleFactor) / 2;
layoutCenterY = (getHeight() * mScaleFactor) / 2;
// Toast.makeText(getContext(), ""+layoutCenterX+"***"+layoutCenterY, 2000).show();
firstTym = false;
}
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.rotate(mRotationDegrees, layoutCenterX,layoutCenterY);
canvas.translate((mFocusX - layoutCenterX), (mFocusY - layoutCenterY));
super.dispatchDraw(canvas);
canvas.restore();
}
#SuppressWarnings("deprecation")
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
layoutCenterX = (v.getWidth() * mScaleFactor) / 2;
layoutCenterY = (v.getHeight() * mScaleFactor) / 2;
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
return true;
}
#TargetApi(Build.VERSION_CODES.FROYO)
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor(); // scale change since
// previous event
ZoomableRelativeLayout.this.invalidate();
// Don't let the object get too small or too large.
// mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
return true;
}
}
private class RotateListener extends
RotateGestureDetector.SimpleOnRotateGestureListener {
#Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegrees -= detector.getRotationDegreesDelta();
ZoomableRelativeLayout.this.invalidate();
return true;
}
}
private class MoveListener extends
MoveGestureDetector.SimpleOnMoveGestureListener {
#Override
public boolean onMove(MoveGestureDetector detector) {
PointF d = detector.getFocusDelta();
mFocusX += d.x;
mFocusY += d.y;
ZoomableRelativeLayout.this.invalidate();
return true;
}
}
}
As you are returning true on the overriden onTouch method, it means that the event won't go deeper in your view hierarchy, and therefore it will never hit the button. Try to give the MotionEvent directly to the button:
(you must reference it in your ZoomableRelativeLayout first)
#Override
public boolean onTouch(View v, MotionEvent event) {
layoutCenterX = (v.getWidth() * mScaleFactor) / 2;
layoutCenterY = (v.getHeight() * mScaleFactor) / 2;
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
myButton.onTouch(event);
return true;
}
Button myButton;
public void setMyButton(Button b) {
this.myButton = b;
}
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;
}
}
}