I current'y have an image that can be moved either Left or Right, depending on whether the User touches the Left Or Right part of their device's screen. However, I don't want the User to move the image off of the screen! So I was wondering, can I put a limit or restriction on how far the User can move the image Left or Right?
Here is the code for the image that moves (when the Left or Right part of the device's screen is touched)
//OnTouch Function
#Override
public boolean onTouch(View v, MotionEvent event) {
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int x = (int)event.getX();
if ( x >= ( screenWidth/2) ) {
int ScreenWidth = getResources().getDisplayMetrics().widthPixels;
float Xtouch = event.getRawX();
int sign = Xtouch > 0.5 * ScreenWidth ? 1 : -1;
float XToMove = 85;
int durationMs = 50;
v.animate().translationXBy(sign*XToMove).setDuration(durationMs);
}else {
if( x < ( screenWidth/2) ) {
int ScreenWidth = getResources().getDisplayMetrics().widthPixels;
float xtouch = event.getRawX();
int sign = xtouch < 0.5 / ScreenWidth ? 1 : -1;
float xToMove = 60; // or whatever amount you want
int durationMs = 50;
v.animate().translationXBy(sign*xToMove).setDuration(durationMs);
}
}
return false;
}
});
Just keep track of xPosition of the object (add/subtract from a class variable every time it moves) add a check in there before moving the object. As in
if( xPosition < ScreenWidth-buffer ) {
//run code to move object right
}
and the opposite (xPosition > buffer) in the code that moves the image left, where buffer is some margin you want on the edge of the screen. For example:
private float xPosition; // set to initial position in onCreate
//OnTouch Function
#Override
public boolean onTouch(View v, MotionEvent event) {
int screenWidth = getResources().getDisplayMetrics().widthPixels;
float x = event.getRawX();
int durationMs = 50;
int buffer = 90;
if ( x >= ( screenWidth/2) && xPosition < screenWidth-buffer ) {
float XToMove = 85;
v.animate().translationXBy(XToMove).setDuration(durationMs);
xPosition += XToMove;
}else if( x < ( screenWidth/2) && xPosition > buffer ) {
float XToMove = -60; // or whatever amount you want
v.animate().translationXBy(XToMove).setDuration(durationMs);
xPosition += XToMove;
}
return false;
}
});
Related
this is my very first experience here on stackoverflow :). I hope i don't break any rule with my first question.
I want to get color from bitmap but i have a problem with converting Imageview to Bitmap. Wherever i put my ImageView on screen, it's moving to (0,0) screen position after converting it to Bitmap. The worst thing is that this is not visible on screen, but when i try to catch color from 'empty' position it gives me "color". Im attaching screens because im sure im not understandable now :D.
clicked on area where is invisible bitmap and catching color)
clicked on area where bitmap is located but it doesn't catch color
I really don't know what is wrong. sitting whole week on this case and i registered here to ask you...
It may be stupid thing because im newbie. Thanks for any help...
public class Game extends AppCompatActivity {
private boolean isTouch = false;
private ImageView imageOpponent;
private Bitmap bitmap1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
ImageView image = findViewById(R.id.imageView3);
ImageView imageAim = findViewById(R.id.imageAim);
ImageView imageMove = findViewById(R.id.imageView4);
ImageView imageShoot = findViewById(R.id.imageShoot);
ImageView imageBackground = findViewById(R.id.imageBackground);
//FrameLayout shootingFrame = findViewById(R.id.shootingFrame);
//Bitmap bitmap = imageBackground.getDrawingCache();
Bitmap bitmap = ((BitmapDrawable)imageBackground.getDrawable()).getBitmap();
imageOpponent = findViewById(R.id.imageOpponent);
imageOpponent.setDrawingCacheEnabled(true);
imageOpponent.buildDrawingCache(true);
TextView textX = findViewById(R.id.textView1);
TextView textY = findViewById(R.id.textView2);
//textX.setText(String.valueOf(xSize));
image.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float xSize = image.getWidth();
float ySize = image.getHeight();
float xMaxShootingFrame = imageBackground.getWidth();
float yMaxShootingFrame = imageBackground.getHeight();
float xMoveSize = imageMove.getLayoutParams().width;
float yMoveSize = imageMove.getLayoutParams().height;
textY.setText(String.valueOf(xMaxShootingFrame));
float X = (float) event.getX() - (xMoveSize/2);
float Y = (float) event.getY() - (yMoveSize/2);
float xCalculated = 100 * event.getX() / xSize;
xCalculated = xCalculated/100;
float yCalculated = 100 * event.getY() / ySize;
yCalculated = yCalculated/100;
int eventaction = event.getAction();
//if ((X >= 0 && Y >= 0) || (X <= 100 && Y <= 100))
if ((X >= (0-(xMoveSize/2)) && X <= (xSize-(xMoveSize/2)) && (Y >= (0-(yMoveSize/2)) && Y <= (ySize-(yMoveSize/2))))) {
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
//Toast.makeText(Game.this, "ACTION_DOWN AT COORDS "+"X: "+X+" Y: "+Y, Toast.LENGTH_SHORT).show();
imageMove.setX(X);
imageMove.setY(Y);
imageAim.setX(xMaxShootingFrame*xCalculated-xMoveSize/2);
imageAim.setY(yMaxShootingFrame*yCalculated-yMoveSize/2);
textX.setText(String.valueOf(xCalculated));
isTouch = true;
break;
case MotionEvent.ACTION_MOVE:
//Toast.makeText(Game.this, "MOVE "+"X: "+X+" Y: "+Y, Toast.LENGTH_SHORT).show();
imageMove.setX(X);
imageMove.setY(Y);
imageAim.setX(xMaxShootingFrame*xCalculated-xMoveSize/2);
imageAim.setY(yMaxShootingFrame*yCalculated-yMoveSize/2);
//textX.setText(String.valueOf(bitmap.getHeight()));
break;
case MotionEvent.ACTION_UP:
//Toast.makeText(Game.this, "ACTION_UP "+"X: "+X+" Y: "+Y, Toast.LENGTH_SHORT).show();
imageMove.setX(X);
imageMove.setY(Y);
imageAim.setX(xMaxShootingFrame*xCalculated-xMoveSize/2);
imageAim.setY(yMaxShootingFrame*yCalculated-yMoveSize/2);
//textX.setText(String.valueOf(bitmap.getHeight()));
break;
}
}
float xxx = imageAim.getX();
float yyy = imageAim.getY();
return true;
}
});
imageShoot.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event){
bitmap1 = imageOpponent.getDrawingCache();
Bitmap bitmapBackground = ((BitmapDrawable)imageBackground.getDrawable()).getBitmap();
float xMoveSizex = imageAim.getLayoutParams().width;
float yMoveSizey = imageAim.getLayoutParams().height;
int x = (int)imageAim.getX() + ((int)xMoveSizex/2);
int y = (int)imageAim.getY() + ((int)yMoveSizey/2);
//Bitmap bitmap69 = Bitmap.createScaledBitmap(bitmap1, imageOpponent.getWidth(), imageOpponent.getHeight(), false);
textX.setText(String.valueOf(bitmap1.getHeight()));
textY.setText(String.valueOf(bitmap1.getWidth()));
if (x <= bitmap1.getWidth() && y <= bitmap1.getHeight()) {
int pixel = bitmap1.getPixel(x, y);
int redValue = Color.red(pixel);
int blueValue = Color.blue(pixel);
int greenValue = Color.green(pixel);
//if (redValue == 0 && blueValue == 0 && greenValue == 0){
//imageBackground.setBackgroundColor(Color.rgb(redValue,greenValue,blueValue));
if (bitmap1.getPixel(x, y) == Color.TRANSPARENT) {
textY.setText("Black " + x + " " + y);
} else {
textY.setText("Any Color");
}
}
return true;
}
});
}
}
Add a method to your code that checks each of the four edges of the ball one by one, checking if that edge has collided with the edge of the window. If so, it should change direction accordingly. For instance, if the ball was moving north-east (↗) and the top edge had hit the top of the screen, it would change the direction to south-east (↘) so that the ball would appear to bounce off the edge. (Remember that if the ball goes directly into a corner in a diagonal direction, two edges could hit at once, making it bounce straight back in the opposite diagonal direction.)
I am not sure how to get the four edges of an oval. I have the other 4 directions done (up, down, left, right) done but am not sure how to handle corners of the window.
public void move()
{
//direction right
if(dir == 1)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x + 1,y);
if(x>425)
{
dir = 2;
}
}
//direction left
if(dir == 2)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x - 1,y);
if(x<0)
{
dir = 1;
}
}
//direction down
if(dir == 3)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x,y + 1);
if(y>425)
{
dir = 4;
}
}
//direction up
if(dir == 4)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x ,y - 1);
if(y<0)
{
dir = 3;
}
}
//direction bottom right corner
if(dir == 5)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x + 1,y + 1);
}
//direction upper right corner
if(dir == 6)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x + 1,y - 1);
}
//direction bottom left corner
if(dir == 7)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x - 1,y + 1);
}
//direction upper left corner
if(dir == 8)
{
int x = ball.getX();
int y = ball.getY();
ball.setLocation(x - 1,y - 1);
}
}
Finally, make sure to call this method whenever you move the ball. That way, after you move the ball 1 pixel in its current direction, you run the method to check if it has hit any edge, and change direction. At this point, your ball should move about the screen ricocheting as it goes.
Don't think too much about it being an oval. For the purposes of this assignment, it might just as well be a box. The only difference between the case for corners compared to the case for an edge is that you need to distinguish between 4 outcomes:
You hit a horizontal and vertical wall: Change to the opposite direction of the diagonal movement.
You hit a horizontal wall: Keep the vertical direction and switch the horizontal to the opposite.
You hit a vertical wall: Keep the horizontal direction and switch the vertical to the opposite.
You hit no wall at all: Do nothing.
Furthermore, the instructions specifically recommend that you separate movement logic from the logic for changing direction. In your initial approach, you are doing both at once.
In my example, I introduce a method changeDirection() that only checks if any boundary is hit and then turns you around, but no actual movement is taking place. You still need to implement move() and take care of that, but you can just call this method and assume that the ball is heading towards the correct direction afterwards.
Also, the code becomes easier to understand once you introduce named constants to refer to the directions:
private static final int RIGHT = 1;
private static final int LEFT = 2;
private static final int DOWN = 3;
private static final int UP = 4;
private static final int DOWN_RIGHT = 5;
private static final int UP_RIGHT = 6;
private static final int DOWN_LEFT = 7;
private static final int UP_LEFT = 8;
private static final int WIDTH = 425;
private static final int HEIGHT = 425;
public void changeDirection()
{
int x = ball.getX();
int y = ball.getY();
switch ( dir )
{
case RIGHT:
if ( x >= WIDTH )
{
dir = LEFT;
}
break;
case LEFT:
if ( x <= 0 )
{
dir = RIGHT;
}
break;
case DOWN:
if ( y >= HEIGHT )
{
dir = UP;
}
break;
case UP:
if ( y <= 0 )
{
dir = DOWN;
}
break;
case DOWN_RIGHT:
if ( y >= HEIGHT )
{
if ( x >= WIDTH )
{
dir = UP_LEFT;
}
else
{
dir = UP_RIGHT;
}
}
else if ( x >= WIDTH )
{
dir = DOWN_LEFT;
}
break;
case UP_RIGHT:
if ( y <= 0 )
{
if ( x >= WIDTH )
{
dir = DOWN_LEFT;
}
else
{
dir = DOWN_RIGHT;
}
}
else if ( x >= WIDTH )
{
dir = UP_LEFT;
}
break;
case DOWN_LEFT:
if ( y >= HEIGHT )
{
if ( x <= 0 )
{
dir = UP_RIGHT;
}
else
{
dir = UP_LEFT;
}
}
else if ( x <= 0 )
{
dir = DOWN_RIGHT;
}
break;
case UP_LEFT:
if ( y <= 0 )
{
if ( x <= 0 )
{
dir = DOWN_RIGHT;
}
else
{
dir = UP_LEFT;
}
}
else if ( x <= 0 )
{
dir = UP_RIGHT;
}
break;
}
}
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
I'm working in a spaceship first person view game. I have a joystick, and when i move the joystick i can move all the objects (asteroids) of the screen simulating that the spaceship is being moved with the joystick.
The game works fine, but now i have a problem. If you are pressing the joystick in the max left position and then you do ACTION_UP and then instantly ACTION_DOWN in the joystick again but in the max right position, the spaceship starts moving to the right at max speed. It is hard to explain it. For example, If you press the joystick in max left position the spaceship is moving -20px per frame to the left and if you press the joystick in the max right position, the spaceship moves to the right +20px per frame.
So, now, if i do a fast max left and max right touch on the joystick, the spaceship does this movement: -20....+20
It is not reallistic movement.
I want to get this movement: -20 -17 -14 -9 -5 0 +5 +9 +14 +17 +20.... I mean a more reallistic spaceship movement. But the problem is that i am not a math or physics expert, and i dont have any idea of how to get that kind of functionality in this joystick... any help will be very grateful.
Here you can find a demo project with the joystick: https://mega.co.nz/#!cp5FhYIT!dM88qx_xQdyhED9fX_4xeJ9ciQYJirUlNzEi-KOzU2k
This is the joystick code, i found it in google and works very well except for the non realistic movement that i described before:
public class Joystick extends View {
public static final int INVALID_POINTER = -1;
private JoystickMovedListener moveListener;
//# of pixels movement required between reporting to the listener
private float moveResolution;
//Max range of movement in user coordinate system
private float movementRange;
//Last touch point in view coordinates
private int pointerId = INVALID_POINTER;
private float touchX;
private float touchY;
private float touchXDelayedMovement;
private float touchYDelayedMovement;
//Handle center in view coordinates
private float handleX;
private float handleY;
//Last reported position in view coordinates (allows different reporting sensitivities)
private float reportX;
private float reportY;
//Center of the view in view coordinates
private int cX;
private int cY;
//Size of the view in view coordinates
private int dimX;
private int dimY;
private int innerPadding;
private int bgRadius;
private int handleRadius;
private int movementRadius;
private int handleInnerBoundaries;
//Cartesian coordinates of last touch point - joystick center is (0,0)
private int cartX;
private int cartY;
//User coordinates of last touch point
private int userX;
private int userY;
//Offset co-ordinates (used when touch events are received from parent's coordinate origin)
private int offsetX;
private int offsetY;
private Paint bgPaint;
private Paint handlePaint;
boolean disabled;
Handler handler;
Handler handlerDelayedMovement;
public Joystick(Context context) {
super(context);
initJoystickView();
}
private void initJoystickView() {
setFocusable(true);
handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
handlePaint.setColor(Color.RED);
handlePaint.setStrokeWidth(1);
handlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(Color.DKGRAY);
bgPaint.setStrokeWidth(1);
bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
this.moveResolution = 1.0f;
handler = new Handler();
handlerDelayedMovement = new Handler();
}
public void setMovementRange(float movementRange) {
this.movementRange = movementRange;
}
public void setOnJostickMovedListener(JoystickMovedListener listener) {
this.moveListener = listener;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int d = Math.min(getMeasuredWidth(), getMeasuredHeight());
dimX = d;
dimY = d;
cX = d / 2;
cY = d / 2;
bgRadius = dimX/2 - innerPadding;
handleRadius = (int)(d * 0.2);
handleInnerBoundaries = handleRadius;
movementRadius = Math.min(cX, cY) - handleInnerBoundaries;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Here we make sure that we have a perfect circle
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
}
private int measure(int measureSpec) {
int result = 0;
// Decode the measurement specifications.
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.UNSPECIFIED) {
result = 200; // Return a default size of 200 if no bounds are specified.
} else {
result = specSize; // As you want to fill the available space always return the full available bounds.
}
return result;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
// Draw the background
canvas.drawCircle(cX, cY, bgRadius, bgPaint);
// Draw the handle
handleX = touchX + cX;
handleY = touchY + cY;
canvas.drawCircle(handleX, handleY, handleRadius, handlePaint);
canvas.restore();
}
public void setPointerId(int id) {
this.pointerId = id;
}
public int getPointerId() {
return pointerId;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (disabled==true)
break;
return processMoveEvent(ev);
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if ( pointerId != INVALID_POINTER ) {
returnHandleToCenter();
returnHandleToCenterDelayedMovement();
setPointerId(INVALID_POINTER);
}
break;
}
case MotionEvent.ACTION_POINTER_UP: {
if ( pointerId != INVALID_POINTER ) {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if ( pointerId == this.pointerId ) {
returnHandleToCenter();
returnHandleToCenterDelayedMovement();
setPointerId(INVALID_POINTER);
return true;
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
handlerDelayedMovement.removeCallbacksAndMessages(null);
if ( pointerId == INVALID_POINTER ) {
int x = (int) ev.getX();
if ( x >= offsetX && x < offsetX + dimX ) {
setPointerId(ev.getPointerId(0));
if (disabled==true){
return true;
}
return processMoveEvent(ev);
}
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
if ( pointerId == INVALID_POINTER ) {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
int x = (int) ev.getX(pointerId);
if ( x >= offsetX && x < offsetX + dimX ) {
setPointerId(pointerId);
return true;
}
}
break;
}
}
return false;
}
private boolean processMoveEvent(MotionEvent ev) {
if ( pointerId != INVALID_POINTER ) {
final int pointerIndex = ev.findPointerIndex(pointerId);
// Translate touch position to center of view
float x = ev.getX(pointerIndex);
touchX = x - cX - offsetX;
float y = ev.getY(pointerIndex);
touchY = y - cY - offsetY;
reportOnMoved();
invalidate();
return true;
}
return false;
}
private void reportOnMoved() {
//constraint circle
float diffX = touchX;
float diffY = touchY;
double radial = Math.sqrt((diffX*diffX) + (diffY*diffY));
if ( radial > movementRadius ) {
touchX = (int)((diffX / radial) * movementRadius);
touchY = (int)((diffY / radial) * movementRadius);
}
//We calc user coordinates
//First convert to cartesian coordinates
cartX = (int)(touchX / movementRadius * movementRange);
cartY = (int)(touchY / movementRadius * movementRange);
//Cartesian Coordinates
userX = cartX;
userY = cartY;
if (moveListener != null) {
boolean rx = Math.abs(touchX - reportX) >= moveResolution;
boolean ry = Math.abs(touchY - reportY) >= moveResolution;
if (rx || ry) {
this.reportX = touchX;
this.reportY = touchY;
moveListener.OnMoved(userX, userY);
}
}
}
private void reportOnMovedDelayedMovement() {
//constraint circle
float diffX = touchXDelayedMovement;
float diffY = touchYDelayedMovement;
double radial = Math.sqrt((diffX*diffX) + (diffY*diffY));
if ( radial > movementRadius ) {
touchXDelayedMovement = (int)((diffX / radial) * movementRadius);
touchYDelayedMovement = (int)((diffY / radial) * movementRadius);
}
//We calc user coordinates
//First convert to cartesian coordinates
cartX = (int)(touchXDelayedMovement / movementRadius * movementRange);
cartY = (int)(touchYDelayedMovement / movementRadius * movementRange);
//Cartesian Coordinates
userX = cartX;
userY = cartY;
if (moveListener != null) {
boolean rx = Math.abs(touchXDelayedMovement - reportX) >= moveResolution;
boolean ry = Math.abs(touchYDelayedMovement - reportY) >= moveResolution;
if (rx || ry) {
this.reportX = touchXDelayedMovement;
this.reportY = touchYDelayedMovement;
moveListener.OnMoved(userX, userY);
}
}
}
private void returnHandleToCenter() {
final int numberOfFrames = 5;
final double intervalsX = (0 - touchX) / numberOfFrames;
final double intervalsY = (0 - touchY) / numberOfFrames;
handler.removeCallbacksAndMessages(null);
for (int i = 0; i < numberOfFrames; i++) {
final int j = i;
handler.postDelayed(new Runnable() {
#Override
public void run() {
touchX += intervalsX;
touchY += intervalsY;
//reportOnMoved();
invalidate();
if (moveListener != null && j == numberOfFrames - 1) {
moveListener.OnReturnedToCenter();
}
}
}, i * 10);
}
if (moveListener != null) {
moveListener.OnReleased();
}
}
private void returnHandleToCenterDelayedMovement() {
final int numberOfFrames = 25;
touchXDelayedMovement=touchX;
touchYDelayedMovement=touchY;
final double intervalsX = (0 - touchXDelayedMovement) / numberOfFrames;
final double intervalsY = (0 - touchYDelayedMovement) / numberOfFrames;
handlerDelayedMovement.removeCallbacksAndMessages(null);
for (int i = 0; i < numberOfFrames; i++) {
handlerDelayedMovement.postDelayed(new Runnable() {
#Override
public void run() {
touchXDelayedMovement += intervalsX;
touchYDelayedMovement += intervalsY;
reportOnMovedDelayedMovement();
}
}, i * 50);
}
}
public void setInnerPadding(int innerPadding){
this.innerPadding=innerPadding;
}
public void disable(){
disabled=true;
}
public void enable(){
disabled=false;
}
public interface JoystickMovedListener {
public void OnMoved(int pan, int tilt);
public void OnReleased();
public void OnReturnedToCenter();
}
}
You must do this in the class that will use the joystick:
private JoystickMovedListener joystickListener = new JoystickMovedListener() {
#Override
public void OnMoved(int pan, int tilt) {
//here i move the objects in the game
}
}
#Override
public void OnReleased() {}
public void OnReturnedToCenter() {};
};
joystickOnScreen = new Joystick(this);
joystickOnScreen.setMovementRange(screenHeight/50);
joystickOnScreen.setInnerPadding(screenHeight/30);
joystickOnScreen.setOnJostickMovedListener(joystickListener);
RelativeLayout.LayoutParams joystickParams = new RelativeLayout.LayoutParams(sh/3, sh/3);
joystickParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
joystickParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
joystickParams.setMargins(sh/100, 0, 0, sh/100);
joystickOnScreen.setLayoutParams(joystickParams);
joystickOnScreen.setAlpha(0.3f);
I will not implement the changes for you but hopefully this answer can help you towards implementing this on your own.
With your current implementation you are updating the object position (x, y) each frame. To get the more realistic physics that you want, you need to store and update velocity as well (vx, vy).
Add two new variables, vx and vy (with initial values of zero) in the objects that you are currently updating the position for. The joystick should control the change of the velocity instead of the position. Change the code that updates the positions x and y, to update the velocities vx and vy instead. When the joystick is max left, you can for example set vx = vx - 3.
After the velocity is updated, you need to update the position using the velocity variables. For example, set the position x = x + vx. Ideally you want this to happen in a different method that runs even if you don't move the joystick, but to keep it simple you can do this update right after the update of the velocity variables.
With this implementation you will get a more realistic game physics. As a next step you might want to add limits on the velocity to not move too fast. This can be done with an if-statement where you check that the value is not too big before adding more to it, or too smal before subtracting from it. Good luck!
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