I'm writing a application that there are a upper-imageview and a scollview under the imageview.
The imageview is used by a class provided by my friend, it supports pinch zoom, but after i zood in the image then i touch the scollview below, the image will resize to fit the imageview's size.
It's a big trouble for me to build my app because i want to scroll the information after zooming.
Then i try to change the scrollview to be a imageview as well, i find that there is not resizimg between two views.
I wonder if it is possible to disable the resizing function for the imageview to implement my idea.
The class below is used for the imageview.
Thank you for solutions.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
float redundantXSpace, redundantYSpace;
float width, height;
static final int CLICK = 3;
float saveScale = 1f;
float right, bottom, origWidth, origHeight, bmWidth, bmHeight;
int windowId;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context,int id) {
super(context);
super.setClickable(true);
this.context = context;
this.windowId=id;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
setBackgroundColor(Color.BLACK);
matrix.setTranslate(1f, 1f);
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
EasyPageActivity.resetView();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float scaleWidth = Math.round(origWidth * saveScale);
float scaleHeight = Math.round(origHeight * saveScale);
if (scaleWidth < width) {
deltaX = 0;
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
} else if (scaleHeight < height) {
deltaY = 0;
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
} else {
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
}
matrix.postTranslate(deltaX, deltaY);
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
if(windowId==1){
EasyPageActivity.defineId(1);
}
if(windowId==2){
EasyPageActivity.defineId(2);
}
if(windowId==3){
EasyPageActivity.defineId(3);
}
if(windowId==4){
EasyPageActivity.defineId(4);
}
if(windowId==5){
EasyPageActivity.defineId(5);
}
if(windowId==6){
EasyPageActivity.defineId(6);
}
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
bmWidth = bm.getWidth();
bmHeight = bm.getHeight();
}
public void setMaxZoom(float x)
{
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = (float)Math.min(Math.max(.301f, detector.getScaleFactor()), 4);
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
if (origWidth * saveScale <= width || origHeight * saveScale <= height) {
matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
if (mScaleFactor < 1) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * saveScale) < width) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
return true;
}
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
//Fit to screen.
float scale;
float scaleX = (float)width / (float)bmWidth;
float scaleY = (float)height / (float)bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
saveScale = 1f;
// Center the image
redundantYSpace = (float)height - (scale * (float)bmHeight) ;
redundantXSpace = (float)width - (scale * (float)bmWidth);
redundantYSpace /= (float)2;
redundantXSpace /= (float)2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = width - 2 * redundantXSpace;
origHeight = height - 2 * redundantYSpace;
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
setImageMatrix(matrix);
}
}
Did you find any solution?
If not, then you must understand that your image re-sizes itself to fit the screen in the onMeasure method. So, to detect the problem you must debug and see why onMeasure is called after you change the focus to the ScrollView. Try to put some logs and see the flow of actions.
Related
i have seen many answers about this question. but none of those worked perfectly for me. I just want to know how to display and immageview thumbnail by clicking/touching it into full screen view and also it enables to zoomin zoomout.
thank you
Instead of ImageView Use TouchImageView class and open it in full
Screen
public class TouchImageView extends ImageView {
Matrix matrix;
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight || viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
// Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
I have an ImageView and i want to zoom/drag on by two fingers and draw on it using one finger , but after scaling ImageView , bitmap coordinates and ImageView coordinates doesn't match , to solve this i provided onTouch like this :
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
final int NONE = 0;
final int DRAW = 1;
final int PINCH = 2;
float x,y;
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
drawStartX = event.getX();
drawStartY = event.getY();
imageMode = DRAW;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//drag
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
x = event.getX(0) - event.getX(1);
y = event.getY(0) - event.getY(1);
oldDistance = (float) Math.sqrt(x * x + y * y);
if (oldDistance > 5f) {
savedMatrix.set(matrix);
x = event.getX(0) + event.getX(1);
y = event.getY(0) + event.getY(1);
mid.set(x / 2, y / 2);
imageMode = PINCH;
}
break;
case MotionEvent.ACTION_UP:
imageMode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
imageMode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (imageMode == DRAW) {
float drawEndX = event.getX();
float drawEndY = event.getY();
drawTest(drawStartX/bitmapScale - xTranslate, drawStartY/bitmapScale- yTranslate,drawEndX/bitmapScale - xTranslate,drawEndY/bitmapScale - yTranslate,0);
} else if (imageMode == PINCH) {
x = event.getX(0) - event.getX(1);
y = event.getY(0) - event.getY(1);
float newDistance = (float) Math.sqrt(x * x + y * y);
matrix.set(savedMatrix);
if(newDistance > 5f) {
//drag
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
//zoom
float scale = (newDistance / oldDistance);
matrix.postScale(scale, scale);
}
//get matrix
float[] mat = new float[9];
matrix.getValues(mat);
bitmapScale = mat[0];
xTranslate = mat[2];
yTranslate = mat[5];
}
break;
}
view.setImageMatrix(matrix);
return true;
}
drawTest is just a void that draws a line.
pic
in before scale picture i drag my finger on imageview and line drawn in exact position , but after scaling i dragged on same position but result is not right
but it's not working properly. how can i fix it ?
I've tried modifying xml without success.
Currently this happens: The image appears in the center of my TouchImageView.
╔══════════════════════════════════════════════╗
║ TouchImageView ║
║ ╔══════════════╗ ║
║ ║ ║ (Center) ║
║ ║ Actual image ║ ║
║ ║ ║ ║
║ ║ (Center) ║ ║
║ ║ ║ ║
║ ╚══════════════╝ ║
║ ║
╚══════════════════════════════════════════════╝
Want this: The image should appear at the top left corner of my TouchImageView.
╔══════════════════════════════════════════════╗
║╔══════════════╗ ║
║║ ║ ║
║║ Actual image ║ ║
║║ ║ ║
║║ ║ ║
║║ ║ ║
║╚══════════════╝ ║
║ ║
║ TouchImageView ║
╚══════════════════════════════════════════════╝
This is the code for the TouchImageView I'm using.
How do I make the necessary changes so the image appears on the top left corner of the TouchImageView instead of smack in the center?
enter code here
import android.app.Activity;
import android.content.Context;
import android.graphics.Matrix;`enter code here`
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix;
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void hideSoftKeyboard() {
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(this.getWindowToken(), 0);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
requestFocus();
hideSoftKeyboard();
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
//Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
Thanks!
Just remove the translation that centers the image.
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
// take out the translation that centers the image
// leave all the other calculations since they are used
// to determine origWidth and origHeight
// matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
To support the imageView scaleType (ScaleType.FIT_START and ScaleType.FIT_XY), the following code can be modified in TouchImageView class:
For ScaleType.FIT_START, remove the following line:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// matrix.postTranslate(redundantXSpace, redundantYSpace);
}
For ScaleType.FIT_XY, replace the following line:
#Override
public boolean onScale(ScaleGestureDetector detector) {
// matrix.postScale(scaleFactor, scaleFactor, mViewWidth / 2,
// mViewHeight / 2);
//Replace this line with:
matrix.setScale(scaleX,scaleY);
}
I need to draw the rectangle from the center of the canvas based on ACTION_MOVE.
I tried this way
public class CustomRectangle extends View {
private Bitmap bitTopLeft;
private Bitmap bitTopRight;
private Bitmap bitBottomLeft;
private Bitmap bitBottomRight;
private Paint rectAnglePaint;
private Context context;
private Paint bitmapPaint;
private Rect rect;
private int maxX;
private int maxY;
private int centerX;
private int centerY;
private Paint canvasPaint;
public CustomRectangle(Context context) {
super(context);
this.context = context;
Init();
}
private void Init() {
bitTopLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitTopRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
bitmapPaint = new Paint();
bitmapPaint.setColor(Color.BLACK);
rect = new Rect();
Display display = ((Activity) context).getWindowManager()
.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
maxX = size.x;
maxY = size.y;
centerX = maxX / 2;
centerY = maxY / 2;
rect.left = centerX / 2 + bitBottomRight.getWidth() / 2;
rect.top = centerY / 2 + bitBottomRight.getWidth() / 2;
rect.right = centerX + centerX / 2 + bitBottomRight.getWidth() / 2;
rect.bottom = centerY + centerY / 2 + bitBottomRight.getWidth() / 2;
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPaint(canvasPaint);
// Path p = new Path();
canvas.drawBitmap(bitTopLeft,
rect.left - bitBottomRight.getWidth() / 2, rect.top
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitTopRight, rect.right - bitBottomRight.getWidth()
/ 2, rect.top - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomLeft, rect.left - bitBottomRight.getWidth()
/ 2, rect.bottom - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomRight,
rect.right - bitBottomRight.getWidth() / 2, rect.bottom
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawRect(rect, rectAnglePaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
invalidate();
break;
case MotionEvent.ACTION_MOVE:
int currentx = (int) event.getX();
int currenty = (int) event.getY();
Calculatingpoint(currentx, currenty);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
private void Calculatingpoint(int x, int y) {
if (x < maxX && y < maxY) {
int dx = 0;
int dy = 0;
dx = rect.right - x;
dy = rect.bottom - y;
// this is working perfect when i touched right bottom.
rect.inset(dx, dy);
invalidate();
}
}
}
But when i touch other corners the rectangle is gone.
I need to draw the rectangle from center point. i need to increase or decrease the width and height based on the touch.
Please advice, how to achieve this. Please give me samples.
I find the Direction from the user touching position, Here is my updated code.
public class CustomRectangle extends View {
private Bitmap bitTopLeft;
private Bitmap bitTopRight;
private Bitmap bitBottomLeft;
private Bitmap bitBottomRight;
private Paint rectAnglePaint;
private Context context;
private Paint bitmapPaint;
private Rect rect;
private int maxX;
private int maxY;
private int centerX;
private int centerY;
private Paint canvasPaint;
private String direction;
public CustomRectangle(Context context) {
super(context);
this.context = context;
Init();
}
private void Init() {
bitTopLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitTopRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomLeft = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
bitBottomRight = BitmapFactory.decodeResource(context.getResources(),
R.drawable.squarsmall);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
rectAnglePaint = new Paint();
rectAnglePaint.setColor(Color.GREEN);
rectAnglePaint.setStrokeWidth(5);
rectAnglePaint.setStyle(Paint.Style.STROKE);
bitmapPaint = new Paint();
bitmapPaint.setColor(Color.BLACK);
rect = new Rect();
Display display = ((Activity) context).getWindowManager()
.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
maxX = size.x;
maxY = size.y;
centerX = maxX / 2;
centerY = maxY / 2;
rect.left = centerX / 2 + bitBottomRight.getWidth() / 2;
rect.top = centerY / 2 + bitBottomRight.getWidth() / 2;
rect.right = centerX + centerX / 2 + bitBottomRight.getWidth() / 2;
rect.bottom = centerY + centerY / 2 + bitBottomRight.getWidth() / 2;
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPaint(canvasPaint);
// Path p = new Path();
canvas.drawBitmap(bitTopLeft,
rect.left - bitBottomRight.getWidth() / 2, rect.top
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitTopRight, rect.right - bitBottomRight.getWidth()
/ 2, rect.top - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomLeft, rect.left - bitBottomRight.getWidth()
/ 2, rect.bottom - bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawBitmap(bitBottomRight,
rect.right - bitBottomRight.getWidth() / 2, rect.bottom
- bitBottomRight.getWidth() / 2, bitmapPaint);
canvas.drawRect(rect, rectAnglePaint);
}
float x1, x2, y1, y2, dx, dy;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = event.getRawX();
y1 = event.getRawY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
x2 = event.getRawX();
y2 = event.getRawY();
int currentx = (int) event.getX();
int currenty = (int) event.getY();
Calculatingpoint(currentx, currenty);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
private void Calculatingpoint(int x, int y) {
if (x2 < maxX && y2 < maxY) {
int dx = 0;
int dy = 0;
// dx = rect.right - (int)x2 ;
// dy = (int)y2 - rect.top ;
//
// Log.e("dx", ""+dx);
// Log.e("dy", ""+dy);
boolean isLeft = false;
boolean isTop = false;
if (x2 > maxX / 2) {
direction = "right";
isLeft = false;
} else {
direction = "left";
isLeft = true;
}
if (y2 > maxY / 2) {
direction = "bottom";
isTop = false;
} else {
direction = "top";
isTop = true;
}
if (!isLeft && !isTop) {
dx = rect.right - (int) x2;
dy = rect.bottom - (int) y2;
}
if (isLeft && !isTop) {
dx = (int) x2 - rect.left;
dy = rect.bottom - (int) y2;
}
if (isLeft && isTop) {
dx = (int) x2 - rect.left;
dy = (int) y2 - rect.top;
}
if (!isLeft && isTop) {
dx = rect.right - (int) x2;
dy = (int) y2 - rect.top;
}
// this will perfect
rect.inset(dx, dy);
invalidate();
}
}
}
I have an OBB2D class based on SAT.
This is my point in OBB method:
public boolean pointInside(float x, float y)
{
float newy = (float) (Math.sin(angle) * (y - center.y) + Math.cos(angle) *
(x - center.x));
float newx = (float) (Math.cos(angle) * (x - center.x) - Math.sin(angle) *
(y - center.y));
return (newy > center.y - (getHeight() / 2)) &&
(newy < center.y + (getHeight() / 2))
&& (newx > center.x - (getWidth() / 2)) &&
(newx < center.x + (getWidth() / 2));
}
public boolean pointInside(Vector2D v)
{
return pointInside(v.x,v.y);
}
Here is the rest of the class; the parts that pertain:
public class OBB2D
{
private Vector2D projVec = new Vector2D();
private static Vector2D projAVec = new Vector2D();
private static Vector2D projBVec = new Vector2D();
private static Vector2D tempNormal = new Vector2D();
private Vector2D deltaVec = new Vector2D();
private ArrayList<Vector2D> collisionPoints = new ArrayList<Vector2D>();
// Corners of the box, where 0 is the lower left.
private Vector2D corner[] = new Vector2D[4];
private Vector2D center = new Vector2D();
private Vector2D extents = new Vector2D();
private RectF boundingRect = new RectF();
private float angle;
//Two edges of the box extended away from corner[0].
private Vector2D axis[] = new Vector2D[2];
private double origin[] = new double[2];
public OBB2D(float centerx, float centery, float w, float h, float angle)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(centerx,centery,w,h,angle);
}
public OBB2D(float left, float top, float width, float height)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(left + (width / 2), top + (height / 2),width,height,0.0f);
}
public void set(float centerx,float centery,float w, float h,float angle)
{
float vxx = (float)Math.cos(angle);
float vxy = (float)Math.sin(angle);
float vyx = (float)-Math.sin(angle);
float vyy = (float)Math.cos(angle);
vxx *= w / 2;
vxy *= (w / 2);
vyx *= (h / 2);
vyy *= (h / 2);
corner[0].x = centerx - vxx - vyx;
corner[0].y = centery - vxy - vyy;
corner[1].x = centerx + vxx - vyx;
corner[1].y = centery + vxy - vyy;
corner[2].x = centerx + vxx + vyx;
corner[2].y = centery + vxy + vyy;
corner[3].x = centerx - vxx + vyx;
corner[3].y = centery - vxy + vyy;
this.center.x = centerx;
this.center.y = centery;
this.angle = angle;
computeAxes();
extents.x = w / 2;
extents.y = h / 2;
computeBoundingRect();
}
//Updates the axes after the corners move. Assumes the
//corners actually form a rectangle.
private void computeAxes()
{
axis[0].x = corner[1].x - corner[0].x;
axis[0].y = corner[1].y - corner[0].y;
axis[1].x = corner[3].x - corner[0].x;
axis[1].y = corner[3].y - corner[0].y;
// Make the length of each axis 1/edge length so we know any
// dot product must be less than 1 to fall within the edge.
for (int a = 0; a < axis.length; ++a)
{
float l = axis[a].length();
float ll = l * l;
axis[a].x = axis[a].x / ll;
axis[a].y = axis[a].y / ll;
origin[a] = corner[0].dot(axis[a]);
}
}
public void computeBoundingRect()
{
boundingRect.left = JMath.min(JMath.min(corner[0].x, corner[3].x), JMath.min(corner[1].x, corner[2].x));
boundingRect.top = JMath.min(JMath.min(corner[0].y, corner[1].y),JMath.min(corner[2].y, corner[3].y));
boundingRect.right = JMath.max(JMath.max(corner[1].x, corner[2].x), JMath.max(corner[0].x, corner[3].x));
boundingRect.bottom = JMath.max(JMath.max(corner[2].y, corner[3].y),JMath.max(corner[0].y, corner[1].y));
}
public void set(RectF rect)
{
set(rect.centerX(),rect.centerY(),rect.width(),rect.height(),0.0f);
}
// Returns true if other overlaps one dimension of this.
private boolean overlaps1Way(OBB2D other)
{
for (int a = 0; a < axis.length; ++a) {
double t = other.corner[0].dot(axis[a]);
// Find the extent of box 2 on axis a
double tMin = t;
double tMax = t;
for (int c = 1; c < corner.length; ++c) {
t = other.corner[c].dot(axis[a]);
if (t < tMin) {
tMin = t;
} else if (t > tMax) {
tMax = t;
}
}
// We have to subtract off the origin
// See if [tMin, tMax] intersects [0, 1]
if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
// There was no intersection along this dimension;
// the boxes cannot possibly overlap.
return false;
}
}
// There was no dimension along which there is no intersection.
// Therefore the boxes overlap.
return true;
}
public void moveTo(float centerx, float centery)
{
float cx,cy;
cx = center.x;
cy = center.y;
deltaVec.x = centerx - cx;
deltaVec.y = centery - cy;
for (int c = 0; c < 4; ++c)
{
corner[c].x += deltaVec.x;
corner[c].y += deltaVec.y;
}
boundingRect.left += deltaVec.x;
boundingRect.top += deltaVec.y;
boundingRect.right += deltaVec.x;
boundingRect.bottom += deltaVec.y;
this.center.x = centerx;
this.center.y = centery;
computeAxes();
}
// Returns true if the intersection of the boxes is non-empty.
public boolean overlaps(OBB2D other)
{
if(right() < other.left())
{
return false;
}
if(bottom() < other.top())
{
return false;
}
if(left() > other.right())
{
return false;
}
if(top() > other.bottom())
{
return false;
}
if(other.getAngle() == 0.0f && getAngle() == 0.0f)
{
return true;
}
return overlaps1Way(other) && other.overlaps1Way(this);
}
public Vector2D getCenter()
{
return center;
}
public float getWidth()
{
return extents.x * 2;
}
public float getHeight()
{
return extents.y * 2;
}
public void setAngle(float angle)
{
set(center.x,center.y,getWidth(),getHeight(),angle);
}
public float getAngle()
{
return angle;
}
public void setSize(float w,float h)
{
set(center.x,center.y,w,h,angle);
}
public float left()
{
return boundingRect.left;
}
public float right()
{
return boundingRect.right;
}
public float bottom()
{
return boundingRect.bottom;
}
public float top()
{
return boundingRect.top;
}
public RectF getBoundingRect()
{
return boundingRect;
}
public boolean overlaps(float left, float top, float right, float bottom)
{
if(right() < left)
{
return false;
}
if(bottom() < top)
{
return false;
}
if(left() > right)
{
return false;
}
if(top() > bottom)
{
return false;
}
return true;
}
public static float distance(float ax, float ay,float bx, float by)
{
if (ax < bx)
return bx - ay;
else
return ax - by;
}
public Vector2D project(float ax, float ay)
{
projVec.x = Float.MAX_VALUE;
projVec.y = Float.MIN_VALUE;
for (int i = 0; i < corner.length; ++i)
{
float dot = Vector2D.dot(corner[i].x,corner[i].y,ax,ay);
projVec.x = JMath.min(dot, projVec.x);
projVec.y = JMath.max(dot, projVec.y);
}
return projVec;
}
public Vector2D getCorner(int c)
{
return corner[c];
}
public int getNumCorners()
{
return corner.length;
}
public boolean pointInside(float x, float y)
{
float newy = (float) (Math.sin(angle) * (y - center.y) + Math.cos(angle) *
(x - center.x));
float newx = (float) (Math.cos(angle) * (x - center.x) - Math.sin(angle) *
(y - center.y));
return (newy > center.y - (getHeight() / 2)) &&
(newy < center.y + (getHeight() / 2))
&& (newx > center.x - (getWidth() / 2)) &&
(newx < center.x + (getWidth() / 2));
}
public boolean pointInside(Vector2D v)
{
return pointInside(v.x,v.y);
}
public ArrayList<Vector2D> getCollsionPoints(OBB2D b)
{
collisionPoints.clear();
for(int i = 0; i < corner.length; ++i)
{
if(b.pointInside(corner[i]))
{
collisionPoints.add(corner[i]);
}
}
for(int i = 0; i < b.corner.length; ++i)
{
if(pointInside(b.corner[i]))
{
collisionPoints.add(b.corner[i]);
}
}
return collisionPoints;
}
};
What could be wrong? When I getCollisionPoints for 2 OBBs I know are penetrating, it returns no points.
Thanks
I also tried:
public boolean pointInside(float x, float y)
{
float xx = (x - center.x);
float yy = (y - center.y);
float newx = (float) (xx * Math.cos(angle) - yy * Math.sin(angle));
float newy = (float) (xx * Math.sin(angle) + yy * Math.cos(angle));
return (newy > center.y - (getHeight() / 2)) &&
(newy < center.y + (getHeight() / 2))
&& (newx > center.x - (getWidth() / 2)) &&
(newx < center.x + (getWidth() / 2));
}
With no luck.
I didn't read all of your class, but I'm assuming angle is the angle by which you would need to rotate your rectangle clockwise in order to make it axis-aligned.
I believe that both sin(angle) * (y-center.y) and cos(angle) * (x-center.x) are equal to the distance between your center point and the point you're testing. So newy will always equal twice that distance, and newx will always equal 0.
This is how I prefer to rotate a point about another point: get the angle and distance between the two points, then apply the rotation to the angle, then calculate the new position from the angle and distance. In pseudocode:
//takes a point and rotates it `theta` angles
//counterclockwise around the given center point
function rotateAboutPoint(x,y, centerX, centerY, theta){
radius = sqrt((centerX-x)**2 + (centerY-y)**2) //`**` is the exponentiation operator
currentAngle = atan2(y-centerY, x-centerX) //prefer `atan2` over ordinary `atan` if you can get it
newAngle = currentAngle + theta
newX = centerX + radius*cos(newAngle)
newY = centerY + radius*sin(newAngle)
return (newX, newY)
}
function pointInside(x,y){
//point must be rotated clockwise, so we provide a negative angle
newX, newY = rotateAboutPoint(x,y,center.x, center.y, -angle)
return (
newY > center.y - (getHeight() / 2) &&
newY < center.y + (getHeight() / 2) &&
newX > center.x - (getHeight() / 2) &&
newX < center.x + (getHeight() / 2) &&
)
}