Mapping Xamarin (Android) touch event coords to webview pixels - android

I have a webview in an android xamarin application. When a user swipes the screen left or right I need to check if the swipe happened over a specific element. To handle the swipe logic I am using this code:
float startY = 0;
private void BindTouchEvents() {
float startX = 0;
float webViewWidth = 0;
webView.Touch += (sender, e) => {
if (e.Event.Action == Android.Views.MotionEventActions.Down)
{
webViewWidth = webView.Width;
startX = e.Event.GetX();
startY = e.Event.RawY; //I've tried both GetY and RawY here
}
if (e.Event.Action == Android.Views.MotionEventActions.Up)
{
float movement = e.Event.GetX() - startX;
float offset = webViewWidth / 2;
if (Math.Abs(movement) > offset)
{
if (movement < 0)
{
client.NotifyTouch(webView, e.Event.GetX(), startY, "SwipeLeft");
}
else
{
client.NotifyTouch(webView, e.Event.GetX(), startY, "SwipeRight");
}
}
}
e.Handled = false;
};
}
The client.NotifyTouch call invokes some javascript in the webview which determines if the swipe event occured over a specific element. If it did, it toggles that elements visibility to hidden. The element is in a fixed position at 0,0 and is 64px in height, so this logic is quite simple:
$scope.$on('received-touch-event', function (e, args) {
if ($scope.showLaunchNavBar && (args.event === "SwipeLeft" || args.event === "SwipeRight") && args.y > 0 && args.y < 64) {
$scope.showLaunchNav(false);
};
});
The problem is that the startY value doesn't seem to translate evenly to webview pixels. For example, if I try to swipe roughly at 0,0 and inspect the starting Y position it's above 100, if I try to swipe roughly at 0,64, the starting Y position ends up being above 400. I need to figure how to translate these values into WebView pixels. I'm assuming it has something to do with the pixel density on my device, but I'm not sure how to go about translating the values into "web-pixels" in a generic way that would work across all devices.
I've tried googling, reading docs and searching on here for an answer but can't seem to find what I'm looking for. Probably not using right terminology. Any help is appreciated.

I believe I figured out, here's the solution:
public static float ConvertDpToPixel(float dp, Context context)
{
return dp / ((float)context.Resources.DisplayMetrics.DensityDpi / (float)DisplayMetricsDensity.Default);
}

Related

creating interactive animation with libgdx

I am looking for a way to connect pan gesture with percentage of animation completion. Let me show you what I mean.
This image represents an animation that I want to execute, namely a moving Image actor or a sprite. The animation gets executed by pan gesture. Animation is 100% complete and at stage 6 when user slides for a 200px. If user slided only 100px, it would be 50% complete and at stage 3. If the user didn't execute pan gesture the animation stays at 0% and at stage 1. I am looking for tips on how to start building such a model. I believe it is called interactive. Do you have any suggestions?
You can use a GestureDetector to handle the panning input. The gestureListener.pan method can update an animation position parameter.
private int screenPixelsToAnimationPositionRatio = 0.01f; //will need to tweak this and probably adjust it at runtime based on screen dimensions and resolution
private float animationPosition = 0; //from 0 to 1, fraction of animation complete
public void create () {
//...
GestureAdapter gestureAdapter = new GestureAdapter {
#Override
public boolean pan (float x, float y, float deltaX, float deltaY) {
animationPosition += deltaX * screenPixelsToAnimationPositionRatio;
animationPosition = MathUtils.clamp(animationPosition, 0, 1);
return true;
}
};
GestureDetector gestureDetector = new GestureDetector(gestureAdapter);
Gdx.input.setInputProcessor(gestureDetector); //or put the detector in an InputMultiplexer with your other input listeners.
}
Then you would create a method that can update your object's position and rotation based on the current value of animationPosition. You would need to figure out the equations that determine the movement you want. For example, something that looks sort of like what you illustrated above:
private void updateAnimation (){
x = animationPosition * 30;
float y = 0, rotation = 0;
if (animationPosition >= 0.25f) {
float jumpPosition = Math.min(1, (animationPosition - 0.25f) / 0.5f);
y = 30 * (1 - Interpolation.pow2In.apply(Math.abs(2 * jumpPosition - 1)));
rotation = 180 * jumpPosition;
}
mySprite.setPosition(x, y);
mySprite.setRotation(rotation);
}
Then call this update method somewhere in render.

How to listen to touch/click event on any object which is being animated by ValueAnimator

I am going through "BouncingBalls" api guide provided by google. I wonder whether there is any way to listen to touch or click event to the ball object created dynamically. Actually I am trying to use ValueAnimator class for a simple game which requires user to touch the object. There are many objects of diffrent types ( for example balls of different radii ) and these objects can either be static images from drawable or can be drawn at run time.
Is there any way to detect the touch to the balls ?
Here is my sample ValueAnimator object
ValueAnimator simpleAnimation = ObjectAnimator.ofFloat(myObject,"y",startY,endY)
simpleAnimation.setDuration(something)
simpleAnimation.setInterpolator(something)
ValueAnimator startAnim = new AnimatorSet()
startAnim.play(simpleAnimation);
Now, I want to do some stuff when user touches myObject.Till now , All I know is this
simpleAnimation.addListener()
but none of the paramaters suffices to touch event handling.
You can extend ShapeHolder, add a method to check whether touch point is inside of the ShapeHolder.
class MyShapeHolder extends ShapeHolder {
public MyShapeHolder(ShapeDrawable s) {
super(s);
// TODO Auto-generated constructor stub
}
private boolean isTouchInside(final float x, final float y) {
if (x < getX() + getWidth() / 2 &&
x > getX() - getWidth() / 2 &&
y < getY() + getHeight() / 2 &&
y > getY() - getHeight() / 2) {
return true;
}
return false;
}
}
Then in onTouchEvent, add the following code
for (int i = 0; i < balls.size(); ++i) {
MyShapeHolder shapeHolder = balls.get(i);
if (shapeHolder.isTouchInside(event.getX(), event.getY()))
Log.d("TEST", "TOUCH ON BALL " + i);
}
}
Add you can also create a listener for ShapeHolder, like OnShapeTouchListener. It will be a good practice for you. :)

Calculating a BoundingBox from a set of points

I really cant find any resource to know how to compute the bounding box of a set of points.
I have a float/int array of points for which I wish to compute the bounding box ( I want to know all the four corners of the rectangle of the bounding box ). How do I accomplish this?
You could loop through the array:
int minX = Integer.MAX_VALUE, minY, maxX, maxY = Integer.MAX_VALUE;
for (int i=0;i<myArray.length;i++){
if (myArray[i].x > maxX){
maxX = myArray[i].x;
} else if (myArray[i].x < minX) {
minX = myArray[i].x;
} else if (myArray[i].y > maxY){
maxY = myArray[i].y;
} else (myArray[i].y < minY) {
minY = myArray[i].y;(
}
}
You didn't say what kind of list you are using (array of points or whatever) so you'll need to adjust myArray[i].y and maxY = Integer.MAX_VALUE as required.
Computing AABB (Axis aligned bounding box) is fairly trivial. Just sort the points in each axis, find the min max on each axis. The intersection of the 4 lines from these points is your AAB rectangle.
Computing OBB (oriented bounding box) is slightly non trivial. Luckily there is a method on GestureUtils that does exactly that, namely :
GestureUtils.computeOrientedBoundingBox(float[] points)
pass it your float array of points and life is good :)
Although you didn't really specify what sort of points you were referring to, this bit of code should still work. I use it for creating a bounding box around the vertices of a model. It should also be noted that rotation should happen separate to loading the vertices. I'm actually fairly certain you can't detect rotation unless it's explicitly stated, or serialized with the vertex data. Also, AABB's and OBB's are technically—from a mathematical perspective—the same thing, as I proved here: https://stackoverflow.com/a/63094985/3214889. So even though your question specifically states Oriented Bounding Box, the following code will work for both. However, you will need to rotate the box afterwards; unless you serialize the rotation somehow.
public void FromVertices(Vertex[] vertices)
{
// Calculate Bounding Box
float minX = float.PositiveInfinity;
float maxX = float.NegativeInfinity;
float minY = float.PositiveInfinity;
float maxY = float.NegativeInfinity;
float minZ = float.PositiveInfinity;
float maxZ = float.NegativeInfinity;
for (int i = 0; i < vertices.Length; i++)
{
Vector3 vertex = vertices[i].Location;
// Check for maximum
if (vertex.X > maxX)
{
maxX = vertex.X;
}
if (vertex.Y > maxY)
{
maxY = vertex.Y;
}
if (vertex.Z > maxZ)
{
maxZ = vertex.Z;
}
// Check for Minimum
if (vertex.X < minX)
{
minX = vertex.X;
}
if (vertex.Y < minY)
{
minY = vertex.Y;
}
if (vertex.Z < minZ)
{
minZ = vertex.Z;
}
}
this.Minimum = new Vector3(minX, minY, minZ);
this.Maximum = new Vector3(maxX, maxY, maxZ);
}

Getting coordinates on touch event relative to scrollable map

Is there a way to get the coordinates of a touch event on a scrollable map?
Meaning when I touch at one point on the screen, getX() and getY() return the respective values and then when I scroll the map, the coordinates returned should be relative to the map, not the screen.
eg. I have a 750x750 background map, and my device screen size is 480x800.
when I first touch, say the coordinates returned are (100, 200). now when I scroll the map and touch somewhere else, I get the coordinates as 200, 200.
I want to get the coordinates with respect to the map and not the screen.
I've been trying to figure this out for a long time and have scoured the net and other sites in vain.
please help.
thanks in advance
\m/
i need the coordinates coz i'm developing a game in which i have a large map and objects placed on it. when i scroll the map, i need the objects to move along with in the same position.
here is my code:
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart.set(ev.getX(), ev.getY());
x = ev.getX();
y = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
newX = ev.getX() - touchStart.x + prevPicStart.x;
newY = ev.getY() - touchStart.y + prevPicStart.y;
if ((newX <= 0 && newX > 0 - mBG.getWidth() + Craz.DISP_WIDTH)) {
picStart.x = newX;
}
if ((newY <= 0 && newY > 0 - mBG.getHeight() + Craz.DISP_HEIGHT)) {
picStart.y = newY;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
prevPicStart.x = picStart.x;
prevPicStart.y = picStart.y;
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
canvas.drawBitmap(mBG, picStart.x, picStart.y, paint);
canvas.translate(picStart.x, picStart.y);
mBDoor.draw(canvas);
}
Its pretty easy. When you scroll your map, you have an offset. If you scroll to the right (the background will move to the left), your offset will be negative. Lets say, you have an offset on x of -50, and you click the screen coordinates 100 you simply do the math:
mapCoordX = screenX - offsetX; // makes: 100 - (-50) = 150
just looking at the X coordinate, for Y it should be the same.
I have written a tutorial about a map of tiles and scrolling over it. Maybe you take a look at it, too.
Pseudocode!
for (int id = 0; id < mapSize; id++) {
Tile tile = new Tile(id);
startX = id * tileWidth;
startY = id % rowLenght + id / rowLenght;
tile.setBackground(createCroppedBitmap(background, startX, startY));
}

Android Animation Question

I am trying to write the logic for an animation sequence and can't seem to get the thing right. What I want to happen is: if the user clicks on the screen, the method takes in the touchEvent coordinates, and then changes the movement variables of a sprite so that the sprite travels to where the user touched the screen. I have my "launch" event setup like this.
public void launch(float eventX, float eventY) {
//get the touch event coords and then move the banana to them.
fire = true;
//the x and y variables for the sprite
getX();
getY();
//the target x and y variables
targetX = eventX;
targetY = eventY;
//the total distance the two variable have to "travel"
distanceX = x - targetX;
distanceY = y - targetY;
//variables to update the movement
moveX = distanceX;
moveY = distanceY;
}
Then, I thought I was supposed to put the movement variables in the update method like this:
public void update(long gameTime) {
if(gameTime > frameTicker + framePeriod) {
frameTicker = gameTime;
currentFrame++;
if(currentFrame >= frameNbr){
currentFrame = 0;
}
}
this.sourceRect.left = currentFrame * spriteWidth;
this.sourceRect.right = this.sourceRect.left + spriteWidth;
if(fire == true){
x = (int) moveX;
y = (int) moveY;
}
If the user clicks as it is, the animation shows up like it's supposed to, but then instantaneously goes to the top left corner of the screen or what I have come to understand is (0,0) on a coordinate system. I can't figure out how to slow it down so that it moves at a reasonable space and goes where it is supposed to.
You could put the whole animation in your launch() function if you want.
For instance, at the end of the function something like:
float incrementX = distanceX / 100;
float incrementY = distanceY / 100;
float spriteX = getX();
float spriteY = getY();
bool xDone = false;
bool yDone = false;
while(!(xDone && yDone)) {
if (distanceX <= spriteX) {
spriteX += incrementX; // update the sprite's x coordinate as well
}
if (distanceY <= spriteY) {
spriteY += incrementY; // update the sprite's y coordinate as well
}
try{ Thread.sleep(10) } catch(Exception e) {}
}
That code relies on the sprite starting at a lower x and y than the event; if that's not the case it needs to be modified.

Categories

Resources