I have a drag and drop list implementation in Xamarin. The problem is that I want to be able to drag and drop element only when the drag and drop button is touched not the whole element.
I was trying to detect for the ImageView being pressed but I am unable to get the correct hit ImageView.
public bool OnDown (MotionEvent e) {
int x = (int) e.GetX();
int y = (int) e.GetY();
int itemnum = PointToPosition(x, y);
if (itemnum == AdapterView.InvalidPosition) {
return false;
}
if (m_dragging != null) {
m_dragging.Stop();
m_dragging = null;
}
View item = GetChildAt(itemnum - FirstVisiblePosition);
item.Pressed = false;
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
Rect bounds = new Rect();
dragImage.GetDrawingRect (bounds);
if (!bounds.Contains (x, y)) {
return false;
}
The following code works only for the first element in the list and does not apply to any other. I am suspecting that the detection of hit is incorrect.
Solved followingly. I realized that I actually do not need to care about the Y axis and it's enough to take just X axis in account.
// Detect if the user is actually pressing the drag and drop image or not.
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
if (dragImage != null) {
var dragImageHitRect = new Rect ();
dragImage.GetDrawingRect (dragImageHitRect);
// In this case we do not care about the Y axis. Just compare the x axis.
if (!dragImageHitRect.Contains (x, (int)dragImage.GetY ())) {
return false;
}
}
Related
There's someone here can help me. Im new to unity. I was working on making objects moves only on x-axis / y-axis when the object touch and drag to the left the object moves to left and stops when the touch or removes on the object.
using UnityEngine;
using System.Collections;
public class MoveScriptVertical : MonoBehaviour
{
//touch offset allows ball not to shake when it start moving
float deltax,deltay;
Rigidbody2D rb;
bool moveAllowed = false;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
}
void Update ()
{
//touch event
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch (0);
//knowing the touch position
Vector2 touchPos = Camera.main.ScreenToWorldPoint(touch.position);
//touch phase
switch (touch.phase)
{
case TouchPhase.Began:
//if you touch the car
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos))
{
//get the offset between position you touch
deltax = touchPos.x - transform.position.x;
deltay = touchPos.y - transform.position.y;
//if touch began within the car collider
//then it is allowed to move
moveAllowed = true;
//restriction some rigidbody properties
rb.freezeRotation = true;
rb.velocity = new Vector2 (0, 0);
GetComponent<BoxCollider2D> ().sharedMaterial = null;
}
break;
//you move your finger
case TouchPhase.Moved:
//if you touches the car and move is allowed
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos) && moveAllowed)
rb.MovePosition (new Vector2 (0, touchPos.y - deltay));
break;
//you released your finger
case TouchPhase.Ended:
//restore intial parameters
//when touch is ended
moveAllowed = false;
rb.freezeRotation = true;
rb.gravityScale = 0;
PhysicsMaterial2D mat = new PhysicsMaterial2D ();
GetComponent<BoxCollider2D> ().sharedMaterial = mat;
break;
}
}
}
}
There are some libraries that allow you to have complex finger events, for instance Touchscript. If you choose this solution you will find tutorials online.
The most basic way is to use unity's Input class with a nice doc here: https://docs.unity3d.com/ScriptReference/Input.html
This allow you to know when a finger touched the screen or moved on the screen. It will give you x an y coordinate so if you just want to move along x you would use only the x value, and, for example translate your object with it using
transform.Translate(new Vector3(x, 0f, 0f));
Edit
Since you use touchscript, have a look at TransformGesture, it should allow you to handle many transform events. If you want to have more control, you can use PressGesture, PanGesture and ReleaseGesture instead
How to show a response while gesture input is in progress?
I'm using swipe down gesture to reload my WebView.
Using this for gesture detection:How to detect swipe direction between left/right and up/down
Problem
SimpleGestureListener captures the result of user input only. It cannot be used to show a response, e.g. animation, while the user is performing a gesture.
Imperfect inelegant solution:
flaw: Shows animation independent of SimpleOnGestureListener; a response to user gesture input is displayed and gesture input may still fail or vice versa.
private volatile float userTouchY = -1;
// translating WebView every onTouch event call is inefficient.
// only translate when translateYBy value is greater than a threshold.
private float translateYBy = 0;
/**
* Shift webView without animation.
* #param num
*/
private void __shiftWebViewDownBy(int num){
float current = webView.getY();
if(current >= webviewTranslationLimit) return;
current += num;
if(current> webviewTranslationLimit) current = webviewTranslationLimit;
webView.setY(current);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
//Skip everything unless at the content top.
if(webView.getScrollY() > 0) return false;
int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE){
float y = event.getY();
float dif = y - userTouchY;
if(dif < webviewTranslationLimit/10){
//dif less than screenHeight*1/40, ignore input
}else if(dif < -(webviewTranslationLimit/5)){
//swipe up
userTouchY = -1;//not swipe down cancelling
__shiftWebViewTopTo(0);
}else if(userTouchY < y) {
//swipe down
translateYBy += dif;
if(userTouchY == -1){
//userTouchY at the initial value, ignore for this once.
userTouchY = y;
}else{
userTouchY = y;
if(translateYBy > 5 ){
__shiftWebViewDownBy((int)translateYBy);
translateYBy = 0;
}
}
}
}else{
Log.d(TAG,"action not move:" +MotionEvent.actionToString(action));
// Webview shift down should only occur while moving
// Once finger is off,cancel
__shiftWebViewTopTo(0);
userTouchY = -1;
}
boolean result = gestureDetector.onTouchEvent(event);
return result;
}
Writing a GestureListener of a sort from scratch obviously solves the problem and I can simply use Toast to message the user for a failed gesture input, but there ought to be an easier, more readable solution.
I'm trying to make an Android puzzle game out of a picture.
What i've done 'till now is:
-i've cut the picture in how many parts i get from the EditText field;
-i've put the cut pictures in a gridview and got a puzzle table;
-i've also used the Collection.suffle(list) to suffle the images in the gridview;
Now, i am trying to implement the sliding effect on two images from gridview. So what i want to achieve is a sliding effect similar to CandyCrush app or this one http://www.youtube.com/watch?v=qUoP87fNB8w . I mean, i want to hold an image and drag it top so it should slide top. If i click the picture and slide bottom, it should change with the picture below it and so on...
Here is my code:
Reset = (Button) findViewById(R.id.BReset);
SplitNo = (EditText) findViewById(R.id.edSplitBy);
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
puzzle = (GridView) findViewById(R.id.puzzle);
rootImg = (ImageView) findViewById(R.id.source_image);
splitImage(rootImg);
formPuzzle(smallimages);
Reset.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Reset.setText("Reset");
SplitNo.getEditableText().toString();
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
Collections.shuffle(smallimages);
formPuzzle(smallimages);
}
});
And here is how i form the puzzle:
private void formPuzzle(ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0); }
PuzzleItem.class:
public class PuzzleItem {
Bitmap puzzlepartImage;
int correctPosition;
public PuzzleItem(Bitmap d, int index) {
puzzlepartImage = d;
correctPosition = index;
}
I used this class just to remember the position of the pieces so i can check if the puzzle is finished...
So i'm only interested in how to implement the sliding motion by draging an item from gridview...
Thanks :)
EDIT: Should i use an OnTouchListener ?
Ok, I've managed to find the solution by myself :
private void formPuzzle(final ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0);
puzzle.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startX = event.getX();
startY = event.getY();
smallimage_Position = puzzle.pointToPosition((int) startX,
(int) startY);
return true;
}
case MotionEvent.ACTION_MOVE: {
endX = event.getX();
endY = event.getY();
Log.d("X", "..." +endX);
Log.d("Y", "..." +endY);
if(endX > startX + smallimage_Width/2 && (endY < startY - smallimage_Height/2 || endY < startY + smallimage_Height/2) ){
PuzzleItem p1 = SMI.smallimages.get(smallimage_Position);
PuzzleItem p2 = SMI.smallimages.get(smallimage_Position +1);
SMI.smallimages.set(smallimage_Position, p2);
SMI.smallimages.set(smallimage_Position +1, p1);
SMI.notifyDataSetChanged();
startX = endX*200;
startY = endY*200;
}
return true;
}
}
return true;
}
});
The slide is given by the MotionEvent.Action_MOVE and by calculating the pixels you can find out in what direction is the finger moving ... For example .. the if from my Action_Move case is recognising a swipe to the right ...
I hope this will help some of you guys ...
Another way to go about achieving slides in a slider puzzle game is to use a game engine. I recommend Cocos2D for Android, and theres a detailed tutorial on that here ...
http://denvycom.com/blog/step-by-step-guide-on-how-to-build-your-first-slider-puzzle-game-in-cocos2d-for-android-part-1/
I am working on a multitouch program that needs to record only the movements made by the second finger or index pointer.
Now the documentation says that we can use MotionEvent.ACTION_POINTER_INDEX_MASK and & it with action and shift by INDEX_SHIFT to get the pointer that made the action like going up or down. But this technique does not work on move.
Is there anyway that we can detect the move action made by a certain pointer alone?
Thx,
yes, you can have something like this in your View class:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
if(event.getPointerCount()>1){
//where 1 is the index of the second finger
final int Y = event.getY(1);
final int X = event.getX(1);
}
break;
}
}
so depending on what finger you want to get the movent you can set the get to that index. Rember that values may be from 0 (the first pointer that is down) to getPointerCount()-1.
I tested this on 2.2 Gingerbread so I hope it be useful for you :)
You can get the effective pointer index by checking which pointer changed:
private final int MAX_POINTER = 5; // 5 different touch pointers supported on most devices
private float mLastTouchPositionX[];
private float mLastTouchPositionY[];
#Override
public boolean onTouchEvent(MotionEvent aEvent)
int tActionIndex = aEvent.getActionIndex();
int tPointerCount = aEvent.getPointerCount();
/*
* Check which pointer changed on move
*/
if (tMaskedAction == MotionEvent.ACTION_MOVE) {
for (int i = 0; i < tPointerCount && i < MAX_POINTER; i++) {
if (mLastTouchPositionX[i] != aEvent.getX(i) || mLastTouchPositionY[i] != aEvent.getY(i)) {
mLastTouchPositionX[i] = aEvent.getX(i);
mLastTouchPositionY[i] = aEvent.getY(i);
// Found new action index
tActionIndex = i;
break;
}
}
}
...
}
Can anyone help please.
Im writing a small Android game where the player is able to select a "barrier" and drag it accross the screen with their finger. I have the barriers drawn on the screen and I am able to drag it accross the screen.
My problem however is when I add more than 1 barrier, eg 3 barriers, and drag a barrier accross the screen, they all drag and they all drag to the same position. That is to say they all lie on top of each other, making it look as though there is only 1 barrier.
Here is my code, can anyone please tell me where I am going wrong/explain where I am going wrong.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener {
// Initialising the Barrier
private Barrier barrier[] = new Barrier[3];
// The Main Game Panel
public MainGamePanel(Context context) {
super(context);
// Adding a call-back (this) to the surfaceHolder to intercept events
getHolder().addCallback(this);
// Creating the Game items
// The starting coordinates of the Barrier
int x = 30;
int y = 270;
barrier[0] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.blue_barrier), x, y);
barrier[1] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.green_barrier), x + 15, y);
barrier[2] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.pink_barrier), x + 30, y);
// Create the Game Loop Thread
thread = new MainThread(getHolder(), this);
// Make the GamePanel focusable so it can handle events
setFocusable(true);
}
// Handles the touch events
public boolean onTouchEvent(MotionEvent event)
{
int eventAction = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (eventAction)
{
// Touch down so check if finger is on Barrier
case MotionEvent.ACTION_DOWN:
if (x > barrier[0].getX() && x < barrier[0].getX() + 8
&& y > barrier[0].getX() && y < barrier[0].getY() + 8)
{
barrier[0].isTouched();
}
else if (x > barrier[1].getX() && x < barrier[1].getX() + 8
&& y > barrier[1].getX() && y < barrier[1].getY() + 8)
{
barrier[1].isTouched();
}
else if (x > barrier[2].getX() && x < barrier[2].getX() + 8
&& y > barrier[2].getX() && y < barrier[2].getY() + 8)
{
barrier[2].isTouched();
}
break;
// Touch-drag with the Barrier
case MotionEvent.ACTION_MOVE:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i] == barrier[0])
{
barrier[0].setX(x);
barrier[0].setY(y);
} // end if
else if (barrier[i] == barrier[1])
{
barrier[1].setX(x);
barrier[1].setY(y);
}
else if (barrier[i] == barrier[2])
{
barrier[2].setX(x);
barrier[2].setY(y);
} // end else if
} // end for
break;
case MotionEvent.ACTION_UP:
// Finger no longer on Barrier - Do Nothing
break;
}
return true;
}
// Render - Draws the Game Item Bitmaps to the screen
public void render(Canvas canvas)
{
// Set the background to white
canvas.drawColor(Color.WHITE);
barrier[0].draw(canvas);
barrier[1].draw(canvas);
barrier[2].draw(canvas);
}
// Update
// This is the Game's update method
// It iterates through all the Objects and calls their update() methods (if they have one)
public void update()
{
} // end update
In your barrier move code, you aren't checking a particular barrier is touched, so you're moving all of them to the same coordinates. The following loop does exactly the same thing as your current code:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
barrier[i].setX(x);
barrier[i].setY(y);
} //end for
To fix this, you need to check if the current barrier in the loop is the one that was touched, and can change that whole loop to something like:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i].isTouched())
{
barrier[i].setX(x);
barrier[i].setY(y);
} // end if
} // end for
You would then need to make sure that you un-set the touched property in the ACTION_UP section. If you post your Barrier class definition I can probably help more.
I'm pretty sure your problem could be solved by making the barriers three separate objects instead of in an array.
As already pointed out, your code in handling the move is wrong because it moves all the Barriers to the same X,Y when it should only move the one that is touched.
Also, you are never resetting the isTouched on the objects in the Action up. When the user lifts their finger you should set them all to isTouched == false. If you don't do that then once you touch one it will always move to the X,Y.