I wish to create a simple game for Android, where the player will be shown a table of buttons. He should have the ability to swipe/drag his fingers over some buttons, and the result of his move should only be displayed after he stops touching the screen.
I can see when Action_UP and Action_Down is called, but I can't use Action_HOVER_MOVE:
View.OnTouchListener OTL = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction()==MotionEvent.ACTION_DOWN
|| motionEvent.getAction()==MotionEvent.ACTION_HOVER_MOVE)
{
Button B = (Button)view;
if (B.isActivated())
{
String letter = B.getText()+"";
word+=letter;
}
B.setActivated(false);
Toast t = Toast.makeText(MainActivity.this,word,Toast.LENGTH_SHORT);
t.show();
}
if (motionEvent.getAction()==MotionEvent.ACTION_UP)
{
Toast t = Toast.makeText(MainActivity.this,word,Toast.LENGTH_SHORT);
t.show();
word="";
resetButtons();
}
return true;
}
};
How to detect swipe over other buttons?
Per the docs, you probably want ACTION_MOVE
int ACTION_MOVE
Constant for getActionMasked(): A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP). The motion contains the most recent point, as well as any intermediate points since the last down or move event.
You're having trouble with ACTION_HOVER_MOVE because:
int ACTION_HOVER_MOVE
Constant for getActionMasked(): A change happened but the pointer is not down (unlike ACTION_MOVE). The motion contains the most recent point, as well as any intermediate points since the last hover move event.
This action is always delivered to the window or view under the pointer.
This action is not a touch event so it is delivered to onGenericMotionEvent(MotionEvent) rather than onTouchEvent(MotionEvent).
Source: https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_MOVE
Related
I have a question to a doubletap-to-zoom action.
With the GestureDetector I got a list of gestures like the doubletap function:
#Override
public boolean onDoubleTap(MotionEvent event)
{
//Scrollanimation
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
Log.d(DEBUG_TAG, "");
}
Log.d(DEBUG_TAG, "onDoubleTap: ");
return true;
}
Now I want to implement a scroll function that enables a scale function by scrolling. How can I implement a scale effect without a scale detector that has to be extended by my MainClass?
DoubleTap is for something like that:
finger down,
finger up,
finger down,
finger up //end of interaction and here event is send onDoubleTap
but ScaleGestureDetector.SimpleOnScaleGestureListener will provide you callbacks in two scenarios:
One:
two fingers down, onScaleBegin
moving two fingers (scaling) onScale
end of interaction onScaleEnd
Second:
one finger down
one finger up (quick)
one finger down
moving finger onScaleBegin and onScale
one finger up onScaleEnd
The second allows you to scale with one finger.
I was wondering if there is a way to know exactly where a button was tapped, and take different actions based on where the user tapped it. Something like:
fooBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
switch(onClickLocation){
case LEFT:
foo();
break;
case RIGHT:
bar();
break;
case MIDDLE:
baz();
break;
}
}
});
Not using an OnClickListener. OnTouchListener gives you a MotionEvent that you can use to determine where the actual touch event occurred.
For example, here I register both an OnClickListener and an OnTouchListener on the same View (called row):
row.setOnClickListener(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
row.setOnTouchListener(new View.OnTouchListener() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public boolean onTouch(View v, MotionEvent event) {
v
.findViewById(R.id.row_content)
.getBackground()
.setHotspot(event.getX(), event.getY());
return(false);
}
});
}
In this case, I don't need to know where the widget was touched for processing the click, but I do need to know where the widget was touched for adjusting the RippleDrawable background, so the ripple appears to emanate from where the user touched. Returning false from onTouch() means I am not consuming the touch event, and so eventually my onClick() method will also be called.
In your case, either:
do the actual work in onTouch(), or
cache the last-seen touch point in onTouch() but do not do the work until onClick()
My gut tells me that the latter should give you better results (e.g., you won't misinterpret a long-click), but I have not tried doing what you are seeking.
I have an ImageView overlay inside of a RelativeLayout and want to prevent any clicks from going through the ImageView to the Buttons etc that are behind it (thus in effect disabling the entire RelativeLayout).
Is the a simpler way of doing this then iterating the RelativeLayout views and setting them to disabled as I currently am doing using this code:
RelativeLayout rlTest = (RelativeLayout ) findViewById(R.id.rlTest);
for (int i = 0; i < rlTest.getChildCount(); i++) {
View view = rlTest.getChildAt(i);
view.setEnabled(true);
}
you can set the image to be
android:clickable="true"
Simply call rlTest.setClickable(false). This will prevent the click to be propagate to the children
There is a much cleaner way
You can use:
android:onClick="preventClicks"
in XML and in your Activity
public void preventClicks(View view) {}
This works with fragments.
Example inside this Activity has multiple fragments overlapping one another, just by adding the XML attribute in the background of your fragment it will still call the Activity.preventClicks and will prevent touches on fragments behind it
The following solution works in the general case:
_container.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// NOTE: This prevents the touches from propagating through the view and incorrectly invoking the button behind it
return true;
}
});
It basically blocks any touches from propagating through the view by marking the touch event as handled.
This works on both UI controls and layout containers (ie: LinearLayout, FrameLayout etc.).
The solution to set "clickable" as false did not work for me for layout containers either in code or in the view XML.
I assume that you are using onClickListeners.
How about using onTouchListener instead of onClickListeners. By doing this you will have a control over how deep down in your hierarchy the touch even can be visible. For example, if you have toch listeners on a relative-layout(RL) and a image-view(IV)(contained in RL), and you assign touchListeners to both. Now if you return true from IV's touch event, the lower down member RL will not receive the touch event. However if you return false from from IV's touch event, the lower down member RL will receive the touch event.
Hope this helps!
Just add these two listeners:
// Set an OnTouchListener to always return true for onTouch events so that a touch
// sequence cannot pass through the item to the item below.
view.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return true;
}
});
// Set an OnHoverListener to always return true for onHover events so that focus cannot
// pass through the item to the item below.
view.setOnHoverListener(new OnHoverListener() {
#Override
public boolean onHover(View v, MotionEvent event) {
v.onHoverEvent(event);
return true;
}
});
You could use databindings and consume the clicks like this:
android:onClick="#{() -> true}"
In C#, I use an empty delegate:
objectName.Click += delegate {};
I haven't encountered any problems with it but it does prevent clicks from filtering through to underlying controls.
you can also se the root click listener to null:
// Do not process clicks on other areas of this fragment
binding.root.setOnClickListener(null)
This works 100%.
It doesnt affect other listeners that are already set on the fragment's views.
I've implemented a grid of buttons using a grid layout. I'm trying to allow for a single swipe to activate multiple buttons. When the user touches any one of the buttons, I call a function with the specific button pressed to take the according action. But, currently, I can only activate one button per touch. Multi-touch works, but not a single swipe. The problem is that while the onTouch function is called continuously the view object that I use to determine the button pressed is only updated on the initial touch. What I need to do is get the id's of all of the buttons that were swiped across.
Thanks.
#Override
public boolean onTouch(View v, MotionEvent event)
{
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_MOVE)
{
switch (v.getId())
{
case R.id.padZeroZero:
padTouch(0,0);
break;
case R.id.padZeroOne:
padTouch(0,1);
break;
case R.id.padZeroTwo:
padTouch(0,2);
break;
//There's a lot more cases (it's a 9x8 grid), but they all do the same thing.
}
}
return false;
}
So hi there.
I have a simple Layout with 2 Views in it. Both have an onTouchListener attached.
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
System.out.println("Touching");
return false;
}
});
But when I open the application on my phone and touch the first view and do NOT relase my finger and touch the second view with another finger, the second view wont trigger the touch event. why is this so?
I think in this case both touches are passed to the first view as a multi-touch event. So this is one event but contains (I forgot the details) both touch positions.