I'm developing a game where different bitmap run on the screen (a SurfaceView). Since they have different speed, it happen that sometimes two characters are superimposed, one behind the other. I've used the OnTouch function to catch the moment when the player touch the character. However, when two characters are superimposed, I always "touch" the one that is behind, I never touch the other one in front of him. Why? How I can change this behaviour? Since I'm touching the one in front, it's is really annoying that I always touch the other behind.
I hope the problem is clear.
Thanks!
Edit: This is my OnTouchEvent function:
#Override
public boolean onTouchEvent(MotionEvent event){
synchronized (getHolder()) {
boolean chibi_toccato = false;
if (!blocco_tocco){
// Here I have the structure (a Vector) containing the chibis to be shown
for (int i = (mChibiVector.size() - 1); i >= 0; i--) {
Chibi chibi = mChibiVector.elementAt(i);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (!chibi_toccato){
// touched is a flag that tell if a chibi were touched before
if (chibi.touched) {
// if I touch on the boat bitmap (somewhere on the screen) the chibi will be safe
int navexend = posiz_nave_x + thread.mNave.getWidth();
int naveyend = posiz_nave_y + thread.mNave.getHeight();
if ((int) event.getX() >= posiz_nave_x &&
(int) event.getX() <= navexend &&
(int) event.getY() >= posiz_nave_y &&
(int) event.getY() <= naveyend){
chibi.chibisalvo = true;
thread.calcola_vel_x(chibi);
thread.calcola_vel_y(chibi);
} else {
chibi.touched = false;
int temp_chibi_y = chibi.getCoordinate().getY();
chibi.getCoordinate().setY(
temp_chibi_y +
chibi.getBitmapDimensions()[1]);
}
chibi_toccato = true;
} else {
// Here I check if a chibi is touched:
chibi = thread.checkTouched(chibi, (int) event.getX(),
(int) event.getY());
if (chibi.touched){
// If the chibi is touched, I save the X-Y info:
chibi.getCoordinate().setTouchedX((int) event.getX());
chibi.getCoordinate().setTouchedY((int) event.getY());
int temp_chibi_y = chibi.getCoordinate().getY();
chibi.getCoordinate().setY(
temp_chibi_y -
chibi.getBitmapDimensions()[1]);
chibi_toccato = true;
}
}
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (chibi.touched){
chibi_toccato = false;
int navexend = posiz_nave_x + thread.mNave.getWidth();
int naveyend = posiz_nave_y + thread.mNave.getHeight();
if ((int) event.getX() >= posiz_nave_x &&
(int) event.getX() <= navexend &&
(int) event.getY() >= posiz_nave_y &&
(int) event.getY() <= naveyend){
chibi.chibisalvo = true;
thread.calcola_vel_x(chibi);
thread.calcola_vel_y(chibi);
}
}
}
}
}
}
return true;
}
Well, after a while, finally I got the issue. The problem were in the for cycle:
for (int i = (mChibiVector.size() - 1); i >= 0; i--)
Since I started to pick the frames from the end of the vector, the first touch were always on the image behind the others. I'm still wondering why, but finally I changed the for cycle starting from the first vector item to the end and it worked.
Hope it could be useful for others.
Related
I'm using motion event, action_down, action_move etc, to detect when there is no finger movement but the finger is still on the screen. For instance, the user moves in a vertical line from top of the screen to the bottom and then stops movement without lifting the finger. How do I detect when there is no movement but the finger is still on the screen after the drag/swipe?
EDIT: What I'm trying to do is count every time I change direction of movement in a vertical direction. And to do that I'm trying to detect when I stop movement against the change of movement. For instance, I move down the screen and then move back up, that counts as two counts. Here is my code, please don't provide me with code as a direct answer but hints or clues so that I can try and figure it out myself (my code might look a bit confusing, I'm just trying out different things):
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = event.getX();
oldY = event.getY();
oldSpeedY = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
posY = event.getY();
posSpeedY = System.currentTimeMillis();
float timeElapsed = (posSpeedY - oldSpeedY) / 1000;
float diffSpeed = posSpeedY - oldSpeedY;
if (changeOfMovement(posY, oldY)) {
//if (diffSpeed == 0)
count += 1;
}
break;
case MotionEvent.ACTION_UP:
// count seconds here
break;
}
Toast.makeText(getApplicationContext(), "Swipe: " + count,
Toast.LENGTH_SHORT).show();
return false;
}
public boolean changeOfMovement(float posY, float oldY) {
int newY = Math.round(posY);
double distance = Math.abs(newY - oldY);
oldY = newY;
//float speed = (float) (distance / time);
//if (distance < 25)
//return false;
//if (speed == 0)
//return true;
if (distance < 25)
return true;
return false;
}
The finger touches the screen until you receive either of the MotionEvent actions ACTION_UP or ACTION_CANCEL
The ACTION_DOWN Event is still valid until the finger is lifted from the screen or you can wait till a ACTION_UP Event is detected
I'm not sure if my situation is like yours, but mine I was want to detect if the user stopped moving inside circle within one second and starts moving again, by comparing between last two currentTimeMillis.
So, what I've done is I initialized fixed ArrayList to save the last two times in move event:
public class FixedArrayList extends ArrayList {
int fixedSize = 10;
public FixedArrayList(){}
public FixedArrayList(int fixedSize){
this.fixedSize = fixedSize;
}
#SuppressWarnings("All")
public void addItem(Object object){
if(size() < fixedSize){
add(object);
} else{
remove(0);
add(object);
}
}
}
Now, I've initialized my new class with fixed 2 items to be saved:
FixedArrayList savedLastMove = new FixedArrayList(2);
int secondsToWait = 1;
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_MOVE):
currentSystemTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
savedLastMove.addItem(currentSystemTime);
if(savedLastMove.size() >= 2 && ((long)savedLastMove.get(1) - (long)savedLastMove.get(0)) >= secondsToWait){
//Do what you want after secondsToWait
}
return true;
}
I hope that will help! Because it solved my problem.
This positions are works only 480x800 screens devices. But don't right works 1024x600 device. How can i fix it?
sinif = (ImageView) findViewById(R.id.imageSinif);
sinif.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
Toast.makeText(getApplicationContext(),"x= " +x + " y= " +y, Toast.LENGTH_SHORT).show();
if ((610 < event.getX() && event.getX() < 703)
&& (11 < event.getY() && event.getY() < 128)
&& deger == 0) {
// i want run any method in there. for example:
myAnimation.setVisibility(View.INVISIBLE);
sinif.setEnabled(false);
sinif.setVisibility(View.INVISIBLE);
sorulist.setVisibility(View.VISIBLE);
text1.setOnClickListener(dinle);
text2.setOnClickListener(dinle);
}
return false;
}
});
The problem is you are comparing value of touch X and Y with hard coded values say 601,703,11,128. But as android specification pixel value are device dependent so you need to convert it to device dependent size.
if ((convertSizeToDeviceDependent(610) < event.getX() && event.getX() < convertSizeToDeviceDependent(703))
&& (convertSizeToDeviceDependent(11) < event.getY() && event.getY() < convertSizeToDeviceDependent(128)) && deger == 0) {
// i want run any method in there. for example:
myAnimation.setVisibility(View.INVISIBLE);
sinif.setEnabled(false);
sinif.setVisibility(View.INVISIBLE);
sorulist.setVisibility(View.VISIBLE);
text1.setOnClickListener(dinle);
text2.setOnClickListener(dinle);
}
Put This Method in your Activity
public int convertSizeToDeviceDependent(int value) {
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
return ((dm.densityDpi * value) / 160);
}
I would like to use two ways to change the input of my EditText field.
The EditText field will be only allowing number ("12.34" format).
Normal behavior:
When the user touches the EditText it should show the keyboard and just behave like a normal EditText field
Move behavior
When the user touches and drags up/down I would like the EditText to be incremented/decremented.
I've searched for this type of implementation, because I expect more people have simimular implementeations, but was unable to find it.
Yesterday I tried the following code, but this doesn't work. The keyboard is still showing during draging and using the sleep is also not a very nice implementation.
Hopefully someone here can help me with a better solution.
Thanks in advance!
inputET.setOnTouchListener(otl);
private OnTouchListener otl = new OnTouchListener() {
public boolean onTouch (View v, MotionEvent event) {
if (v == inputET) {
// to distinguish between touch and drag
if (event.getX > 50) { // drag
float oldPos = event.getX;
thread.sleep(10);
inputET.setText(Float.toString(Float.parseFloat(inputET.getText()) - oldPos - event.getX);
return true;
} else { // touch
return false;
}
}
};
float mLastMotionX;
#Override
public boolean onTouch(View v, MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
{
mLastMotionX = x;
break;
}
case MotionEvent.ACTION_MOVE:
{
final int deltaY = (int) (x - mLastMotionX);
if( Math.abs(deltaY) % 50 == 0 && deltaY != 0)
{
mLastMotionX = x;
float current = Float.valueOf(mTextView.getText().toString()) + deltaY;
mTextView.setText("" + round(current, 2.0));
}
break;
}
}
return true;
}
private double round( final float value, double precision )
{
precision = Math.pow(10, precision);
return Math.round(value * precision) / precision;
}
You should set the initial text for your EditText some thing like 10.34.
Your code its ok, just remove the thread's sleep and add this to the activity that is showing the EditText:
android:windowSoftInputMode="stateHidden"
I would also add this to your if condition:
if (event.getAction() == MotionEvent.ACTION_MOVE && event.getX() > 50)
Tested in my device (Android > 4.0) and works, hope it helps you :)
So I have a custom view widget I am allowing a user to drag around by their finger. They are dragging it in a custom scrolling view based on a FrameLayout (code here: http://blog.gorges.us/2010/06/android-two-dimensional-scrollview/) that contains a RelativeLayout, so that they can place it where they want and have plenty of space that is not visible on screen to place the item as well.
What I would like to happen is if the user is moving the widget near the edge of the visible scroll area is to scroll the area in the appropriate direction until they either let go (ACTION_UP, in which case no more events would take place anyway) or have moved away from the edge. The code right now has two problems:
1) My phone (T-Mobile G2) continues to trigger ACTION_MOVE even when my finger is still where as my Nexus 7 tablet does not continue to trigger until it has moved. I need to reliably continue to trigger this event until ACTION_UP.
2) The area scrolls but only as I move my finger towards the edge of the screen. In otherwords, I'd like for a finger that is staying still to continue to scroll and move the object when it is near the edge, but this is not happening.
What am I missing? Here is the onTouchEvent that handles the moving of the widget
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE
&& this.getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
// mScrollParent gets set in a setter and points to the TwoDScrollView
// mOffsetX/Y is based on where the user touches versus the position of the custom view
// mWidth/mHeight gets set once in the GestureDetector based on the custom view's getWidth()/getHeight()
// mTitleBarHeight gets set once in the GestureDetctor onDown event
// lp is a ViewGroup.LayoutParams type
if (!mGestureDetector.onTouchEvent(event)) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
int finalX = (int) (event.getRawX() - mOffsetX);
int finalY = (int) (event.getRawY() - mOffsetY - (mTitleBarHeight > 0 ? mTitleBarHeight * 2
: 0));
if (finalX < 0 || finalY < 0)
return false;
int scrollLeft = (int) Math.ceil(mScrollParent.getScrollX() * 1.05);
int scrollTop = (int) Math.ceil(mScrollParent.getScrollY() * 1.05);
int scrollRight = (int) Math.floor((scrollLeft + mScrollParent.getRight()) * 0.95);
int scrollBottom = (int) Math.floor((scrollTop + mScrollParent.getBottom()) * 0.95);
int scrollX = 0;
int scrollY = 0;
if (scrollLeft > finalX)
scrollX = finalX - scrollLeft;
if (scrollRight < (finalX + mWidth))
scrollX = (finalX + mWidth)- scrollRight;
if (scrollTop > finalY)
scrollY = finalY - scrollTop;
if (scrollBottom < (finalY + mHeight))
scrollY = (finalY + mHeight) - scrollBottom;
if (scrollX != 0 || scrollY != 0)
mScrollParent.scrollBy(scrollX * 4, scrollY * 4);
lp.setMargins(finalX + scrollX, finalY + scrollY, 0, 0);
setLayoutParams(lp);
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
return true;
} else {
return super.onTouchEvent(event);
}
} else {
return true;
}
}
Greets,
Does anyone how I go about detecting when a user presses on a bitmap which is inside a canvas?
Thanks
You should work with something like so:
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
int x = event.getX() // or getRawX();
int y = event.getY();
switch(action){
case MotionEvent.ACTION_DOWN:
if (x >= xOfYourBitmap && x < (xOfYourBitmap + yourBitmap.getWidth())
&& y >= yOfYourBitmap && y < (yOfYourBitmap + yourBitmap.getHeight())) {
//tada, if this is true, you've started your click inside your bitmap
}
break;
}
}
That's a start, but you need to handle case MotionEvent.ACTION_MOVE and case MotionEvent.ACTION_UP to make sure you properly deal with the user input. The onTouchEvent method gets called every time the user: puts a finger down, moves a finger already on the screen or lifts a finger. Each time the event carries the X and Y coordinates of where the finger is. For example if you want to check for someone tapping inside your bitmap, you should do something like set a boolean that the touch started inside the bitmap on ACTION_DOWN, and then check on ACTION_UP that you're still inside the bitmap.
Steve,
Google has a great article and tutorial for handling UI Events # http://developer.android.com/guide/topics/ui/ui-events.html. This is what got me started and it was great for me :-)
This will detect a touch and check if it is not transparent. You need this if your bitmaps are not rectangles. myBitmap is just a simple container class I use.
private boolean clickOnBitmap(MyBitmap myBitmap, MotionEvent event) {
float xEnd = myBitmap.originX() + myBitmap.width();
float yEnd = myBitmap.originY() + myBitmap.height();;
if ((event.getX() >= myBitmap.originX() && event.getX() <= xEnd)
&& (event.getY() >= myBitmap.originY() && event.getY() <= yEnd) ) {
int pixX = (int) (event.getX() - myBitmap.originX());
int pixY = (int) (event.getY() - myBitmap.originY());
if (!(myBitmap.getBitmap().getPixel(pixX, pixY) == 0)) {
return true;
} else {
return false;
}
}
return false;
}
This is the on touch code for completeness
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (clickOnBitmap(dog, event)) {
Toast.makeText(getContext(), "dog", Toast.LENGTH_SHORT).show();
} else if (clickOnBitmap(mouse,event)) {
Toast.makeText(getContext(), "mouse", Toast.LENGTH_SHORT).show();
}
return true;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
return true;
}
return false;
}