Dragging a rotated view by adjusting layout params - java

I am trying to make a view follow my finger and do some rotation and scaling in multitouch with the following code
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final int action = motionEvent.getActionMasked();
int newPosX,newPosY;
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = motionEvent.getActionIndex();
final float x = motionEvent.getX( pointerIndex);
final float y = motionEvent.getY( pointerIndex);
RelativeLayout.LayoutParams parms = (RelativeLayout.LayoutParams) view.getLayoutParams();
// Remember where we started (for dragging)
mLastTouchX = (int) x;
mLastTouchY = (int) y;
// Save the ID of this pointer (for dragging)
mActivePointerId = motionEvent.getPointerId( 0);
break;
}
case MotionEvent.ACTION_MOVE: {
if(motionEvent.getPointerCount()==2){
float newDist = spacing(motionEvent);
float scale = newDist / oldDist * view.getScaleX();
view.setScaleY(scale);
view.setScaleX(scale);
float newAngle = rotation(motionEvent);
float a = newAngle - oldAngle;
view.animate().rotationBy(a).setDuration(0).setInterpolator(new LinearInterpolator()).start();
}
// Find the index of the active pointer and fetch its position
final int pointerIndex =
motionEvent.findPointerIndex( mActivePointerId);
final float x = motionEvent.getX( pointerIndex);
final float y = motionEvent.getY( pointerIndex);
// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
view.setLayoutParams(layoutParams);
break;
}
case MotionEvent.ACTION_POINTER_DOWN:{
oldDist = spacing(motionEvent);
oldAngle = rotation(motionEvent);
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = motionEvent.getActionIndex();
final int pointerId = motionEvent.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 = (int) motionEvent.getX( newPointerIndex);
mLastTouchY = (int) motionEvent.getY( newPointerIndex);
mActivePointerId = motionEvent.getPointerId( newPointerIndex);
}
break;
}
}
return true;
}
private float spacing(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);
}
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
Everything is working well until the view is rotated. When it is rotated above 90 degrees and we try to drag it it's jumping around the touch point. I think it has something to do with
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
setLayoutParams(layoutParams);
I am working to make this work for last two days with no success.
NOTE: what I am trying to achieve is to make a view drag rotate and scale with 2 fingers(drag with single finger also)
I followed the drag code from Google's documentation so as to make it not jump while switching fingers.
I am using this to rotate
view.animate().rotationBy(a).setDuration(0).setInterpolator(new LinearInterpolator()).start();
because when i use view.setRotate(),the view is vibrating.
EDIT 1:
I removed
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
and replaced it with:
//To get the moved distance of the finger(X and Y)
float diffX = motionEvent.getX(pointerIndex) - mLastTouchX;
float diffY = motionEvent.getY(pointerIndex) - mLastTouchY;
//to get the distance from touch point and the top or left of the view
final float dx = motionEvent.getRawX() - (motionEvent.getRawX()-motionEvent.getX());
final float dy = motionEvent.getRawY() - (motionEvent.getRawY()-motionEvent.getY());
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
//Settings appropriate value for the margin top and margin left.
layoutParams.leftMargin = (int) ((( motionEvent.getRawX() )-dx )+ diffX );
layoutParams.topMargin = (int) ((( motionEvent.getRawY() )-dy )+ diffY );
Now even after rotating the view is not moving around. but when i switch the active finger its jumping around from the actual position.
On ACTION_POINTER_UP i am doing this to change shift the active finger
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = motionEvent.getActionIndex();
final int pointerId = motionEvent.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;
Log.d(TAG,"New pointer index :"+newPointerIndex);
mLastTouchX = (int) motionEvent.getX( newPointerIndex);
mLastTouchY = (int) motionEvent.getY( newPointerIndex);
mActivePointerId = motionEvent.getPointerId( newPointerIndex);
}
break;
}

Normally I would advise against manipulating the view's position and size excessively on runtime, it updates the layout needlessly lowers performance, hard to control and rarely looks good.
Instead, if you don't want to delve into OpenGl, then my advice would be a Custom View, create a class that extend View which you later add in your layout XML, <com.packagename.MyCustomView ... />.
this view will have the size of your whole screen.
You #Override the onDraw(Canvas canvas) method, it will give you the canvas of the view, you can use the Canvas to draw graphics in the View.
write invalidate(); in the end of you onDraw() to tell the View to try and call onDraw() again when available, you should stop calling it once user stops touching your view so add some boolean to it.
now the touches, you can register to touch events like you did but inside your custom view and use the x,y and the size of your view from the canvas.getWidth() and canvas.getHeight() to determine what to do.
if you need the size of the view before you reach onDraw() override onMeasure().
Summery
This may not look that easy but it is, and it's fast and quit efficient, the graphics will be smooth, if you want the best possible outcome and are not afraid of lots of code then you can look into drawing using OpenGl but I wouldn't advise you that for what your trying to do.
Hope I understood the problem and helped you - Good Luck!

Related

add signature or image on PDF using drag and drop android (get x and y coordinates )

I am using Pdfbox https://github.com/TomRoush/PdfBox-Android to add signature(image) on pdf i want to add signature using drag and drop i have implemented some code using touch listener please see and help me i am not getting exact x and y coordinates to add it in pdfbox code from touch listener and i also try drag and drop codehttps://www.tutorialspoint.com/android/android_drag_and_drop.htm but it won't help. when i test the app and drop the signature(image) on pdf the result pdf won't get the signature on right position. please help your little brother and guide me if anything wrong with the code
Here is the code of Pdfbox
private void createPdfTwo(File file,float x,float y) {
try {
PDDocument doc = PDDocument.load(file);
PDPage page = doc.getPages().get(0);
// StoredPath is the path where it gonna be saved
PDImageXObject pdImage = PDImageXObject.createFromFile(StoredPath,doc);
PDPageContentStream contents = new PDPageContentStream(doc, page,true,false);
// here is the x,y which i am getting from touch listener
contents.drawImage(pdImage, x, y,150,150);
contents.close();
doc.save(storedPath);
doc.close();
pdfView.fromFile(new File(storedPath)).load();
imageView.setVisibility(View.GONE);
} catch (IOException e) {
e.printStackTrace();
}
}
here is the touch listener code
#SuppressLint("ClickableViewAccessibility")
private View.OnTouchListener onTouchListener()
{
return (view, event) -> {
final int x = (int) event.getRawX();
final int y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams)
view.getLayoutParams();
xDelta = x - lParams.leftMargin;
yDelta = y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
// here is the code where magic should happen
RelativeLayout.LayoutParams qParams = (RelativeLayout.LayoutParams)
view.getLayoutParams();
float xD = x - qParams.leftMargin;
float yD = y - qParams.topMargin;
// this method will calling the create pdf (file is defined global)
createPdfTwo(file,xD,yD);
Toast.makeText(MainActivity.this,
"Signature Drop", Toast.LENGTH_SHORT)
.show();
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = x - xDelta;
layoutParams.topMargin = y - yDelta;
layoutParams.rightMargin = 0;
layoutParams.bottomMargin = 0;
view.setLayoutParams(layoutParams);
break;
}
mainLayout.invalidate();
return true;
};
}
}

How to Pan Zoom in Zoom out Functionality in min3d Android

I am new in OpenGl Devloper,i want to render 3d model in OpenGl android so i choose min3d framework library.
I want to Pan Zoom in zoom out functionality for my model ,like camera zoom in zoom out in min3d
like , i have 3dmodel of lady and i want to zoom out her face any solution without scale object?
You need to change the camera position. I suggest you define some additional parameters center and distance since you want to pan.
When you have a zoom gesture you simply apply zoom scale to distance distance *= scale (or distance = originalDistance*scale depending on the implementation).
On pan you simply move the center by distances center.x += (newXOnScreen-oldXOnScreen)*speedFactor and center.y += (newYOnScreen-oldYOnScreen)*speedFactor. The speed factor may be constant here (play around a bit with it) but it might be best to multiply it by a zoom scale as well so that if it is very close the center will move less.
Now that you have these 2 parameters you need to apply them to the camera. Assuming you have a model position at (0,0,0):
scene.camera.position = {center.x, center.y, center.z-distance}
scene.camera.target = {center.x, center.y, center.z}
scene.camera.up = {0, 1, 0}
The following code snippet worked for me. It is not the complete code, but I have just added the part which does the pinch zoom and drag of the 3d object in min3d framework. I put together the code by following online tutorials from here and modified according to the application
//import statements
public class threedviewActivity extends RendererActivity {
private Object3dContainer Object3D;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f,mLastTouchX,mLastTouchY,mPosX,mPosY;
private int mActivePointerId = INVALID_POINTER_ID,flag;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScaleDetector = new ScaleGestureDetector(threedviewActivity.this, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float y = MotionEventCompat.getY(ev, pointerIndex);
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
updateScene();
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
// Remember this touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;
flag = 1;
updateScene();
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, 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 = MotionEventCompat.getX(ev, newPointerIndex);
mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
break;
}
}
return true;
}
#Override
public void initScene()
{
//this is where you initialize your 3d object. Check below for links for tutorials on that.
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
Object3D.scale().x = Object3D.scale().y = Object3D.scale().z = mScaleFactor*1.5f;
flag = 0;
updateScene();
return true;
}
}
#Override
public void updateScene()
{
if(flag == 1)
{
Object3D.position().x = mPosX/100;
Object3D.position().y = -mPosY/100;
}
}
}
For initalizing min3D, follow the tutorials here and here

Java Android onTouch Direction

I am trying to develop a simple game wherein I need to throw a bullet .
I would like the ball follows the touch, or swipe direction.
However, I have a small problem that I have not had success in solving.
I take coordinates on the ACTION_DOWN of MotionEvent, and the coordinates on the ACTION_UP of MotionEvent, then calculate the straight line equation in order to move the ball according to this straight line.
The problem is that when I swipe in a direction close to "up" or "forward" or "in front of", the ball moves at lightning speed while in the other direction of more skewed (right or left) or diagonal, the ball moves at a normal speed.
Where is the flaw in my calculation?
Help me please, I am not far from the result but need to you for solve this problem!
The onTouch method:
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case(MotionEvent.ACTION_DOWN):
balle.setX(event.getX());
balle.setY(event.getY());
ya = event.getY();
xa = event.getX();
break;
case(MotionEvent.ACTION_UP):
xb = event.getX();
yb = event.getY();
balle.setMove(true);
break;
}
return true;
}
This is my moveDirection method:
public void moveDirection(float xa, float ya, float xb, float yb) {
if(!move) {
return;
}
else {
float m, b;
float dx = xb-xa;
m = (yb - ya) / (xb - xa);
b = ya - (m * xa);
if(dx > 0) {
this.x += speedX;
}
else {
this.x += -speedX;
}
this.y = (m * this.x + b);
}
}
Thank you in advance!
I see you are not capturing all the events so, maybe the code from the Android Documentation with de VelocityTracker api may help, in this example when you pull down your finger, a new tracker is created and when you move (in any direction) you capture the velocity of the event, i think you can move the ball according with the velocity and direction of touch.
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "Velocity";
...
private VelocityTracker mVelocityTracker = null;
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Best practice to use VelocityTrackerCompat where possible.
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
}
Link for doc.

Android- How to drag a picture on a canvas

I need to drag an image like in a videogame. I have a few lines drawed and I need to drag it and see the lines drawed before "out" of the window. I give you two images trying to help to understand it.
Image before drag:
Then I touch the screen with my finger and I make a move to the top of the screen, so this three lines have to be upper than the initial image, like this:
I don't know how to do this "drag efect", can someone help me? This is not an image as a .jpg file, is three lines drawed with "drawLine" on a canvas into an imageView.
My onTouchEvent Code and my drawRectangle code:
public boolean onTouch(View view, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_UP:
{
x = event.getX();
y = event.getY();
getFirstFoot();
return true;
}
case MotionEvent.ACTION_MOVE:
{
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
canvas.save();
canvas.translate(ammountX, ammountY);
//canvas.restore();
imageView.invalidate();
return true;
}
case MotionEvent.ACTION_DOWN:
{
startX = event.getX();
startY = event.getY();
}
}
return true;
}
drawRectagle code:
private void drawRectangle(boolean erase)
{
buttonRed.setVisibility(View.VISIBLE);
buttonGree.setVisibility(View.VISIBLE);
buttonYellow.setVisibility(View.VISIBLE);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.ascensorsininfo);
canvas.drawBitmap(bitmap2, x - (bitmap2.getWidth() / 2), y - bitmap2.getHeight(), new Paint());
drawedPoints.add(String.valueOf(x - (bitmap2.getWidth() / 2)));
drawedPoints.add(String.valueOf(y - bitmap2.getHeight()));
imageView.invalidate();
y -= bitmap2.getHeight();
}
Any solution would be apreciated. Thanks!
Check out Canvas.translate
It will "move" everything you draw afterwards the ammount you say in x and y axis.
Don't forget to save and restore it, like:
canvas.save();
canvas.translate(ammountX, ammountY);
// [draw your stuff here]
canvas.restore();
If your question is more towards how to detect the dragging effect, you have to Override your view's onTouchEvent method. Something like:
#Override
public boolean onTouchEvent (MotionEvent event){
if(event.getActionMasked() == MotionEvent.DOWN){
startX = event.getX();
startY = event.getY();
return true;
}else if(event.getActionMasked() == MotionEvent.MOVE){
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
return true;
}
return super.onTouchEvent(event);
}

Finding new point position after scaling

I scaled my canvas as following (in onDraw):
canvas.scale(mScaleFactor, mScaleFactor, mScaleFocusX, mScaleFocusY);
My onScale function is:
private float mScaleFactor = 1.f;
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFocusX = detector.getFocusX();
mScaleFocusY = detector.getFocusY();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
Now I get more points (using onTouchEvent) to create new path which eventually I want to draw.
onTouchEvent gives me x and y but doesn't take into account that a scale occurred.
How can I know where is the new position of x and y after the scaling?
From your question it is not clear what you do with mScaleFactor, mScaleFocusX, mScaleFocusY when rendering you canvas.
But, basically you just need to reverse this process to calculate the original coordinates of the touch (or any other form of interaction).
So for example if you calculate the render coordinates like this:
float renderX = originalX * mScaleFactor + mScaleFocusX;
float renderY = originalY * mScaleFactor + mScaleFocusY;
so would need to use
float correctedTouchX = (touchX - mScaleFocusX) / mScaleFactor;
float correctedTouchY = (touchY - mScaleFocusY) / mScaleFactor;
to calculate the corrected (original) coordinates. If you use mScaleFactor, mScaleFocusX, mScaleFocusY in some other way, you need to reverse that.
UPDATE: It seems that Canvas.scale() first uses mScaleFocusX and mScaleFocusY to define a pivot point (i.e. offset) and then scale. So you probably need these formulas (maybe change a minus into a plus sign somewhere):
float correctedTouchX = touchX / mScaleFactor - mScaleFocusX;
float correctedTouchY = touchY / mScaleFactor - mScaleFocusY;

Categories