I have a Framelayout which add four imageview at runtime as well in center it contains main image with which user can perform different action but i face the problem with rotate layout view
currently on touch of rotate button i'm doing this
public void setRotateListener() {
mRotateImage.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(0);
float y = event.getY(0);
float theta = getTheta(x, y);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
theta_old = theta;
break;
case MotionEvent.ACTION_MOVE:
float delta_theta = theta - theta_old;
theta_old = theta;
int direction = (delta_theta > 0) ? 1 : -1;
angle += 3 * direction;
Log.d("Tag", "rotate angle : " + obj.getHeight());
obj.setRotation(angle);
notifyListener(direction);
break;
}
return true;
}
});
}
private float getTheta(float x, float y) {
float sx = x - (obj.getWidth() / 2.0f);
float sy = y - (obj.getHeight() / 2.0f);
float length = (float) Math.sqrt(sx * sx + sy * sy);
float nx = sx / length;
float ny = sy / length;
float theta = (float) Math.atan2(ny, nx);
final float rad2deg = (float) (180.0 / Math.PI);
float thetaDeg = theta * rad2deg;
return (thetaDeg < 0) ? thetaDeg + 360.0f : thetaDeg;
}
but i can't get the expected result i already refer this link as well
https://github.com/rprouse/XkcdClock
as well as try to rotate with gesture and animation too but it seems not working as per my move on the screen
I have Design A Layout that may work as your need.
Download Demo here
Java File
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class ClipArt extends RelativeLayout {
int baseh;
int basew;
int basex;
int basey;
ImageButton btndel;
ImageButton btnrot;
ImageButton btnscl;
RelativeLayout clip;
Context cntx;
boolean freeze = false;
int h;
int i;
ImageView image;
String imageUri;
boolean isShadow;
int iv;
RelativeLayout layBg;
RelativeLayout layGroup;
RelativeLayout.LayoutParams layoutParams;
public LayoutInflater mInflater;
int margl;
int margt;
float opacity = 1.0F;
Bitmap originalBitmap;
int pivx;
int pivy;
int pos;
Bitmap shadowBitmap;
float startDegree;
String[] v;
public ClipArt(Context paramContext) {
super(paramContext);
cntx = paramContext;
layGroup = this;
basex = 0;
basey = 0;
pivx = 0;
pivy = 0;
mInflater = ((LayoutInflater) paramContext.getSystemService("layout_inflater"));
mInflater.inflate(R.layout.clipart, this, true);
btndel = ((ImageButton) findViewById(R.id.del));
btnrot = ((ImageButton) findViewById(R.id.rotate));
btnscl = ((ImageButton) findViewById(R.id.sacle));
layoutParams = new RelativeLayout.LayoutParams(250, 250);
layGroup.setLayoutParams(layoutParams);
image = ((ImageView) findViewById(R.id.clipart));
image.setImageResource(R.drawable.ic_launcher);
setOnTouchListener(new View.OnTouchListener() {
final GestureDetector gestureDetector = new GestureDetector(ClipArt.this.cntx,
new GestureDetector.SimpleOnGestureListener() {
public boolean onDoubleTap(MotionEvent paramAnonymous2MotionEvent) {
return false;
}
});
public boolean onTouch(View paramAnonymousView, MotionEvent event) {
if (!ClipArt.this.freeze) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
layGroup.invalidate();
gestureDetector.onTouchEvent(event);
layGroup.performClick();
basex = ((int) (event.getRawX() - layoutParams.leftMargin));
basey = ((int) (event.getRawY() - layoutParams.topMargin));
break;
case MotionEvent.ACTION_MOVE:
int i = (int) event.getRawX();
int j = (int) event.getRawY();
layBg = ((RelativeLayout) getParent());
if ((i - basex > -(layGroup.getWidth() * 2 / 3))
&& (i - basex < layBg.getWidth() - layGroup.getWidth() / 3)) {
layoutParams.leftMargin = (i - basex);
}
if ((j - basey > -(layGroup.getHeight() * 2 / 3))
&& (j - basey < layBg.getHeight() - layGroup.getHeight() / 3)) {
layoutParams.topMargin = (j - basey);
}
layoutParams.rightMargin = -1000;
layoutParams.bottomMargin = -1000;
layGroup.setLayoutParams(layoutParams);
break;
}
return true;
}
return true;
}
});
this.btnscl.setOnTouchListener(new View.OnTouchListener() {
#SuppressLint({ "NewApi" })
public boolean onTouch(View paramAnonymousView, MotionEvent event) {
if (!ClipArt.this.freeze) {
int j = (int) event.getRawX();
int i = (int) event.getRawY();
layoutParams = (RelativeLayout.LayoutParams) layGroup.getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ClipArt.this.layGroup.invalidate();
ClipArt.this.basex = j;
ClipArt.this.basey = i;
ClipArt.this.basew = ClipArt.this.layGroup.getWidth();
ClipArt.this.baseh = ClipArt.this.layGroup.getHeight();
int[] loaction = new int[2];
layGroup.getLocationOnScreen(loaction);
margl = layoutParams.leftMargin;
margt = layoutParams.topMargin;
break;
case MotionEvent.ACTION_MOVE:
float f2 = (float) Math.toDegrees(Math.atan2(i - ClipArt.this.basey, j - ClipArt.this.basex));
float f1 = f2;
if (f2 < 0.0F) {
f1 = f2 + 360.0F;
}
j -= ClipArt.this.basex;
int k = i - ClipArt.this.basey;
i = (int) (Math.sqrt(j * j + k * k)
* Math.cos(Math.toRadians(f1 - ClipArt.this.layGroup.getRotation())));
j = (int) (Math.sqrt(i * i + k * k)
* Math.sin(Math.toRadians(f1 - ClipArt.this.layGroup.getRotation())));
k = i * 2 + ClipArt.this.basew;
int m = j * 2 + ClipArt.this.baseh;
if (k > 150) {
layoutParams.width = k;
layoutParams.leftMargin = (ClipArt.this.margl - i);
}
if (m > 150) {
layoutParams.height = m;
layoutParams.topMargin = (ClipArt.this.margt - j);
}
ClipArt.this.layGroup.setLayoutParams(layoutParams);
ClipArt.this.layGroup.performLongClick();
break;
}
return true;
}
return ClipArt.this.freeze;
}
});
this.btnrot.setOnTouchListener(new View.OnTouchListener() {
#SuppressLint({ "NewApi" })
public boolean onTouch(View paramAnonymousView, MotionEvent event) {
if (!ClipArt.this.freeze) {
layoutParams = (RelativeLayout.LayoutParams) ClipArt.this.layGroup.getLayoutParams();
ClipArt.this.layBg = ((RelativeLayout) ClipArt.this.getParent());
int[] arrayOfInt = new int[2];
layBg.getLocationOnScreen(arrayOfInt);
int i = (int) event.getRawX() - arrayOfInt[0];
int j = (int) event.getRawY() - arrayOfInt[1];
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ClipArt.this.layGroup.invalidate();
ClipArt.this.startDegree = layGroup.getRotation();
ClipArt.this.pivx = (layoutParams.leftMargin + ClipArt.this.getWidth() / 2);
ClipArt.this.pivy = (layoutParams.topMargin + ClipArt.this.getHeight() / 2);
ClipArt.this.basex = (i - ClipArt.this.pivx);
ClipArt.this.basey = (ClipArt.this.pivy - j);
break;
case MotionEvent.ACTION_MOVE:
int k = ClipArt.this.pivx;
int m = ClipArt.this.pivy;
j = (int) (Math.toDegrees(Math.atan2(ClipArt.this.basey, ClipArt.this.basex))
- Math.toDegrees(Math.atan2(m - j, i - k)));
i = j;
if (j < 0) {
i = j + 360;
}
ClipArt.this.layGroup.setRotation((ClipArt.this.startDegree + i) % 360.0F);
break;
}
return true;
}
return ClipArt.this.freeze;
}
});
this.btndel.setOnClickListener(new View.OnClickListener() {
public void onClick(View paramAnonymousView) {
if (!ClipArt.this.freeze) {
layBg = ((RelativeLayout) ClipArt.this.getParent());
layBg.performClick();
layBg.removeView(ClipArt.this.layGroup);
}
}
});
}
public void disableAll() {
this.btndel.setVisibility(4);
this.btnrot.setVisibility(4);
this.btnscl.setVisibility(4);
}
public ImageView getImageView() {
return this.image;
}
public void setFreeze(boolean paramBoolean) {
this.freeze = paramBoolean;
}
}
Layout file
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<ImageButton android:id="#+id/rotate" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:adjustViewBounds="true" android:background="#android:color/transparent" android:scaleType="fitCenter" android:src="#drawable/rotation"/>
<ImageButton android:id="#+id/sacle" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:adjustViewBounds="true" android:background="#android:color/transparent" android:scaleType="fitCenter" android:src="#drawable/pointer"/>
<ImageButton android:id="#+id/del" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:adjustViewBounds="true" android:background="#android:color/transparent" android:scaleType="fitCenter" android:src="#drawable/close"/>
<ImageView android:id="#+id/clipart" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"/>
</RelativeLayout>
and images put in drawable
Related
I'm developing a game, and I have a custom view Joystick which haves a onTouch event. I also have a ImageView which also haves onTouch for shooting.
If I add the views programatically using java code to a main relative layout, the onTouch of the two views works perfectly and at the same time.
The problem is that if I add the views using a XML layout file, then, if you touch the custom view Joystick, the onTouch of the other view doesn't works. It only happens if I use XML layout file for creating the layout.
How can I solve the problem?
The layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/shootImageView"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:src="#drawable/shoot"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<com.myapp.uiviews.Joystick
android:id="#+id/joystick"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
The class:
mainLayout = (RelativeLayout) findViewById(R.id.mainLayout);
shootImageView= (ImageView) findViewById(R.id.shootImageView);
shootImageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
touchX=sw/2;
touchY=sh/2;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
LaserManager.getInstance().startLaserThread();
break;
case MotionEvent.ACTION_UP:
LaserManager.getInstance().stopLaserThread();
break;
}
return true;
}
});
shootImageView.invalidate();
shootImageView.refreshDrawableState();
shootImageView.forceLayout();
joystickOnScreen = (Joystick) findViewById(R.id.joystick);
int factor = GameState.getInstance().getSpaceship().getSpeedFactorCorrespondence();
joystickOnScreen.setMovementRange(sh/factor);
joystickOnScreen.setInnerPadding(sh/30);
joystickOnScreen.setOnJostickMovedListener(joystickListener);
joystickOnScreen.invalidate();
joystickOnScreen.refreshDrawableState();
joystickOnScreen.forceLayout();
Using that code doesn't works, but if I use this next code it works perfectly... why?
shootImageView = new ImageView(this);
shootImageView.setId(102);
RelativeLayout.LayoutParams shootImageViewParams = new RelativeLayout.LayoutParams(sw/8, sw/8);
shootImageViewParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
shootImageViewParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
shootImageViewParams.setMargins(0, 0, sh/15, sh/15);
shootImageView.setLayoutParams(shootImageViewParams);
shootImageView.setImageDrawable(getResources().getDrawable(R.drawable.shoot));
shootImageView.setAlpha(0.4f);
shootImageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
touchX=sw/2;
touchY=sh/2;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
LaserManager.getInstance().startLaserThread();
break;
case MotionEvent.ACTION_UP:
LaserManager.getInstance().stopLaserThread();
break;
}
return true;
}
});
main.addView(shootImageView);
joystickOnScreen = new Joystick(this);
int factor = GameState.getInstance().getSpaceship().getSpeedFactorCorrespondence();
joystickOnScreen.setMovementRange(sh/factor);
joystickOnScreen.setInnerPadding(sh/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);
main.addView(joystickOnScreen);
This is the Joystick class:
public class Joystick extends View {
public static final int INVALID_POINTER = -1;
private JoystickMovedListener moveListener;
//# of pixels moveWithAcceleration required between reporting to the listener
private float moveResolution;
//Max range of moveWithAcceleration 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, AttributeSet attrs) {
super(context, attrs);
initJoystickView();
}
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 ) {
returnHandleToCenterVisually();
returnHandleToCenterAcceleratedMovement();
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 ) {
returnHandleToCenterVisually();
returnHandleToCenterAcceleratedMovement();
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;
moveWithAcceleration();
invalidate();
return true;
}
return false;
}
private void moveWithAcceleration() {
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);
}
final int numberOfFrames = 5;
final double intervalsX = (touchX - touchXDelayedMovement) / numberOfFrames;
final double intervalsY = (touchY - touchYDelayedMovement) / numberOfFrames;
handlerDelayedMovement.removeCallbacksAndMessages(null);
for (int i = 0; i <= numberOfFrames; i++) {
handlerDelayedMovement.postDelayed(new Runnable() {
#Override
public void run() {
touchXDelayedMovement += intervalsX;
touchYDelayedMovement += intervalsY;
reportOnMoved();
}
}, i * 50);
}
}
private void reportOnMoved() {
//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 returnHandleToCenterVisually() {
final int numberOfFrames = 2;
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;
invalidate();
if (moveListener != null && j == numberOfFrames - 1) {
moveListener.OnReturnedToCenter();
}
}
}, i * 15);
}
if (moveListener != null) {
moveListener.OnReleased();
}
}
private void returnHandleToCenterAcceleratedMovement() {
final int numberOfFrames = 10;
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;
reportOnMoved();
}
}, 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();
}
}
Incredible but true, the problem was with
android:layout_height="wrap_content" in shoot button and android:layout_width="wrap_content" in joystick, after setting a fixed size the problem dissapeared. Didn't understand why..
I am Trying to implement a swipe touch to control the paddle in the app, I've managed to get the paddle move by detecting a single touch but I can't understand how to make a fling work. I've tried implementing ths solution by referring the official android docs but this crashes the app. Any Advice?
package com.nblsoft.pong;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v4.view.GestureDetectorCompat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class PongLogic extends View {
MainActivity mainactivity = (MainActivity) getContext();
private GestureDetectorCompat mDetector = new GestureDetectorCompat(mainactivity, new MyGestureListner());
//set screen constrains in dip
Configuration configuration = this.getResources().getConfiguration();
int dpHeight = configuration.screenHeightDp; //The current height of the available screen space, in dp units, corresponding to screen height resource qualifier.
int dpWidth = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
//int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
//DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
//float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
//float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
private int dptopixel(int DESIRED_DP_VALUE) {
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_DP_VALUE) * scale + 0.5f);
}
private int pixeltodp(int DESIRED_PIXEL_VALUE) {
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_PIXEL_VALUE) - 0.5f / scale);
}
//set paddle size, speed, position vector
int AI_paddle_pos_x = 4 * (dptopixel(dpWidth) / 100); //3 for 320x480, 10 for 1080x1920 etc.
int paddle_width = (dptopixel(dpWidth) / 10); //
int AI_paddle_pos_y = (dptopixel(dpHeight) / 10); //48 for 320x480, 190 for 1080x1920 etc.
int paddle_height = (dptopixel(dpHeight) / 100) + 3; //the paddle is 100% of the total height of phone.
int user_paddle_pos_x = 4 * (dptopixel(dpWidth) / 100);
int user_paddle_pos_y = dptopixel(dpHeight) - ((dptopixel(dpHeight) / 10) + (dptopixel(dpHeight) / 100) + 3);
//Score
int score_user = 0;
int score_AI = 0;
//User Paddle
public Rect paddle_user = new Rect(user_paddle_pos_x,
user_paddle_pos_y,
user_paddle_pos_x + paddle_width,
user_paddle_pos_y + paddle_height);
int user_paddle_vel = 0;
//AI paddle
Rect paddle_AI = new Rect(AI_paddle_pos_x,
AI_paddle_pos_y,
AI_paddle_pos_x + paddle_width,
AI_paddle_pos_y + paddle_height);
//set ball position vector, Velocity vector, acceleration
int ball_pos_x = 0;
int ball_pos_y = (dptopixel(dpHeight) / 2);
int ball_size = dptopixel(dpWidth) / 100;
int ball_velocity_x = 1;
int ball_velocity_y = 3;
// Ball
Rect ball = new Rect(ball_pos_x,
ball_pos_y,
ball_pos_x + ball_size,
ball_pos_y + ball_size);
//Override onDraw method
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint myUI = new Paint();
myUI.setColor(Color.WHITE);
Paint myscore = new Paint();
myscore.setTextSize(dptopixel(dpWidth / 20));
myscore.setColor(Color.WHITE);
//mytext.setStyle(Paint.Style.STROKE);
//mytext.setStrokeWidth(2);
// Draw Score
canvas.drawText(Integer.toString(score_AI), (dptopixel(dpWidth) / 10), (dptopixel(dpHeight) / 4), myscore);
canvas.drawText(Integer.toString(score_user), (dptopixel(dpWidth) / 10), 3 * (dptopixel(dpHeight) / 4), myscore);
// Draw Middle point
canvas.drawRect(0, ((dptopixel(dpHeight)) / 2), (dptopixel(dpWidth)), (((dptopixel(dpHeight)) / 2) + 2), myUI);
// Draw both paddles
canvas.drawRect(paddle_user, myUI);
canvas.drawRect(paddle_AI, myUI);
// Draw ball
canvas.drawRect(ball, myUI);
//Practise Methods
//canvas.drawText(Integer.toString(dptopixel(dpHeight)),300,300,mytext);
//canvas.drawText(Integer.toString(dptopixel(dpWidth)), 400, 400, mytext);
//canvas.drawText(Integer.toString(dpHeight),500,500,mytext);
//canvas.drawText(Integer.toString(dpWidth),600,600,mytext);
//canvas.drawText("Fuck", 700, 700, mytext);
//canvas.drawRect(0,0,dptopixel(dpWidth),dptopixel(dpHeight),mytext);
//Game Loop Updater
update();
invalidate();
}
private void update() {
if(ball.centerY() < dptopixel(dpHeight)/2){ paddle_AI.offsetTo(ball.centerX()- dptopixel(10), AI_paddle_pos_y); }
if (paddle_user.contains(ball)) {
ball_velocity_y = ball_velocity_y * -1;
} else if (paddle_AI.contains(ball)) {
ball_velocity_y = ball_velocity_y * -1;
} else if ((ball.centerX() > (dptopixel(dpWidth))) || (ball.centerX() < 0)) {
ball_velocity_x = ball_velocity_x * -1;
} else if (ball.centerY() < 0) {
//Update the user score
score_user = 1 + score_user;
//re draw the ball
ball.offsetTo(0, (dptopixel(dpHeight) / 2));
} else if (ball.centerY() > (dptopixel(dpHeight))) {
//Update the AI score
score_AI = 1 + score_AI;
//re draw the ball
ball.offsetTo(0, (dptopixel(dpHeight) / 2));
}
ball.offset(ball_velocity_x, ball_velocity_y);
}
//Override Touch method
/*
//
NOT WORKING SWIPE METHODS
float x1, x2, y1, y2;
final int MIN_DISTANCE = 70;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = event.getX();
y1 = event.getY();
break;
case MotionEvent.ACTION_UP:
x2 = event.getX();
y2 = event.getY();
float deltaX = x2 - x1;
float deltaY = y2 - y1;
if (deltaX > MIN_DISTANCE)
{
paddle_user.offset(1, 0);
break;
}
else if (Math.abs(deltaX) > MIN_DISTANCE)
{
paddle_user.offset(-1, 0);
break;
}
}
return true;
}*/
#Override
public boolean onTouchEvent(MotionEvent event) {
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
/*
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (event.getX() > dptopixel(dpWidth) / 2) {
paddle_user.offset(10, 0);
} else {
paddle_user.offset(-10, 0);
}
}
return true;
*/
}
/* #Override
public boolean onTouch(View v, MotionEvent event) {
this.paddle_user.offsetTo(10,10);
return true; //Event Handled
}
*/
public PongLogic(Context context) {
super(context);
setBackgroundColor(Color.BLACK); //to set background
this.setFocusableInTouchMode(true); //to enable touch mode
}
class MyGestureListner extends GestureDetector.SimpleOnGestureListener{
#Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
if (event1.getAction() == MotionEvent.ACTION_DOWN) {
user_paddle_vel = (int) velocityX;
paddle_user.offset(user_paddle_vel,0);
}
else if(event1.getAction() == MotionEvent.ACTION_UP){
user_paddle_vel = 0;
}
return true;
}
}
}
You can take a look to my article in Medium, I explained step by step how to do it: https://medium.com/#euryperez/android-pearls-detect-swipe-and-touch-over-a-view-203ae2c028dc#.lcmg4jytc
I'm trying to make a simple app, with an imageview that can be zoomed in and out with buttons, and when zoomed, user could move around it with his fingers.
I've been reading the whole day, here and there, found different solutions. But I can't seem to make my picture move. Thing is, I want to keep the program like it is now, but just maybe change the imageview OnTouchEvent.
I'm adding my code:
public class LoadMap extends Activity {
ZoomControls zoom;
ImageView img;
ImageButton linkButton;
float startX, startY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_map);
zoom = (ZoomControls) findViewById(R.id.zoomControls1);
img = (ImageView) findViewById(R.id.imageView1);
linkButton = (ImageButton) findViewById(R.id.infopoint);
startX = img.getScaleX();
startY = img.getScaleY();
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
linkButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Uri uri = Uri.parse("just some site");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
zoom.setOnZoomInClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
float x = img.getScaleX();
float y = img.getScaleY();
img.setScaleX((float) (x+1));
img.setScaleY((float) (y+1));
}
});
zoom.setOnZoomOutClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
float x = img.getScaleX();
float y = img.getScaleY();
if((x>startX) & (y>startY)) {
img.setScaleX((float) (x-1));
img.setScaleY((float) (y-1));
}
}
});
}
Is there a way to just change some things inside this method without messing my app too much and is this even possible the way I'm trying to make it work or my program logic is wrong.
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
Sorry, if it has been asked before, but i searched alot and couldn't find anything specific for my 'problem'.
Thanks alot to whoever spends some time on my problem :).
try this code,
put this code on image setOnTouchListener,
img.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
LayoutParams layoutParams = (LayoutParams) img
.getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int x_cord = (int) event.getRawX();
int y_cord = (int) event.getRawY();
if (x_cord > windowwidth) {
x_cord = windowwidth;
}
if (y_cord > windowheight) {
y_cord = windowheight;
}
layoutParams.leftMargin = x_cord - 25;
layoutParams.topMargin = y_cord - 75;
img.setLayoutParams(layoutParams);
break;
default:
break;
}
return true;
}
});
I hope it will solve your problem..
Try using a custom ImageVIew
public class ZoomableImageView extends ImageView implements OnTouchListener {
private Context mContext;
final private float MAX_SCALE = 2f;
private Matrix mMatrix;
private final float[] mMatrixValues = new float[9];
// display width height.
private int mWidth;
private int mHeight;
private int mIntrinsicWidth;
private int mIntrinsicHeight;
private float mScale;
private float mMinScale;
private float mPrevDistance;
private boolean isScaling;
private int mPrevMoveX;
private int mPrevMoveY;
private GestureDetector mDetector;
final String TAG = "ScaleImageView";
public ZoomableImageView(Context context, AttributeSet attr) {
super(context, attr);
this.mContext = context;
initialize();
}
public ZoomableImageView(Context context) {
super(context);
this.mContext = context;
initialize();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.initialize();
}
#Override
public void setImageResource(int resId) {
super.setImageResource(resId);
this.initialize();
}
private void initialize() {
this.setScaleType(ScaleType.MATRIX);
this.mMatrix = new Matrix();
Drawable d = getDrawable();
if (d != null) {
mIntrinsicWidth = d.getIntrinsicWidth();
mIntrinsicHeight = d.getIntrinsicHeight();
setOnTouchListener(this);
}
mDetector = new GestureDetector(mContext,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
maxZoomTo((int) e.getX(), (int) e.getY());
cutting();
return super.onDoubleTap(e);
}
});
}
#Override
protected boolean setFrame(int l, int t, int r, int b) {
mWidth = r - l;
mHeight = b - t;
mMatrix.reset();
int rNorm = r - l;
mScale = (float) rNorm / (float) mIntrinsicWidth;
int paddingHeight = 0;
int paddingWidth = 0;
// scaling vertical
if (mScale * mIntrinsicHeight > mHeight) {
mScale = (float) mHeight / (float) mIntrinsicHeight;
mMatrix.postScale(mScale, mScale);
paddingWidth = (r - mWidth) / 2;
paddingHeight = 0;
// scaling horizontal
} else {
mMatrix.postScale(mScale, mScale);
paddingHeight = (b - mHeight) / 2;
paddingWidth = 0;
}
mMatrix.postTranslate(paddingWidth, paddingHeight);
setImageMatrix(mMatrix);
mMinScale = mScale;
zoomTo(mScale, mWidth / 2, mHeight / 2);
cutting();
return super.setFrame(l, t, r, b);
}
protected float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
protected float getScale() {
return getValue(mMatrix, Matrix.MSCALE_X);
}
public float getTranslateX() {
return getValue(mMatrix, Matrix.MTRANS_X);
}
protected float getTranslateY() {
return getValue(mMatrix, Matrix.MTRANS_Y);
}
protected void maxZoomTo(int x, int y) {
if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
// threshold 0.1f
float scale = mMinScale / getScale();
zoomTo(scale, x, y);
} else {
float scale = MAX_SCALE / getScale();
zoomTo(scale, x, y);
}
}
public void zoomTo(float scale, int x, int y) {
if (getScale() * scale < mMinScale) {
return;
}
if (scale >= 1 && getScale() * scale > MAX_SCALE) {
return;
}
mMatrix.postScale(scale, scale);
// move to center
mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2,
-(mHeight * scale - mHeight) / 2);
// move x and y distance
mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
setImageMatrix(mMatrix);
}
public void cutting() {
int width = (int) (mIntrinsicWidth * getScale());
int height = (int) (mIntrinsicHeight * getScale());
if (getTranslateX() < -(width - mWidth)) {
mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
}
if (getTranslateX() > 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
}
if (getTranslateY() < -(height - mHeight)) {
mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
}
if (getTranslateY() > 0) {
mMatrix.postTranslate(0, -getTranslateY());
}
if (width < mWidth) {
mMatrix.postTranslate((mWidth - width) / 2, 0);
}
if (height < mHeight) {
mMatrix.postTranslate(0, (mHeight - height) / 2);
}
setImageMatrix(mMatrix);
}
private float distance(float x0, float x1, float y0, float y1) {
float x = x0 - x1;
float y = y0 - y1;
return FloatMath.sqrt(x * x + y * y);
}
private float dispDistance() {
return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (mDetector.onTouchEvent(event)) {
return true;
}
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
if (touchCount >= 2) {
float distance = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
mPrevDistance = distance;
isScaling = true;
} else {
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
}
case MotionEvent.ACTION_MOVE:
if (touchCount >= 2 && isScaling) {
float dist = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
float scale = (dist - mPrevDistance) / dispDistance();
mPrevDistance = dist;
scale += 1;
scale = scale * scale;
zoomTo(scale, mWidth / 2, mHeight / 2);
cutting();
} else if (!isScaling) {
int distanceX = mPrevMoveX - (int) event.getX();
int distanceY = mPrevMoveY - (int) event.getY();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
mMatrix.postTranslate(-distanceX, -distanceY);
cutting();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_2_UP:
if (event.getPointerCount() <= 1) {
isScaling = false;
}
break;
}
return true;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return super.onTouchEvent(event);
}
}
Try it that way:
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
return true;
}
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
You need to return true when the ACTION_DOWN event is triggered to indicate that you are interested in the subsequent calls relating to that same event.
Is there an equivalent in android of UIScrollView where I can slide an image around. Thank you.
Sorry I was not more specific. I need to slide in all directions. Androids ScrollView will not do this. I been using a WebView which works but it only works on 4.0 and higher it seems like.
This is what I came up with. I put my ImageView inside of a WebView and I assigned setOnTouchListener on ImageView. It seems to work well.
web = new WebView(this);
web.setVerticalScrollBarEnabled(false);
web.setHorizontalScrollBarEnabled(false);
web.setBackgroundColor(Color.RED);
web.setId(100);
RelativeLayout.LayoutParams layoutForContainer = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutForContainer.height = 300;
layoutForContainer.width = 300;
layoutForContainer.addRule(RelativeLayout.CENTER_IN_PARENT);
web.setLayoutParams(layoutForContainer);
layout.addView(web);
myImageView = new ImageView(this);
myImageView.setImageResource(R.drawable.myImage);
RelativeLayout.LayoutParams layoutForLargeImage = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutForLargeImage.height = (int) 768;
layoutForLargeImage.width = (int) 1024;
myImageView.setLayoutParams(layoutForLargeImage);
web.addView(myImageView);
myImageView.setOnTouchListener(new View.OnTouchListener() {
float downX, downY;
int scrollByX, scrollByY;
public boolean onTouch(View view, MotionEvent event) {
float currentX, currentY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
currentX = event.getX();
currentY = event.getY();
scrollByX = (int)(downX - currentX);
scrollByY = (int)(downY - currentY);
myImageView.scrollBy(scrollByX, scrollByY);
downX = currentX;
downY = currentY;
break;
}
return true;
}
});
This question's almost a year old, but it's the top in a google search for this problem, so I thought I'd add some details.
If you're looking for a generic one direction view that can scroll, the other answers of using Android's built in ScrollView will work fine.
However, if you want the multidirectional aspect of iOS's UIScrollView, you'll have to design/use a custom view (or use a hack such as using a webview). An example implementation of a view that can do this can be found in a related question here:
View with horizontal and vertical pan/drag and pinch-zoom
Then depending on your tastes, you can modify it as needed, such as this implementation without the zooming (just the panning):
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.*;
public class MultiDirectionPanGroup extends ViewGroup {
private static final int INVALID_POINTER_ID = 1;
private int mActivePointerId = INVALID_POINTER_ID;
private float mPosX;
private float mPosY;
private Matrix mTranslateMatrix = new Matrix();
private Matrix mTranslateMatrixInverse = new Matrix();
private float mLastTouchX;
private float mLastTouchY;
private float mFocusY;
private float mFocusX;
private float[] mInvalidateWorkingArray = new float[6];
private float[] mDispatchTouchEventWorkingArray = new float[2];
private float[] mOnTouchEventWorkingArray = new float[2];
public MultiDirectionPanGroup(Context context) {
super(context);
mTranslateMatrix.setTranslate(0, 0);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight());
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.translate(mPosX, mPosY);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mDispatchTouchEventWorkingArray[0] = ev.getX();
mDispatchTouchEventWorkingArray[1] = ev.getY();
mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
ev.setLocation(mDispatchTouchEventWorkingArray[0],
mDispatchTouchEventWorkingArray[1]);
return super.dispatchTouchEvent(ev);
}
/**
* Although the docs say that you shouldn't override this, I decided to do
* so because it offers me an easy way to change the invalidated area to my
* likening.
*/
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
mInvalidateWorkingArray[0] = dirty.left;
mInvalidateWorkingArray[1] = dirty.top;
mInvalidateWorkingArray[2] = dirty.right;
mInvalidateWorkingArray[3] = dirty.bottom;
mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]),
Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3]));
location[0] *= mScaleFactor;
location[1] *= mScaleFactor;
return super.invalidateChildInParent(location, dirty);
}
private float[] scaledPointsToScreenPoints(float[] a) {
mTranslateMatrix.mapPoints(a);
return a;
}
private float[] screenPointsToScaledPoints(float[] a){
mTranslateMatrixInverse.mapPoints(a);
return a;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mOnTouchEventWorkingArray[0] = ev.getX();
mOnTouchEventWorkingArray[1] = ev.getY();
mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
mTranslateMatrix.preTranslate(dx, dy);
mTranslateMatrix.invert(mTranslateMatrixInverse);
mLastTouchX = x;
mLastTouchY = y;
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
}
Obviously, there are many options like UIScrollView, (1) you can take HorizontalScrollView (2) you can take Viewpager.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<WebView
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
you can do this using in android
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
</ScrollView>
I try to implement the zoom in/out by spread/pinch gesture and the drag and drop functions on a Relative Layout.
This is the code of my OnPinchListener to handle the zoom effect.
The mainView is the RelativeLayout defined in the layout xml file.
I implement the touch listener in the fakeview which should be in front of all view. The touch event will change the mainview according to the code.
I want to ask if it is possible to get the actual left, top, width and height after the scale? It always return 0,0 for left and top, and the original width and height after zoom.
Thanks very much!
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linear_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/zoomable_relative_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
>
<ImageView
android:id="#+id/imageView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/background" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/relative_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:orientation="vertical" >
</RelativeLayout>
</RelativeLayout>
public class MainActivity extends Activity {
//ZoomableRelativeLayout mainView = null;
RelativeLayout mainView = null;
RelativeLayout rl = null;
public static final String TAG = "ZoomText."
+ MainActivity.class.getSimpleName();
private int offset_x;
private int offset_y;
private boolean dragMutex = false;
RelativeLayout fakeView = null;
float width = 0, height = 0;
private OnTouchListener listener = new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
// Log.e(TAG, event + "");
// Log.e(TAG, "Pointer Count = "+event.getPointerCount());
Log.e(TAG,
event.getX() + "," + event.getY() + "|" + mainView.getX()
+ "(" + mainView.getWidth() + "),"
+ mainView.getY() + "(" + mainView.getHeight()
+ ")");
if (event.getX() >= mainView.getLeft()
&& event.getX() <= mainView.getLeft() + mainView.getWidth()
&& event.getY() >= mainView.getTop()
&& event.getY() <=mainView.getTop() + mainView.getHeight())
if (event.getPointerCount() > 1) {
return scaleGestureDetector.onTouchEvent(event);
} else {
return llListener.onTouch(arg0, event);
}
return false;
}
};
private ScaleGestureDetector scaleGestureDetector;
private OnTouchListener llListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
// Log.d(TAG, event + ",LL");
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
offset_x = (int) event.getX();
offset_y = (int) event.getY();
// Log.e(TAG, offset_x + "," + offset_y);
dragMutex = true;
return true;
case MotionEvent.ACTION_MOVE:
// Log.e(TAG, "Finger down");
int x = (int) event.getX() - offset_x;
int y = (int) event.getY() - offset_y;
Log.e(TAG, event.getX() + "," + event.getY());
float _x = mainView.getX();
float _y = mainView.getY();
mainView.setX(_x + x);
mainView.setY(_y + y);
offset_x = (int) event.getX();
offset_y = (int) event.getY();
return true;
case MotionEvent.ACTION_UP:
dragMutex = false;
return true;
}
return false;
}
};
private OnDragListener dragListener = new View.OnDragListener() {
#Override
public boolean onDrag(View arg0, DragEvent arg1) {
Log.e(TAG, "DRAG Listener = " + arg1);
return false;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainView = (RelativeLayout) findViewById(R.id.zoomable_relative_layout);
// mainView.setOnTouchListener(new OnPinchListener());
// mainView.setOnTouchListener(listener);
scaleGestureDetector = new ScaleGestureDetector(this,
new OnPinchListener());
rl = (RelativeLayout) findViewById(R.id.linear_layout);
mainView.setOnDragListener(dragListener);
// mainView.setOnTouchListener(llListener);
fakeView = (RelativeLayout) findViewById(R.id.relative_layout);
fakeView.setOnTouchListener(listener);
}
class OnPinchListener extends SimpleOnScaleGestureListener {
float startingSpan;
float endSpan;
float startFocusX;
float startFocusY;
public boolean onScaleBegin(ScaleGestureDetector detector) {
startingSpan = detector.getCurrentSpan();
startFocusX = detector.getFocusX();
startFocusY = detector.getFocusY();
return true;
}
public boolean onScale(ScaleGestureDetector detector) {
// mainView.scale(detector.getCurrentSpan() / startingSpan,
// startFocusX, startFocusY);
// if(width==0)
// width = mainView.getWidth();
// if(height==0)
// height = mainView.getHeight();
mainView.setPivotX(startFocusX);
mainView.setPivotY(startFocusY);
mainView.setScaleX(detector.getCurrentSpan() / startingSpan);
mainView.setScaleY(detector.getCurrentSpan() / startingSpan);
// LayoutParams para = mainView.getLayoutParams();
// width*=detector.getCurrentSpan() / startingSpan;
// height*=detector.getCurrentSpan() / startingSpan;
// para.width = (int)width;
// para.height = (int)height;
// mainView.setLayoutParams(para);
return true;
}
public void onScaleEnd(ScaleGestureDetector detector) {
//mainView.restore();
mainView.invalidate();
Log.e(TAG, mainView.getLeft()+","+mainView.getRight());
}
}
}
you need to get the transformation matrix and use that to transform your original points.
so something like this (after you do the scaling):
Matrix m = view.getMatrix(); //gives you the transform matrix
m.mapPoints(newPoints, oldPoints); //transform the original points.
This is how I solved it in my case (getViewRect should be called after view was laid out, for example through view.post(Runnable), so view.getWidth()/getHeight() returns actual values):
public static Rect getViewRect(View view) {
Rect outRect = new Rect();
outRect.right = (int)(view.getWidth() * getScalelX(view));
outRect.bottom = (int)(view.getHeight() * getScalelY(view));
int[] location = new int[2];
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect;
}
public static float getScalelX(View view) {
float scaleX = view.getScaleX();
view = getParent(view);
while (view != null) {
scaleX *= view.getScaleX();
view = getParent(view);
}
return scaleX;
}
public static float getScalelY(View view) {
float scaleX = view.getScaleY();
view = getParent(view);
while (view != null) {
scaleX *= view.getScaleY();
view = getParent(view);
}
return scaleX;
}
public static ViewGroup getParent(View view) {
if (view == null) {
return null;
}
ViewParent parent = view.getParent();
if (parent instanceof View) {
return (ViewGroup) parent;
}
return null;
}
however it just turned out that android 3.1 (probably 3.0 also) doesn't take in account scale factor while dealing with getLocationOnScreen method.
I'm gonna to manually scale the rect before returning it from my getViewRect(View) function in case of android 3.1 api like this:
public static void scaleRect(Rect rect, float scaleX, float scaleY, float pivotX, float pivotY) {
rect.left = (int) ((rect.left - pivotX) * scaleX + pivotX);
rect.right = (int) ((rect.right - pivotX) * scaleX + pivotX);
rect.top = (int) ((rect.top - pivotY) * scaleY + pivotY);
rect.bottom = (int) ((rect.bottom - pivotY) * scaleY + pivotY);
}
However one should know a pivot coordinates for every view in the hierarchy from the current view to the root and corresponding zoom levels to handle this transformation correctly.
If someone has any straightforward solution it will be appreciated
EDIT:
This is how I modified the getViewRect(View) method so it works on 3.1 also:
public static Rect getViewRect(View view) {
Rect outRect = new Rect();
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2){
outRect.right = (int)(view.getWidth() * getScalelX(view));
outRect.bottom = (int)(view.getHeight() * getScalelY(view));
int[] location = new int[2];
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
} else {
outRect.right = view.getWidth();
outRect.bottom = view.getHeight();
int[] location = new int[2];
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
View parent = view;
while(parent != null){
parent.getLocationOnScreen(location);
scaleRect(outRect, parent.getScaleX(), parent.getScaleY(), parent.getPivotX() + location[0], parent.getPivotY() + location[1]);
parent = getParent(parent);
}
}
return outRect;
}
I think if- clause could be removed so that the second branch (else) should work for all versions. However I prefer to use straightforward solution (the first branch of the if) so that the second is just a workaround :)