Updating ImageView with a new Bitmap resets the matrix - java

I am trying to freely translate Bitmap inside an imageView.
I have ImageView with MATRIX scaletype and width and height match_parent
Here is my code for Touch listener
Following is my code for touch
public class Touch implements OnTouchListener
{
// These matrices will be used to move and zoom image
public Matrix matrix = new Matrix();
Matrix savedMatrix = 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 start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
float[] lastEvent = null;
float d = 0f;
float newRot = 0f;
private float dx; // postTranslate X distance
private float dy; // postTranslate Y distance
private float[] matrixValues = new float[9];
float matrixX = 0; // X coordinate of matrix inside the ImageView
float matrixY = 0; // Y coordinate of matrix inside the ImageView
float width = 0; // width of drawable
float height = 0; // height of drawable
View mainView;
#Override
public boolean onTouch(View v, MotionEvent event)
{
ImageView view = (ImageView) v;
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
lastEvent = null;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f)
{
//savedMatrix.set(matrix);
//midPoint(mid, event);
//mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG)
{
matrix.set(savedMatrix);
matrix.getValues(matrixValues);
matrixX = matrixValues[2];
matrixY = matrixValues[5];
width = matrixValues[0] * (((ImageView) view).getDrawable()
.getIntrinsicWidth());
height = matrixValues[4] * (((ImageView) view).getDrawable()
.getIntrinsicHeight());
dx = event.getX() - start.x;
dy = event.getY() - start.y;
//if image will go outside left bound
if (matrixX + dx < 0){
dx = -matrixX;
}
//if image will go outside right bound
if(matrixX + dx + width > view.getWidth()){
dx = view.getWidth() - matrixX - width;
}
//if image will go oustside top bound
if (matrixY + dy < 0){
dy = -matrixY;
}
//if image will go outside bottom bound
if(matrixY + dy + height > view.getHeight()){
dy = view.getHeight() - matrixY - height;
}
matrix.postTranslate(dx, dy);
}
break;
}
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
public void updateValues(ImageView view)
{
matrix = view.getImageMatrix();
matrix.postTranslate(dx, dy);
view.setImageMatrix(matrix);
}
/** Determine the space between the first two fingers */
#SuppressLint("FloatMath")
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
}
It works perfect, however when i try to attach another Bitmap, it loses its current position and bitmap is moved to its original 0,0 state. However after setting bitmap if i call updateValues(ImageView view) it retains its current dragged positon. But when i try to drag again it suddenly goes to 0,0
How can i fix this. I want the new bitmap to start at the same position as before
Also how can i set the image to appear in Center at first place , currently it shows at top left corner

Try this to make it center on load
vTranslate.x = (getWidth()/2) - (scale * (sWidth()/2));
vTranslate.y = (getHeight()/2) - (scale * (sHeight()/2));

Related

ImageView onTouch not moving up and down but only around circle

I am developing a joystick button. Everything is ok when moving it around circle, but when I want to move it up or down, it goes only around circle again from the right side. So, I want to have both options (to move up or down in circle like real joystick). Here is my code example. Thanks for any help
public class JoystickView extends FrameLayout implements View.OnTouchListener {
private Context context;
private ImageView backgroundImageView;
private ImageView buttonImageView;
private RelativeLayout relativeLayout;
private View rootView;
float xx = 0;
float yy = 0;
public JoystickView(Context context) {
super(context);
this.context = context;
setLayout(this);
buttonImageView.setOnTouchListener(this);
this.setClipChildren(false);
}
public void setLayout(ViewGroup view) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
View v = inflateControlerLayout();
view.addView(v, layoutParams);
}
protected View inflateControlerLayout() {
initSlider();
rootView = relativeLayout;
return rootView;
}
public void initSlider() {
if (relativeLayout == null) {
relativeLayout = new RelativeLayout(context);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
relativeLayout.setLayoutParams(layoutParams);
relativeLayout.setClipChildren(false);
}
if (backgroundImageView == null) {
backgroundImageView = new ImageView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
backgroundImageView.setLayoutParams(layoutParams);
// load image
try {
// get input stream
InputStream ims = getContext().getAssets().open("joystick_background.png");
// load image as Drawable
Drawable d = Drawable.createFromStream(ims, null);
// set image to ImageView
backgroundImageView.setImageDrawable(d);
} catch (IOException ex) {
return;
}
}
if (buttonImageView == null) {
buttonImageView = new ImageView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER);
buttonImageView.setLayoutParams(layoutParams);
// load image
try {
// get input stream
InputStream ims2 = getContext().getAssets().open("jostick_button.png");
// load image as Drawable
Drawable d2 = Drawable.createFromStream(ims2, null);
// set image to ImageView
buttonImageView.setImageDrawable(d2);
buttonImageView.bringToFront();
} catch (IOException ex) {
return;
}
}
if (relativeLayout != null) {
relativeLayout.addView(backgroundImageView);
relativeLayout.addView(buttonImageView);
}
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
view.setX(xx);
view.setY(yy);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
float cx = view.getWidth() / 2.f;
float cy = view.getHeight() / 2.f;
float x = motionEvent.getRawX();
float y = motionEvent.getRawY();
float w = buttonImageView.getWidth();
float h = buttonImageView.getHeight();
double r = Math.min(cx, cy) / 2.;
double dx = x - cx;
double dy = y - cy;
double hypot = Math.hypot(dx, dy);
double cos = dx / hypot;
double sin = dy / hypot;
double rdx = hypot < 1. ? 0. : r * cos;
double rdy = hypot < 1. ? 0. : r * sin;
buttonImageView.setTranslationX((float) (cx + rdx - w / 2.));
buttonImageView.setTranslationY((float) (cy + rdy - h / 2.));
break;
}
return true;
}
}
These lines constrain the coordinates to a circle (oval) path or center:
double rdx = hypot < 1. ? 0. : r * cos;
double rdy = hypot < 1. ? 0. : r * sin;
For hypot to be less than 1 (that's 1 pixel) you'd have to be dead center.
I changed it to only constrain the coordinates within the circle:
double rcos = r * cos;
double rsin = r * sin;
double rdx = Math.abs(dx) < Math.abs(rcos) ? dx : rcos;
double rdy = Math.abs(dy) < Math.abs(rsin) ? dy : rsin;
I think this might be what you want.
I have found a working solution if someone is looking for it
private Point calculate (float x, float y) {
float cx = buttonImageView.getWidth() / 2.f;
float cy = buttonImageView.getHeight() / 2.f;
double r = cx / 2.; // vrednost radius
double dx = x;
double dy = y;
double hypot = Math.hypot(dx, dy); // izracun hipotenuze
double cos = dx / hypot; // cos
double sin = dy / hypot; // sin
double rcos = r * cos;
double rsin = r * sin;
double rdx = Math.abs(dx) < Math.abs(rcos) ? dx : rcos; // if,else
double rdy = Math.abs(dy) < Math.abs(rsin) ? dy : rsin;
return new Point((int)rdx, (int)rdy);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final float x = motionEvent.getRawX(); // x točko
final float y = motionEvent.getRawY(); // y točka
//Log.d("VALUES", "RAW X:" + motionEvent.getRawX() + ", RAW Y:" + motionEvent.getRawY() + ", X:" + motionEvent.getX() + ", CX:" + cx + ", CY:" + cy + ", dx:" + dx + ", dy:" + dy + ", Hypo:" + hypot + ", cos:" + cos + ", sin" + sin);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
xDelta = view.getX() - x;
yDelta = view.getY() - y;
break;
case MotionEvent.ACTION_UP:
doBounceAnimation(buttonImageView);
doVibration();
view.setX(xx);
view.setY(yy);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
final float transX = (float) x;
final float transY = (float) y;
thread = new Thread() {
#Override
public void run() {
Point newPoint = calculate(transX+ xDelta,transY + yDelta);
buttonImageView.setX(newPoint.x);
buttonImageView.setY(newPoint.y);
}
};
thread.start();
Log.d(TRANSLATIONX,"X:" + transX + ", Y:" + transY);
break;
}
return true;
}
Enjoy coding!

Zoom Camera2 Preview using TextureView

i have a Problem with my Preview Zoom for the Camera2 API. I am using a TextureView.
I want to zoom only the preview Stream that was showed in the TextureView.
I want to zoom the Area where i use the Zoom Gesture.
I use the SimpleOnScaleGestureListener!
I added following Code. The zoomingFactor and the x and y Position are right.
private void updateTextureViewSize(float xPosi,float yPosi, float scale){
float scaleX = 1.0f;
float scaleY = 1.0f;
float mVideoWidth = mCamcontrol.getmPreviewSize().getWidth();
float mVideoHeight = mCamcontrol.getmPreviewSize().getHeight();
int rotation = getWindowManager().getDefaultDisplay().getRotation();
RectF viewRect = new RectF(0, 0, 1440, 2560);
RectF bufferRect = new RectF(0, 0, mVideoHeight, mVideoWidth);
bufferRect.offset(xPosi - bufferRect.centerX(), yPosi - bufferRect.centerY());
//16:9 faktor
scaleX = ((mScale * scale) / 9f) * 16f;
scaleY = ((mScale * scale) / 16f) * 9f;
Matrix matrix = new Matrix();
matrix.setRectToRect(bufferRect, viewRect, Matrix.ScaleToFit.FILL);
scalefactorView.setText(String.valueOf(xPosi) + " " + String.valueOf(yPosi));
matrix.setScale(scaleY, scaleX, xPosi, yPosi);
matrix.postRotate(90 * (rotation - 2), xPosi, yPosi);
mTextureView.setTransform(matrix);
}
Zooming is Right, but not the Position where i Zoom. For Example! When i zoom on the position right/middle i see only the left/top rectangle of the Stream.
I added the following pictures to unterstand the problem.
Android Camera2 api : Pinch Zoom In/Out
Use this sample code for Camera2Basic from google developers. https://github.com/googlesamples/android-Camera2Basic
Now declare two class variables –
public float finger_spacing = 0;
public int zoom_level = 1;
and update the given onTouch() method.
public boolean onTouch(View v, MotionEvent event) {
try {
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId);
float maxzoom = (characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM))*10;
Rect m = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
int action = event.getAction();
float current_finger_spacing;
if (event.getPointerCount() > 1) {
// Multi touch logic
current_finger_spacing = getFingerSpacing(event);
if(finger_spacing != 0){
if(current_finger_spacing > finger_spacing && maxzoom > zoom_level){
zoom_level++;
} else if (current_finger_spacing < finger_spacing && zoom_level > 1){
zoom_level--;
}
int minW = (int) (m.width() / maxzoom);
int minH = (int) (m.height() / maxzoom);
int difW = m.width() - minW;
int difH = m.height() - minH;
int cropW = difW /100 *(int)zoom_level;
int cropH = difH /100 *(int)zoom_level;
cropW -= cropW & 3;
cropH -= cropH & 3;
Rect zoom = new Rect(cropW, cropH, m.width() - cropW, m.height() - cropH);
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
}
finger_spacing = current_finger_spacing;
} else{
if (action == MotionEvent.ACTION_UP) {
//single touch logic
}
}
try {
mCaptureSession
.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException ex) {
ex.printStackTrace();
}
} catch (CameraAccessException e) {
throw new RuntimeException("can not access camera.", e);
}
return true;
}
//Determine the space between the first two fingers
#SuppressWarnings("deprecation")
private float getFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
Thanks to #arin 's answer, I made an improved version.
His code is basically working, but there are 2 problems:
1) Readability - actually I don't know what is going on calculating the Rect zoom
2) In my Android 7.1.1 device, the preview will freeze if the zoom is big to a certain extent. Since I solved this problem with the code below, I am pretty sure it is because the original code allowed over-zooming beyond camera's maximum zoom ratio.
(In fact, I don't know why he needs to apply *10 on the ratio returned by CameraCharacteristics)
Below are my codes: (I do this all inside my custom TextureView, which also stores my Camera2 objects and logics):
Related Member variables:
protected CameraCharacteristics cameraCharacteristics;
protected CameraCaptureSession captureSession;
protected CaptureRequest.Builder previewRequestBuilder;
//Zooming
protected float fingerSpacing = 0;
protected float zoomLevel = 1f;
protected float maximumZoomLevel;
protected Rect zoom;
Right after you get CameraCharacteristics from CameraManager, probably in some initial setup:
maximumZoomLevel = cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
override onTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent event) {
try {
Rect rect = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
if (rect == null) return false;
float currentFingerSpacing;
if (event.getPointerCount() == 2) { //Multi touch.
currentFingerSpacing = getFingerSpacing(event);
float delta = 0.05f; //Control this value to control the zooming sensibility
if (fingerSpacing != 0) {
if (currentFingerSpacing > fingerSpacing) { //Don't over zoom-in
if ((maximumZoomLevel - zoomLevel) <= delta) {
delta = maximumZoomLevel - zoomLevel;
}
zoomLevel = zoomLevel + delta;
} else if (currentFingerSpacing < fingerSpacing){ //Don't over zoom-out
if ((zoomLevel - delta) < 1f) {
delta = zoomLevel - 1f;
}
zoomLevel = zoomLevel - delta;
}
float ratio = (float) 1 / zoomLevel; //This ratio is the ratio of cropped Rect to Camera's original(Maximum) Rect
//croppedWidth and croppedHeight are the pixels cropped away, not pixels after cropped
int croppedWidth = rect.width() - Math.round((float)rect.width() * ratio);
int croppedHeight = rect.height() - Math.round((float)rect.height() * ratio);
//Finally, zoom represents the zoomed visible area
zoom = new Rect(croppedWidth/2, croppedHeight/2,
rect.width() - croppedWidth/2, rect.height() - croppedHeight/2);
previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
}
fingerSpacing = currentFingerSpacing;
} else { //Single touch point, needs to return true in order to detect one more touch point
return true;
}
captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, null);
return true;
} catch (final Exception e) {
//Error handling up to you
return true;
}
}
And the getFingerSpacing method:
private float getFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
Finally don't forget to set the crop region when you actually take the photo. My code is base on this Camera2Basic, I do this inside the captureStillPicture() method:
//Zoom
if (zoom != null) {
captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
}
#arin Answer is working thank #arin just one thing zoom sensitivity too high.
To control this i make some changes in might be useful to you.
Change zoom_level data type to double
public int zoom_level = 1; to public double zoom_level = 1;
Then increase or decrease zoom_level with low value i use 0.4
if (current_finger_spacing > finger_spacing && maxzoom > zoom_level) {
zoom_level = zoom_level + .4;
//zoom_level++;
} else if (current_finger_spacing < finger_spacing && zoom_level > 1) {
zoom_level = zoom_level - .4;
//zoom_level--;
}
Here is a Pan and Zoom object from Camera2 that I made to work using the OnScaleGestureListener and SimpleOnGestureListener-onScroll outputs. This will only work as expected if you have a camera with support level > LEGACY, as LEGACY only supports crop to center.
Two caveats:
One is that this is currently NOT set up to output to JPEG output, as rectangles for JPEG outputs must have dimensions which are multiple of 16 (See why here). The second is that I've locked my screen to landscape mode, and my camera is locked to landscape as well, but it should be possible to deal with screen rotations after a few tweaks.
You'll need to pass in the screen dimensions
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) mView.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
the Maximum Camera Digital Zoom
try {
CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
float maxZoom = (characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
} catch (CameraAccessException e) {
e.printStackTrace();
}
the Camera Sensor's Active Array Size
try {
CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
Rect rectInit = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Here is my object initialization
mScaler = new CamScaler(maxZoom, rectInit.width(), rectInit.height(), displayMetrics.heightPixels, displayMetrics.widthPixels);
the CamScaler class
public class CamScaler {
private final float ZOOM_MIN = 1.0f;
private final int X_MIN = 0;
private final int Y_MIN = 0;
private int displayWidth;
private int displayHeight;
private Rect current_rect;
private int xCenter;
private int yCenter;
private int xWidth;
private int yHeight;
private int xMax;
private int yMax;
private float zoomMax;
private float zoomCurrent;
public CamScaler(float zoomMax, int xMax, int yMax, int displayHeight, int displayWidth) {
this.xMax = xMax;
this.yMax = yMax;
this.zoomMax = zoomMax;
current_rect = new Rect(X_MIN,Y_MIN, xMax, yMax); //(0,0,xMax,yMax) as the starting rectangle
zoomCurrent = ZOOM_MIN;
xWidth = current_rect.width();
yHeight = current_rect.height();
xCenter = current_rect.centerX();
yCenter = current_rect.centerY();
this.displayHeight = displayHeight;
this.displayWidth = displayWidth;
}
public void pan(float distanceX, float distanceY){
//calculate the shift in the we want to take on the camera sensor with respect to the distance moved on the screen
int xShift = Math.round((distanceX/displayWidth)*xWidth); //scales down to a percentage of the current view width->converts to a pixel shift
int yShift = Math.round((distanceY/displayHeight)*yHeight); //scales down to a percentage of the current view height->converts to a pixel shift
//check if the shift will push us pass our maximums, this should account for both negative and positive values of xShift and yShift correctly
if ( !((xCenter + Math.round(xWidth/2.0) + xShift < xMax) && (xCenter - Math.round(xWidth/2.0) + xShift > 0))) { //if not within xBounds, set xShift to 0
xShift = 0;
}
if ( !((yCenter + Math.round(yHeight/2) + yShift < yMax) && (yCenter - Math.round(yHeight/2.0) + yShift > 0))) { //if not within yBounds, set yShift to 0
yShift = 0;
}
Log.d("Scaler", "pan: xShift" + xShift + " yShift " + yShift);
current_rect.offset(xShift,yShift);
Log.d("Scaler", "pan: current_rect" + current_rect.toString());
xCenter = current_rect.centerX(); //update center
yCenter = current_rect.centerY(); //update center
}
public void zoom(float scale_change){
if ( (zoomCurrent*scale_change < zoomMax) && (zoomCurrent*scale_change > ZOOM_MIN) ){ //if we are within zoom bounds
zoomCurrent *= scale_change; //update the zoom factor
int newWidthHalf = (int)Math.floor(xMax/zoomCurrent/2.0);
int newHeightHalf = (int)Math.floor(yMax/zoomCurrent/2.0);
int xTempCenter = xCenter;
int yTempCenter = yCenter;
//if at edge we need to shift and scale
if (xCenter + newWidthHalf > xMax) { //if at right edge
xTempCenter = xMax - newWidthHalf; //shift center to the left
} else if (xCenter - newWidthHalf < 0) { //if at left edge
xTempCenter = newWidthHalf; //shift center to the right
}
if (yCenter + newHeightHalf > yMax) { //if at bottom
yTempCenter = yMax - newHeightHalf; //shift center up
} else if (yCenter - newHeightHalf < 0) { //if at top
yTempCenter = newHeightHalf; //shift center down
}
Log.d("Scaler", "zoom: " + zoomCurrent);
Log.d(TAG, "current center(x,y) " + xTempCenter + " " + yTempCenter + "current halfwidths(x,y) " + newWidthHalf + " " + newHeightHalf);
current_rect.set(xTempCenter - newWidthHalf, yTempCenter - newHeightHalf,xTempCenter + newWidthHalf, yTempCenter + newHeightHalf);
Log.d("Scaler", "zoom: current_rect" + current_rect.toString());
xWidth = current_rect.width();
yHeight = current_rect.height();
xCenter = current_rect.centerX(); //update center
yCenter = current_rect.centerY(); //update center
} //if not in digital zoom bounds, do nothing
}
public Rect getCurrentView() {
return current_rect;
}
}
And how to use it
public void pan(float distanceX, float distanceY){
if (mScaler != null) {
synchronized (mScaler) {
mScaler.pan(distanceX, distanceY);
try {
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mScaler.getCurrentView());
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
}
public void zoom(float scale_factor) {
if (mScaler!= null) {
synchronized (mScaler) {
mScaler.zoom(scale_factor);
try {
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mScaler.getCurrentView());
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
}
The inputs to these functions are directly passed through from the gesture listeners
I hope this helps someone!
In addition to arin answer,Need to add captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
To captureStillPicture() method to let zoom take effect on capture

Pinch/Pan on Canvas Android

I need to make an application where i'm drawing images on a canvas. When there are on the screen its need to be possible to pinch/pan on the canvas.When i try this code it doesn't correctly zoom on my nexus 5. What is the solution for it?
public class ZoomView extends View {
//These two constants specify the minimum and maximum zoom
private static float MIN_ZOOM = 1f;
private static float MAX_ZOOM = 5f;
private float scaleFactor = 1.f;
private ScaleGestureDetector detector;
//These constants specify the mode that we're in
private static int NONE = 0;
private static int DRAG = 1;
private static int ZOOM = 2;
private int mode;
//These two variables keep track of the X and Y coordinate of the finger when it first
//touches the screen
private float startX = 0f;
private float startY = 0f;
//These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
private float translateX = 0f;
private float translateY = 0f;
//These two variables keep track of the amount we translated the X and Y coordinates, the last time we
//panned.
private float previousTranslateX = 0f;
private float previousTranslateY = 0f;
public ZoomView(Context context) {
super(context);
detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mode = DRAG;
//We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
//amount for each coordinates This works even when we are translating the first time because the initial
//values for these two variables is zero.
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
//We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
//This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
Math.pow(event.getY() - (startY + previousTranslateY), 2)
);
if(distance > 0) {
dragged = true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
dragged = false;
//All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
//previousTranslate
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = DRAG;
//This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
//and previousTranslateY when the second finger goes up
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
}
detector.onTouchEvent(event);
//We redraw the canvas only in the following cases:
//
// o The mode is ZOOM
// OR
// o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
// set to true (meaning the finger has actually moved)
if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {
invalidate();
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//We're going to scale the X and Y coordinates by the same amount
canvas.scale(scaleFactor, scaleFactor);
//If translateX times -1 is lesser than zero, let's set it to zero. This takes care of the left bound
if((translateX * -1) < 0) {
translateX = 0;
}
//This is where we take care of the right bound. We compare translateX times -1 to (scaleFactor - 1) * displayWidth.
//If translateX is greater than that value, then we know that we've gone over the bound. So we set the value of
//translateX to (1 - scaleFactor) times the display width. Notice that the terms are interchanged; it's the same
//as doing -1 * (scaleFactor - 1) * displayWidth
else if((translateX * -1) > (scaleFactor - 1) * displayWidth) {
translateX = (1 - scaleFactor) * displayWidth;
}
if(translateY * -1 < 0) {
translateY = 0;
}
//We do the exact same thing for the bottom bound, except in this case we use the height of the display
else if((translateY * -1) > (scaleFactor - 1) * displayHeight) {
translateY = (1 - scaleFactor) * displayHeight;
}
//We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
//because the translation amount also gets scaled according to how much we've zoomed into the canvas.
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
/* The rest of your canvas-drawing code */
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
Thx in advance

ImageView onTouchListener: cant touch the imageview behind

I got a tutorial of imageview transformation, w/c I really need. here is the link. It works fine but I need to have some modification. My problem here is, when I add the second imageview, I cant modify the first imageview that I added. It seems that I can't touch it because it is behind the 2nd view. I've read a comment in the link, that is asking for this problem too and he resolve it by doing settag and gettag. I've no idea how to do it. Any suggestion guys? thanks.
public class MainActivity extends Activity implements OnTouchListener {
// these matrices will be used to move and zoom image
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
// we can be in one of these 3 states
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
// remember some things for zooming
private PointF start = new PointF();
private PointF mid = new PointF();
private float oldDist = 1f;
private float d = 0f;
private float newRot = 0f;
private float[] lastEvent = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView view = (ImageView) findViewById(R.id.imageView);
ImageView view2 = (ImageView) findViewById(R.id.imageView2);
view.setOnTouchListener(this);
view2.setOnTouchListener(this);
}
public boolean onTouch(View v, MotionEvent event) {
// handle touch events here
ImageView view = (ImageView) v;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
lastEvent = null;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
lastEvent = new float[4];
lastEvent[0] = event.getX(0);
lastEvent[1] = event.getX(1);
lastEvent[2] = event.getY(0);
lastEvent[3] = event.getY(1);
d = rotation(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
} else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = (newDist / oldDist);
matrix.postScale(scale, scale, mid.x, mid.y);
}
if (lastEvent != null && event.getPointerCount() == 2) {
newRot = rotation(event);
float r = newRot - d;
float[] values = new float[9];
matrix.getValues(values);
float tx = values[2];
float ty = values[5];
float sx = values[0];
float xc = (view.getWidth() / 2) * sx;
float yc = (view.getHeight() / 2) * sx;
matrix.postRotate(r, tx + xc, ty + yc);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
/**
* Determine the space between the first two fingers
*/
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/**
* Calculate the mid point of the first two fingers
*/
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
/**
* Calculate the degree to be rotated by.
*
* #param event
* #return Degrees
*/
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="#+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:src="#drawable/chill"
/>
<ImageView
android:id="#+id/imageView2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:src="#drawable/awesome"
/>
</FrameLayout>
Try this one:
In your xml file set tag for your image with android:tag and at run time get the tag and apply transformation to that particular image.
1.Implement a method in java class,
2.Call that method when either of image is clicked,
3.And identify image by getting its tag using View.getTag(); and apply transformation.

How to get all x and y coordinates of an imageview in Android?

I am implementing an app for getting the correct x and y location of the image-view and x and y coordinates of an image-view when I move the position of images on the screen. When I run the app and when I move my image-view it will get only 0 and 1. Is there a way for find out the x and y location?
Here is my Main-activity class:
public class MainActivity extends Activity{
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
int[] location = new int[4];
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView image = (ImageView) findViewById(R.id.ImageViewOne);
final Bitmap icon = BitmapFactory.decodeResource(this.getResources(),
R.drawable.yellowrose);
image.setImageBitmap(icon);
image.setOnTouchListener(new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event)
{
final ImageView view = (ImageView) v;
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
System.out.println("mode = drag");
RectF r = new RectF();
matrix.mapRect(r);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
System.out.println("mode = none = " + mode);
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG)
{
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
}
else if (mode == ZOOM)
{
float newDist = spacing(event);
if (newDist > 10f)
{
matrix.set(savedMatrix);
float scale = newDist / oldDist;
System.out.println("scale " + scale);
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f)
{
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
System.out.println("mode " + mode);
}
break;
}
// Perform the transformation
view.setImageMatrix(matrix);
return true;
}
});
}
private float spacing(MotionEvent event)
{
float x = event.getRawX() - event.getRawX();
System.out.println("spacing x" + x);
float y = event.getRawY() - event.getRawY();
System.out.println("spacing y" + y);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event)
{
float x = event.getRawX() + event.getRawX();
System.out.println("midpoint x" + x);
float y = event.getRawY() + event.getRawY();
System.out.println("midpoint y" + y);
point.set(x / 2, y / 2);
}
}
I am using View.getLocationOnScreen(int[]) to get X and Y of a view relative to its parent/screen:
int[] posXY = new int[2];
myView.getLocationOnScreen(posXY);
int x = posXY[0];
int y = posXY[1];

Categories