onTouchEvent Action_Move choppy interaction - android

I have the following code:
public boolean onTouchEvent(MotionEvent event) {
fingerSize = event.getSize();
fingerPosX = event.getX();
fingerPosY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
start = true;
break;
}
}
return true;
}
I use the three variables fingerSize, fingerPosX and fingerPosY to draw a circle:
canvas.drawCircle(fingerPosX, fingerPosY, fingerSize
* fingersizeCorrection, paint);
So basically the circle follows my finger as i move through the screen with my finger. The problem is that the movement of the circle as it tries to follow my finger is very choppy.
How this be improved to be more fluint?
this all happens in a SurfaceView, i also added a fps limiter like this:
int FRAMES_PER_SECOND = 60;
int SKIP_TICKS = 1000/FRAMES_PER_SECOND;
long next_game_tick = timer.getElapsedTimeMil();
int sleep_time = 0;
next_game_tick += SKIP_TICKS;
sleep_time = (int) (next_game_tick-timer.getElapsedTimeMil());
if(sleep_time >= 0){
try {
Thread.sleep(sleep_time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
}

Related

Implement inertia in custom scroll in SurfaceView

In my Android game I have implemented a custom code in the onTouchEvent method of a (custom) SurfaceView to emulate a ScrollView. I already tried an actual ScrollView, but due to performance and lack of customizability I preferred to override onTouchEvent myself.
It works perfectly, but I cannot properly emulate the inertia effect typical of scrollviews.
What I did right now is this:
int beginRawY = 0;
int beginScrollValue = 0;
Integer firstPointerId = null;
[...]
private boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
int rawY = (int) event.getRawY();
// This is meant to deal with multitouch: scroll only if I'm using "the same finger".
boolean rightPointer = firstPointerId != null && firstPointerId == pointerId;
switch (action) {
case MotionEvent.ACTION_DOWN:
initVelocityTracker(event);
if(firstPointerId == null) {
// Save initial scrollValue and touch position
beginRawY = rawY;
beginScrollValue = scrollValue;
firstPointerId = pointerId;
return true;
}
case MotionEvent.ACTION_MOVE:
if(rightPointer) {
updateVelocity(event);
// Updates scroll value to new scroll value
scrollValue = beginScrollValue -rawY + beginRawY;
return true;
}
case MotionEvent.ACTION_UP:
if(rightPointer) {
//Start the Dissipator runnable, passing to it the speed of the player's touch (number of pixels in 30 milliseconds)
dissipator.setVelocity(updateVelocity(event).pixelY());
dissipator.run();
firstPointerId = null;
return true;
}
}
return false;
}
private void initVelocityTracker(MotionEvent event) {
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);
}
private PixelDot updateVelocity(MotionEvent event) {
int pointerId = event.getPointerId(event.getActionIndex());
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(30);
return new PixelDot(
VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId),
VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId));
}
[...]
private class DissipatorRunnable implements Runnable {
private float velocity = 0;
public void setVelocity(float velocity) {
this.velocity = velocity;
}
#Override
public void run() {
// Simply linear descreasing of the scroll speed
float vValue = velocity;
if(velocity > 0) {
while (vValue > 0 && scrollValue > 0) {
scrollValue = scrollValue - vValue;
vValue -= 5;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
while (vValue < 0 && scrollValue < height) {
scrollValue = scrollValue - vValue;
vValue += 5;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Code is simplier than it looks:
I use a ScrollValue to represent "how much the player scrolled". It is the number of pixels.
On Touch Down a save th eposition of the finger and the actual scrollvalue. I initialize a VelocityTracker too.
On Touch Move I update the velocity tracker and the scrollvalue.
On Touch Up I start a custom Runnable called Dissipator:
Every tot milliseconds I reduce (or increment) scrollvalue by the last tracked velocity, and then reduce the velocity value.
This kinda works, but the effect doesn't look like a scrollview with proper inertia, inertia is instead silly and too strong.
What should I do to emulate standard scrollviews inertia?

How can I use pinch zoom without calling my swipe function in webview?

When I use the pinch zoom gesture, in a horizontal way, the swipe function is always called when it reached a certain difference of the pressed coordinates and the released coordinates.
How can I fix this?
This are the settings for the webview
view.getSettings().setBuiltInZoomControls(true);
view.getSettings().setSupportZoom(true);
view.getSettings().setDisplayZoomControls(false);
The swipe function
protected void swipePage(View v, MotionEvent event, int book) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_DOWN):
swipeOriginX = event.getX();
swipeOriginY = event.getY();
break;
case (MotionEvent.ACTION_UP):
int quarterWidth = (int) (screenWidth * 0.3);
float diffX = swipeOriginX - event.getX();
float diffY = swipeOriginY - event.getY();
float absDiffX = Math.abs(diffX);
float absDiffY = Math.abs(diffY);
if ((diffX > quarterWidth) && (absDiffX > absDiffY)) {
try {
navigator.goToNextChapter(index);
} catch (Exception e) {
errorMessage(getString(R.string.error_cannotTurnPage));
}
} else if ((diffX < -quarterWidth) && (absDiffX > absDiffY)) {
try {
navigator.goToPrevChapter(index);
} catch (Exception e) {
errorMessage(getString(R.string.error_cannotTurnPage));
}
}
break;
}
}
check if this work - put a if condition checking if single finger touch is there then only do the swipe. change "event" variable to your local variable.
if(event.getPointerCount() == 1)
{
switch (action) {
case (MotionEvent.ACTION_DOWN):
swipeOriginX = event.getX();
swipeOriginY = event.getY();
break;
case (MotionEvent.ACTION_UP):
int quarterWidth = (int) (screenWidth * 0.3);
float diffX = swipeOriginX - event.getX();
float diffY = swipeOriginY - event.getY();
float absDiffX = Math.abs(diffX);
float absDiffY = Math.abs(diffY);
if ((diffX > quarterWidth) && (absDiffX > absDiffY)) {
x = 1;
try {
navigator.goToNextChapter(index);
x = 0;
} catch (Exception e) {
x = 1;
errorMessage(getString(R.string.error_cannotTurnPage));
}
} else if ((diffX < -quarterWidth) && (absDiffX > absDiffY)) {
x = 1;
try {
navigator.goToPrevChapter(index);
x = 0;
} catch (Exception e) {
x = 1;
errorMessage(getString(R.string.error_cannotTurnPage));
}
break;
}
}
}
else if(event.getPointerCount()==2)
{
// code for pinch zoom
}

How does MotionEvent.ACTION_MOVE work inside an onTouchEvent() of a View in Android?

I have implemented the following onTouchEvent() function inside a class extended from View:
public boolean onTouchEvent(MotionEvent event){
// Log.d("MYLOG","TOUCH EVENT OCCURED");
int i, pointToTrack = 0;
boolean trackAPoint = false;
float temp = 0.0f;
float x = event.getX();
float y = event.getY();
// Log.d("MYLOG","TOUCH AT ("+Float.toString(x)+","+Float.toString(y)+")");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Toast.makeText(getApplicationContext(), "ACT_DOWN", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_DOWN");
for(i=0; i<NUM_OF_GRAPH_POINTS; i++){
// if(graphPoints[i][0] == y){
if(Math.abs(graphPoints[i][0] - y)<10.0f ){
pointToTrack = i;
trackAPoint = true;
Log.d("MYLOG", "PRESSED PNT = "+Float.toString(y));
}
}
break;
case MotionEvent.ACTION_MOVE:
// Toast.makeText(getApplicationContext(), "ACT_MOVE", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_MOVE");
/*
if(trackAPoint == true){
graphPoints[pointToTrack][1] = y;
}
*/
temp = temp + 10.0f;
Log.d("MYLOG", "temp="+Float.toString(temp));
receiveGainValues(temp);
break;
case MotionEvent.ACTION_UP:
// Toast.makeText(getApplicationContext(), "ACT_UP", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_UP");
trackAPoint = false;
break;
}
// invalidate();
postInvalidate();
return true;
}
Please look at this section in particular:
case MotionEvent.ACTION_MOVE:
// Toast.makeText(getApplicationContext(), "ACT_MOVE", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_MOVE");
/*
if(trackAPoint == true){
graphPoints[pointToTrack][1] = y;
}
*/
temp = temp + 10.0f;
Log.d("MYLOG", "temp="+Float.toString(temp));
receiveGainValues(temp);
break;
the logcat shows temp = 10 multiple times while I move my finger over the tablet surface. However, since it is detecting the motion, shouldn't temp continue to increase as long as the motion is detected? Why dies it increase once, then stops, and yet the Log.d() below it keeps on getting called?
The tablet is nexus 7 with Android 4.3 and API level 18 if it is relevant
However, since it is detecting the motion, shouldn't temp continue to increase as long as the motion is detected?
Because you reset temp in begin of onTouchEvent:
public boolean onTouchEvent(MotionEvent event){
...
float temp = 0.0f;
Set it global but not in onTouchEvent of method
public class YourClass{
private float temp = 0.0f;
...
}
Well, since you declare the temp variable inside the onTouchEvent() method, it is re-set to 0 for every new MOVE event. Try declaring temp outside of onTouchEvent() and observe the change.

Rotation around 3d object on touch using min3d/Rajawali framework (android)

I am working on an algorithm for rotating the camera around a 3D object using the Min3d/Rajawali framework.
With my implementation, the rotation around axis X is not working properly. I think the method setLookAt() is not working properly.
The problem:
When I rotate the sphere vertically, I can't fully see it. For example, turning the planet Earth, I can not fully see the Antarctic, because the algorithm resets the camera down.
Is it possible to realize the camera rotation around an object without using the method "setLookAt"?
I have tried different solutions, but have not been able to get it working correctly.
Below is my code:
initScene:
scene.camera().position.z = 90;
scene.camera().target = raketeOBJ.position();
onTouchEvent:
public boolean onTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}
if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}
if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
touchTurn = xd / -200f;
touchTurnUp = yd / -200f;
return true;
}
try {
Thread.sleep(15);
} catch (Exception e) {
}
return super.onTouchEvent(me);
}
UpdateScene:
if (touchTurn != 0) {
scene.camera().position.rotateY(touchTurn);
touchTurn = 0;
}
if (touchTurnUp != 0) {
scene.camera().position.rotateX(touchTurnUp);
touchTurnUp = 0;
}
Number3d target = scene.camera.target;
Number3d cp = scene.camera.position.clone();
// move position like target is (0,0,0)
cp.x -= target.x;
cp.y -= target.y;
cp.z -= target.z;
cp.roateX(angle);
// restore offset
cp.x += target.x;
cp.y += target.y;
cp.z += target.z;
scene.camera.position.setAllFrom(cp);
A bit late but in case anyone has trouble with that, like me.
Rajawali offers a camera called ArcballCamera, which does exactly what you/we are trying to do.
In your Renderer add the following:
ArcballCamera arcball = new ArcballCamera(mContext, ((Activity)mContext).findViewById(R.id.contentView));
arcball.setTarget(mObjectGroup); //your 3D Object
arcball.setPosition(0,0,4); //optional
getCurrentScene().replaceAndSwitchCamera(getCurrentCamera(), arcball);
now you can rotate and zoom the object without a dozen lines of code.
setLookAt() needs to be called onDrawFrame if you want the camera to update regularly. But you need to care more about creating a "RotateAroundAnimation". See here for more info: http://www.rozengain.com/blog/2012/03/26/rajawali-tutorial-12-animation-classes/
You'll have to make yourAnimation.setTransformable3D(mCamera), and then it should control your main camera. I often use this methodology and then call the "yourAnimation.start()" on touch, or other external stimulus.
This is my way to rotate 3d model according as x and y
in Render class
public boolean left, right;
public boolean up, down;
and in onDrawFrame
#Override
public void onDrawFrame(GL10 glUnused) {
super.onDrawFrame(glUnused);
// getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
if (left) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() - 1);
}
if (right) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() + 1);
}
if (up) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() - 1);
}
if (down) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
}
}
and in MainActivity
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
xpos = event.getX();
ypos = event.getY();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
mRender.left = false;
mRender.right = false;
mRender.up = false;
mRender.down = false;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
xd = event.getX() - xpos;
yd = event.getY() - ypos;
xpos = event.getX();
ypos = event.getY();
if (xd < 0) {
mRender.up = true;
} else {
mRender.down = true;
}
if (yd < 0) {
mRender.left = true;
} else {
mRender.right = true;
}
}
return super.onTouchEvent(event);
}
by this way, i can rotate my model :)

View not displaying onTouch

I am developing an app in which I would like to display a view when a touch event is registered. The problem is the view will not display and my code become unresponsive.
Here is my view:
public class TouchFX extends View {
RectF oval;
float Tx, Ty;
Paint paint;
int longSide, numRun;
float top, left, right, bottom;
boolean removable;
public TouchFX(Context context) {
super(context);
// TODO Auto-generated constructor stub
Tx = TouchService.x;
Ty = TouchService.y;
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
numRun = 0;
removable = false;
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (canvas.getWidth() > canvas.getHeight())
longSide = canvas.getWidth();
else
longSide = canvas.getHeight();
if (numRun == 0) {
left = Tx - (longSide / 15);
top = Ty + (longSide / 15);
right = Tx + (longSide / 15);
bottom = Ty - (longSide / 15);
} else if (numRun <= 8) {
left += 2;
top += 2;
right += 2;
bottom += 2;
}
oval = new RectF(left, top, right, bottom);
if (numRun < 9)
canvas.drawArc(oval, 0, 360, false, paint);
else
removable = true;
numRun++;
try {
Thread.sleep(400);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Here is my onTouch handler:
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (v == tv && event.getAction() == MotionEvent.ACTION_DOWN
&& isRunning == true) {
x = event.getX();
y = event.getY();
tfx = new TouchFX(this);
winmngr.addView(tfx, lpFX);
//Do some stuff, make some Toasts
while(!tfx.removable) {
//waiting for animation to end
}
winmngr.removeView(tfx);
}
return false;
}
Now, obviously it's getting stuck within the while loop, but why isn't it drawing the arc and updating the boolean?
it is getting stuck because onTouch event operates in UI thread (so as onDraw). So you are blocking your UI thread in while loop and onDraw() doesn't get processor time.
Consider using callback mechanism, so removeView() routines is triggered by your onDraw method. Also sleep(400) in UI thread is extremely bad idea. You can try to use handler and do post delayed execution:
public class YouCustomView extends View
{
Handler handler = new Handler();
#Override
protected onDraw(Canvas canvas){
//your draw routine here
....
//wait 400 ms and remove view
handler.postDelayed(new Runnable()
{
#Override
public void run()
{
//trigger callback to remove view
}
}, 400);
here is my code it completly work for me it will draw a string hope it will help you to solve a problem
#Override
public boolean onTouchEvent(MotionEvent event) {
// draw text at touch
try {
canvas = getHolder().lockCanvas();
synchronized (getHolder()) {
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_MOVE) {
// clear the screen
canvas.drawColor(Color.BLACK);
// draw glyphs
glyphs.drawString(canvas, "Drawing string at "
+ (int) event.getX() + " " + (int) event.getY(),
(int) event.getX(), (int) event.getY());
}
if (event.getAction() == MotionEvent.ACTION_UP) {
canvas.drawColor(Color.BLACK);
glyphs.drawString(canvas, "Drawn string at "
+ (int) event.getX() + " " + (int) event.getY(),
(int) event.getX(), (int) event.getY());
}
}
} finally {
if (canvas != null) {
getHolder().unlockCanvasAndPost(canvas);
}
}
// event was handled
return true;
}

Categories

Resources