I have created multiple views dynamically , in my case textViews. i have given them ids. what iwant is to select all the textViews ids over which i slide my finger. E.g if there are textviews saying A And B and when i slide finger from A to b i should get ids save in my array. Currently I am learning touch. I don't know how to implement it.
Best Regards
OnTouchListener MyOnTouchListener = new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event)
{
Drawable d = getResources().getDrawable(R.drawable.small_corners);
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
vibe.vibrate(20);
id.add(v.getId());
TextView vv = (TextView) v;
val.add(vv.getText().toString());
vv.setBackgroundDrawable(d);
String str = "";
//A pressed gesture has started, the motion contains the initial starting location.
return false;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
int check = 0;
for (int i = 0; i < id.size(); i ++)
{
if (id.get(i) == v.getId())
check = 1;
}
if (check != 1)
{
id.add(v.getId());
vv = (TextView) v;
val.add(vv.getText().toString());
vv.setBackgroundDrawable(d);
}
return false;
case MotionEvent.ACTION_MASK:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
break;
case MotionEvent.ACTION_OUTSIDE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
break;
}
return false;
}
};
Did you tried ACTION_HOVER_ENTER and ACTION_HOVER_EXIT?
It might do the trick. See example in documentation of onGenericMotionEvent.
From what I have understood, try this; Implement onTouchlistner and set this listener for all of your TextViews, in onTouch() do this:
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_MOVE){
//SAVE YOUR VIEWS ID//
someArray[index] = ((TextView)v).getId())
return true;
}
return false;
}
Try different ACTIONS of MotionEvent
Related
I am making a single screen application of a simple logic circuit. I am using onTouchEvent to handle user interactions. I am using ACTION_UP, ACTION_MOVE, & ACTION_DOWN right now, but this only allows me to use one gesture. I want to be able to selection an option in my UI, such as "AND" gate. I want to use one touch to select what I want to do, then the next touch to place the gate on the screen. Instead, my onTouchEvent only allows me to touch the component I want and I have to keep my finger on the screen to drag it to the location I want to place it. This is not want I want.
I've tried researching how to implement some sort of state variable to allow onTouchEvent to wait for the next touch, but I don't think I correctly understand how to implement it.
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
Log.d("Debugging", "In onTouchEvent");
if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
Touch.horizontalTouched = (int)motionEvent.getX()/ grid.getBlockSize();
Touch.verticalTouched = (int)motionEvent.getY()/ grid.getBlockSize();
whatWasTouched = whatWasTouched(Touch.horizontalTouched, Touch.verticalTouched);
}else if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE){
//do nothing, finger is moving on screen
}
else if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP){
Touch.secondHorizontalTouch = (int)motionEvent.getX()/ grid.getBlockSize();
Touch.secondVerticalTouch = (int)motionEvent.getY()/ grid.getBlockSize();
placeComponent();
draw();
}
return true;
}
I expect my first touch on the screen to be able to select an option, ex: "AND", "OR", "NOT", "SWITCH", "EDIT", etc. and then my second touch completes the desired action. I also want to be able to touch a component I placed on the screen and then touch another component so I can wire them together.
By default, event listeners in Android are for waiting - you don't have to provide any delay.
Simply set the onTouchEvent(...) listener on the ImageView and show the first bitmap. When the ImageView is touched, show the next bitmap and so on. All you have to do is keep a count of how many touches there have been in order to know which image to show (image 1, 2, 3, 4 etc).
Example...
public class LoadImage extends Activity {
int imageNumber = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_image);
//get an image and create a bitmap from it
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(bitmap);
}
#Override
public boolean onTouchEvent(MotionEvent evt) {
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
imageNumber++;
switch (imageNumber) {
case 2:
// show image 2
break;
case 3:
// show image 3
break;
...
}
return true;
}
return false;
}
}
I was able to figure this out by using a static class that will handle the state of my touches.
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
Log.d("Debugging", "In onTouchEvent");
if (placeState.getState() == false) {
if ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
Touch.horizontalTouched = (int) motionEvent.getX() / grid.getBlockSize();
Touch.verticalTouched = (int) motionEvent.getY() / grid.getBlockSize();
whatWasTouched = whatWasTouched(Touch.horizontalTouched, Touch.verticalTouched);
if(whatWasTouched.equals("DELETE")){
visualComponents.clear();
logicalComponents.clear();
}
placeState.toggleState();//sets to true
draw();
}
}else if (placeState.getState() == true) {
Touch.secondHorizontalTouch = (int) motionEvent.getX() / grid.getBlockSize();
Touch.secondVerticalTouch = (int) motionEvent.getY() / grid.getBlockSize();
placeComponent();
placeState.toggleState();//sets to false
draw();
}
return true;
}
Solved it by using emandt's suggestion. My personal Solution added below.
I'm using Android Studio for this.
I searched for solutions but couldn't find anything resembling this.
I want to know on which ImageView an UP action occurs while starting the DOWN action on a different ImageView (to eventually be able to drag one image over the other and make it snap to the same position by getting the position of the image I dragged over).
My example has two ImageViews with the id imageView (left) and imageView2(right).
In my example I'm not dragging anything yet, I just want to touch the left image, see "Action was down" in the log and lift the finger over the right image showing "Action was up2".
I don't know if this is easily possible.
As far as I can tell from testing, the MotionEvent.ACTION_UP only fires for an ImageView when you also pressed down on it beforehand. So when I release on top of imageView2 it only shows "Action was up" from the left image.
I wondered if it was possible by playing with return false, since the return value tells if an ActionEvent is consumed so I thought if the UP event of imageView returns false, maybe it does trigger the UP event of imageView2 but no. (Either complete misunderstanding on my part or it doesn't recognise UP on the second because it didn't start with a DOWN and MotionEvents probably always have to start with a DOWN).
public class MainActivity extends Activity {
ImageView imageView;
ImageView imageView2;
String DEBUG_TAG = "action";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
imageView2 = findViewById(R.id.imageView2);
imageView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
//int action = MotionEventCompat.getActionMasked(event);
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN"+v.toString());
return true;
case (MotionEvent.ACTION_MOVE) :
//Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP"+v.toString());
return false;
default :
//return true;
}
return true;
}
});
imageView2.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
//int action = MotionEventCompat.getActionMasked(event);
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN2"+v.toString());
return true;
case (MotionEvent.ACTION_MOVE) :
//Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP2"+v.toString());
return true;
default :
//return true;
}
return true;
}
});
}
}
If there is no simple way to do this, I'm thinking about solving this mathematically, but maybe some of you can help.
So my question is, is there a way to recognise an UP action on a second ImageView while currently being in a MotionEvent of another ImageView?
SOLUTION (see emandt's answer)
I ditched the second OnClickListener because I realised that the 2nd image doesn't need any, I just need its position.
Added this method:
#Nullable
private View getDroppedView(View droppedView, int x, int y, List<View> arrayOfPossibilities) {
Rect cVisibleBoundsRect = new Rect();
for (View cView : arrayOfPossibilities) {
//if currently iterated view doesn't have values for getGlobalVisibleRect, skip the .contains part
//ignore the item which is your current active item (which would potentially be dropped)
//getGlobalVisibleRect sets cVisibleBoundsRect immediately to the Rect given as parameter
if (!cView.getGlobalVisibleRect(cVisibleBoundsRect)||(cView.equals(droppedView))) continue;
if (cVisibleBoundsRect.contains(x, y)) {
Log.d(DEBUG_TAG,"Found something");
//THIS "cView" IS THE VIEW WHERE YOU RELEASED THE FINGER
return cView;
}
}
Log.d(DEBUG_TAG,"Found nothing");
return null;
}
And added this in onUP:
case (MotionEvent.ACTION_UP) :
View dropTarget;
Log.d(DEBUG_TAG,"Action was UP"+v.toString());
dropTarget = getDroppedView(v, (int)event.getRawX(), (int)event.getRawY(), listOfViews);
if (dropTarget != null){
v.setX(dropTarget.getX());
v.setY(dropTarget.getY());
}
I think you want to know which is the View where you release the finger from the screen, am I right?
To do this you can use the same "View.OnTouchListener()" for all of your Views and in the ACTION_UP you have to call a new method similar to this (pseudo-code):
....
case (MotionEvent.ACTION_UP) :
View[] cArrayOfPossibileViews = new View[]{ findViewById(IMAGE_1), findViewById(IMAGE2) }
getDroppedView(v, event.getRawX(), event.getRawY(), cArrayOfPossibileViews);
break;
}
....
#Nullable
private View getDroppedView(View view, int x, int y, View[] arrayOfPossibilities) {
Rect cVisibleBoundsRect = new Rect();
for (View cView : arrayOfPossibilities) {
if (!cView.getGlobalVisibleRect(cVisibleBoundsRect)) continue;
if (cVisibleBoundsRect.contains(x, y)) {
//THIS "cView" IS THE VIEW WHERE YOU RELEASED THE FINGER
return cView;
}
}
return null;
}
This method get View bounds and compare them avains X and Y of your Touch Event. If X and Y are contained inside a View bounds it means that View is the one you need.
I thought this is a pretty relevant and common question, but I couldnt find an answer.
At the moment I have this method:
public boolean onTouchEvent (MotionEvent evt){
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
do stuff ...
}
}
So if the user taps on the screen (wherever) the code is executed. Now I want the distinction between the right side of the display and the left side (left side means --> go back).
You can do this many ways. Here is one of them:
Attach onTouch listener to the view, which stretches to its edges. (For example your RelativeLayout which holds rest of views)
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float halfOfAScreen = mainLayout.getMaxWidth() / 2;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float fingerPosition = event.getX();
if(fingerPosition < halfOfAScreen) {
onBackPressed();
}
return true;
default:
return false;
}
}
};
Refer to this post on how to get touch position.
It seems in your case you will use
int x = (int)event.getX();
int y = (int)event.getY();
and work within the bounds of your layout that you want the app to react to.
I have a listview and I set an onClickListener and an onTouchListener. The click listener for when they click on an item, I had just this and everything thing worked fine. But, when I added the touch listener to watch for left and right swipes, it won't let me scroll anymore.
listview.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
historicX = event.getX();
historicY = event.getY();
break;
case MotionEvent.ACTION_UP:
if(event.getX() - historicX < -DELTA){
slidingLeft(event.getX(),event.getY());
return true;
}
else if(event.getX() - historicX > DELTA){
slidingRight(event.getX(), event.getY());
return true;
}
break;
default: return true;
}
return false;
}
});
How can I have to able to scroll through the listview again. Is there another listener I could add to do this, or could I modify the onTouchListener to watch for scrolls too?
You should use GestureDetector .
You can use example from here, it is very clear.
https://stackoverflow.com/a/12938787/1374065
So what I'm trying to make is a little space game, You control the ship with a graphic on-screen joystick using touch and drag. I have gotten this to work fine. The problem arises once I started to try and add the ability to touch the top portion of the screen to fire a weapon. For some reason, if you are currently dragging the joystick it seems to ignore the other touch input and doesn't do anything (But it works fine once I stop holding the joystick).
This is my both my first time working with java, and with Android so its probably something stupid, but iv been stuck on it for a few days.
Anyway, here my code.
The below is from my
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
#Override
public boolean onTouchEvent(MotionEvent event) {
fingers= event.getPointerCount(); //Returns 1 or 2 properly if 2 fingers on screen
playership.onTouchEvent(event); //Pass the event along to the spaceship object
return true;
//return super.onTouchEvent(event); //no idea what this does, but it seems to disable the drag event
}
Below is the playership.onTouchEvent(event); function
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
if(event.getY()<(Panel.mHeight-150))
{
Laser1.reset(mX,mY,(int)event.getX(),(int)event.getY());
}
break;
}
joy1.onTouchEvent(event);
return true;
}
And below is the joy1.onTouchEvent(event);
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
//Offset between mouse(x,y) and center
double offx=oX-event.getX();
double offy=oY-event.getY();
double d=Math.sqrt((offx*offx)+(offy*offy));
switch (eventaction )
{
case MotionEvent.ACTION_DOWN:
if(d<dragrange) //Start Dragging if clicked
{
offx=offx/d;
offy=offy/d;
Dragging=true;
reset = false;
dX=(int) (offx*dragrange);
dY=(int) (offy*dragrange);
}
break;
case MotionEvent.ACTION_MOVE:
if(Dragging)//if joy is already being draged, update it.
{
offx=offx/d;
offy=offy/d;
if(d>dragrange)
{
dX=(int) (offx*dragrange);
dY=(int) (offy*dragrange);
}
else
{
dX=(int) (offx*d);
dY=(int) (offy*d);
}
reset = false;
}
break;
case MotionEvent.ACTION_UP:
if(Dragging)
{
Dragging=false;
}
break;
}
return true;
}
So yeah, the problem is that if the joystick is in the middle of a case MotionEvent.ACTION_MOVE it seems to block any other touch events from properly registering.
You need to handle detecting the other finger. More specifically handling
MotionEvent.ACTION_POINTER_DOWN and MotionEvent.ACTION_POINTER_UP
otherwise you're ignoring other fingers. Here is some code from one of my projects that does exactly what you're looking for:
public void onTouchEvent(MotionEvent event)
{
int ptrId = -1;
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
down(event.getPointerId(0), (int)event.getX(), (int)event.getY());
break;
case MotionEvent.ACTION_UP:
up(event.getPointerId(0));
break;
case MotionEvent.ACTION_POINTER_DOWN:
ptrId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int ptrIdx = event.findPointerIndex(ptrId);
down(ptrId, (int)event.getX(ptrIdx), (int)event.getY(ptrIdx));
break;
case MotionEvent.ACTION_POINTER_UP:
ptrId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
up(ptrId);
break;
case MotionEvent.ACTION_MOVE:
for(int i = 0; i < event.getPointerCount(); ++i)
if(event.getPointerId(i) == inputPad.id())
{
inputPad.position(event.getX(inputPad.id()));
player.velocity(inputPad.delta());
player.stand();
if(enemy != null) {
Fighter.collide(player, enemy);
enemy.update();
}
player.update();
break;
}
break;
}
}
See here and here for more explanation.