Disable or prevent multitouch in Activity - android

I have several Views on an Activity which a user wants to touch quickly in succession and I capture these touches using a TouchListener and handling MotionEvent.ACTION_DOWN. However, if the user is using two hands, it's very likely that the next View will be 'Touch'ed before the user pulls the previous finger up. In this scenario, a MotionEvent.ACTION_MOVE is fired for the first view rather than the desired MotionEvent.ACTION_DOWN for the second view.
Is there any way to work around or prevent this behavior? I've tried dispatching a new event with MotionEvent.ACTION_UP and also removing the event listener but neither seem to work.

The easiest way I found to force single touch across an entire app is to set it using a theme:
<style name="MyTheme" parent="#android:style/Theme.Holo.Light">
<item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents">false</item>
</style>
Manifest:
<application
android:label="#string/app_name"
android:theme="#style/MyTheme" >

if someone still searching for best solution,
put in the xml the following code :
android:splitMotionEvents = false

I did it like this :
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if(event.getPointerCount() > 1) {
System.out.println("Multitouch detected!");
return true;
}
else
return super.onTouchEvent(event);
}
I think you can override onTouchEvent for any view.

The best way to work around this scenario is to use a ViewGroup and implement the onInterceptTouchEvent method to manually route the touch events as you see fit.
This was originally answered here:
Android multitouch! hack anyone?
Code implementing this solution (found in the comments section of the answer to the above question) is found here:
http://pastebin.com/hiE1aTCw

I have solved this using a custom method - which I did not want to do If anyone finds a better way I'd like to hear about it Thanks:
public static void setViewGroupEnebled(ViewGroup view, boolean enabled)
{
int childern = view.getChildCount();
for (int i = 0; i< childern ; i++)
{
View child = view.getChildAt(i);
if (child instanceof ViewGroup)
{
setViewGroupEnebled((ViewGroup) child,enabled);
}
child.setEnabled(enabled);
}
view.setEnabled(enabled);
}

Override dispatchTouchEvent and intercept all touches there, for all multi touches the pointercount is more than one.
if(ev.getPointerCount() > 1)
{
Log.d("Multitouch detected!");
ev.setAction(MotionEvent.ACTION_CANCEL);
}
This cancels the touches and clears the pressed state of the buttons without taking any further action.

For me it worked like this.
I just put this line in all active styles in my app
<item name="android:splitMotionEvents">false</item>
my style for example
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:splitMotionEvents">false</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:splitMotionEvents">false</item>
</style>

int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {
horizentalLinearGridItem.setMotionEventSplittingEnabled(false);
}
Here horizentalLinearGridItem is parentLayout view where we insert child views.
In my code its LinearLayout. In this LinearLayout I added child views. But when I clicked two child views simultaneously, both were getting the events. To block that I used the above code.

I got it to work for me by just adding some code in OnTouchEvent method,
boolean action_down_required = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if(event.getPointerCount() > 1){
action_down_required = true;
return true;
}
if(action_down_required){
action = MotionEvent.ACTION_DOWN;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
// your code goes here...
action_down_required = false;
break;
// Other events...

Related

Android Launcher: Allow touches to pass through to live wallpaper

I'm trying to create a simple Android Launcher.
I'm also using a live wallpaper (Kustom LWP) that I can tap certain areas of to perform actions.
I'm able to see the wallpaper using these style properties:
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
But if I try to tap an item on my wallpaper, the touch doesn't pass through.
I've tried android:clickable="false and android:focusable="false", and setting all of my views to have a setOnTouchListener that returns false, but none of these fix the issue.
How would I go about doing this?
Answering my own question: after doing some digging in Launcher3, I found that I needed to use WallpaperManager's sendWallpaperCommand function on the touch listener for your homescreen view:
import android.app.WallpaperManager;
private final int[] mTempXY = new int[2];
WallpaperManager mWallpaperManager = WallpaperManager.getInstance(context);
...
homeScreenView.setOnTouchListener((v, event) -> {
onWallpaperTap(v,event);
return true;
});
...
protected void onWallpaperTap(View view, MotionEvent ev) {
final int[] position = mTempXY;
view.getLocationOnScreen(position);
int pointerIndex = ev.getActionIndex();
position[0] += (int) ev.getX(pointerIndex);
position[1] += (int) ev.getY(pointerIndex);
MainActivity.mWallpaperManager.sendWallpaperCommand(view.getWindowToken(),
ev.getAction() == MotionEvent.ACTION_UP
? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
position[0], position[1], 0, null);
}
And now my wallpaper can receive touch input.

How to change action Navigation drawer?

I have to implement the following functionality. See the picture.
Can I change the functionality of Navigation drawer? I haven't any found fix for my problem.
I hope you can help me, thanks in advance.
Yes it's possible. However I advise you against it because it kind of breaks the UI.
If you choose to do it though, first you need to be able to access the overflow menu (circled button). This was answered before here, so I'll just provide a style.xml snippet:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:actionOverflowButtonStyle">#style/Overflow</item>
</style>
<style name="Overflow" parent="Widget.AppCompat.ActionBar">
<item name="android:contentDescription">OVERFLOW</item>
</style>
Then, when the home (navigation drawer) button is clicked you open the overflow menu:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
final View decor = getWindow().getDecorView();
ArrayList<View> results = new ArrayList<>();
decor.findViewsWithText(results, "OVERFLOW",
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if (results.size() == 1) {
results.get(0).performClick();
return true;
}
}
return super.onOptionsItemSelected(item);
}

How to detect if click event is finished in android?

I create views dynamically, and I handle their click events normally, like this :
myView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
v.setBackgroundColor(0xaaffffff);
}
});
But this code does not handle the onRelease event, so the view remains with white background, after the click.
So how can I set the background to another value when the clicked state is over ?
Thanks in advance !
Edit:
OnTouchListener does the exact same thing. I guess I need something like onReleaseListener ?
For that you need to use an onTouchListener => documentation
Refer this tutorial on how to use this.
Example:
myView.setOnTouchListener(
new View.OnTouchListener() {
public boolean onTouch(View myView, MotionEvent event) {
int action = event.getAction();
if (action==MotionEvent.ACTION_MOVE)
{
myView.setBackgroundColor(Color.BLUE);
}
if (action==MotionEvent.ACTION_UP)
{
myView.setBackgroundColor(Color.RED);
}
if (action==MotionEvent.ACTION_DOWN)
{
myView.setBackgroundColor(Color.YELLOW);
}
// TODO Auto-generated method stub
return true;
}
}
Android has a selector for this. Define it in a xml file in your drawable folder and use it as background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:drawable="#drawable/YOURDRAWABLE" />
<item android:state_pressed="true" android:drawable="#drawable/YOURDRAWABLE_WHEN_CLICKED" />
</selector>
Check the DOCUMENTATION
State lists are meant to be for style updates on state changes. Check this out.
http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
You dont need to go for such cómplicated method for capturing a "click" event. Just for this method :-
//Inside on touch listener of course :-
KOTLIN :-
if(event.action == MotionEvent.ACTION_UP && event.action != MotionEvent.ACTION_MOVE) {
// Click has been made...
// Some code
}
JAVA :-
Just replace event.action with event.getAction()
This works for me 😉

how to highlight action bar item select in android

I am creating an app wherein I have three items in the action bar. I want that when I click on one of the item, it should get highlighted.
I went through few examples available but concluded that I will have to create style using
ActionBar Style Generator tool.
I referred the following link.. but was not satisfied
how to highlight a menu item on selection?
Is this the only way to go about?
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:actionBarItemBackground">#drawable/action_bar_item_selector</item>
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:drawable="#drawable/ic_action_blue" />
<item android:state_pressed="true"
android:drawable="#drawable/ic_action_green" />
<item android:drawable="#android:color/transparent" />
</selector>
But the selection does not remain stagnant as it is for state_pressed and state_focused.
I want the item to remain highlighted till the user is on that activity.
More elegant way of handling the issue , is to simply have a boolean value that will check
both states , If you are using the same drawable with different state colors , i will recommend
you to use the colorFilter method to apply to your drawables.
private boolean mSomeValue = true;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// determine the menu item you want to highlight when selected
switch(item.getItemId) {
case yourItem : {
if (mSomeValue) {
item.getIcon().setColorFilter(YourColor, PorterDuff.Mode.MULTIPLY);
mSomeValue = false;
} else {
mSomeValue = true;
item.getIcon().setColorFilter(Default, PorterDuff.Mode.MULTIPLY);
}
break; ...
}
}
return super.onOptionsItemSelected(item);
}
You can define a theme and inherit the Action bar theme you want (for instance Theme.Sherlock.Light.DarkActionBar), set the value of the android:actionBarItemBackground attribute to override the default one in the inheritted theme.
<style name="MyTheme" parent="Theme.Sherlock.Light.DarkActionBar">
<item name="android:actionBarItemBackground">custom_selector</item>
</style>
I am creating an app wherein I have three items in the action bar. I
want that when I click on one of the item, it should get highlighted.
I am not sure if the following will work properly:
Create two sets of icons for each of the menu items: one for the default look, the other highlighted (in whatever way you feel: change the color, change the background color etc.). Declare a global Menu variable to hold the menu item:
// Global variable // Not good practice, but needed in this case
Menu mainMenu;
....
....
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_name, menu);
mainMenu = menu;
return true;
}
In onMenuItemSelected(int featureId, MenuItem item):
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
for (int i = 0; i < mainMenu.size(); i++) {
// Set default icons
if (mainMenu.getItem(i).getItemId() == R.id.action1) {
mainMenu.getItem(i).setIcon(R.drawable.icon_default1);
} else if (mainMenu.getItem(i).getItemId() == R.id.action2) {
mainMenu.getItem(i).setIcon(R.drawable.icon_default2);
} else if (.....) {
.....
}
}
if (item.getItemId() == R.id.action1) {
item.setIcon(R.drawable.icon_highlighted1);
} else if (item.getItemId() == R.id.action2) {
item.setIcon(R.drawable.icon_highlighted2);
} else if(.....) {
.....
}
return super.onMenuItemSelected(featureId, item);
}
I guess you can fine-tune this by checking against item.getItemId() inside the for-loop itself.
Ok, so i suggest you to create two different items in your menu.xml:
<item
android:id="#+id/action_item1"
/>
<item
android:id="#+id/action_item2"
android:visible="false"
/>
Then, in the activity in which you want to highlight your item, override the ononPrepareOptionsMenu , use the menu paremeter to catch your desired item by using the menu.findItem(R.id.your_item_id). And finally toggle the visibility of your items.
You will need to refresh your action bar items automatically by using invalidateOptionsMenu
Let me know if that helps !

How to cancel an Dialog themed like Activity when touched outside the window?

I have an activity with a Dialog theme and I would like to close (finish) this activity when someone touches the screen anywhere outside this activity's window ? How can I do this ?
Just to point out that there is a way to get dialog-like "touch outside to cancel" behaviour from an Activity themed as a dialog, though I've not fully investigated whether it has unwanted side effects.
Within your Activity's onCreate() method, before creating the view, you're going to set two flags on the window: One to make it 'non-modal', to allow views other than your activity's views to receive events. The second is to receive notification that one of those events has taken place, which will send you an ACTION_OUTSDIE move event.
If you set the theme on the activity to the dialog theme, you'll get the behaviour you want.
It looks something like this:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make us non-modal, so that others can receive touch events.
getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);
// ...but notify us that it happened.
getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
// Note that flag changes must happen *before* the content view is set.
setContentView(R.layout.my_dialog_view);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// If we've received a touch notification that the user has touched
// outside the app, finish the activity.
if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
finish();
return true;
}
// Delegate everything else to Activity.
return super.onTouchEvent(event);
}
}
I found an even simpler answer that has worked perfectly for me. If you're using an activity with the dialog theme then you can apply this.setFinishOnTouchOutside(true); to the activity's onCreate() method.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yoptions);
this.setFinishOnTouchOutside(true);
}
It's very simple, just set the property canceledOnTouchOutside = true. look at the example:
Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);
It is possible quite easily:
First define your own theme in style.xml:
<style name="DialogSlideAnim" parent="#android:style/Theme.Holo.Dialog">
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
Then in your manifest apply this theme to activity:
<activity
android:label="#string/app_name"
android:name=".MiniModeActivity"
android:theme="#style/DialogSlideAnim" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
A combination of Gregory and Matt's answers worked best for me (for Honeycomb and presumably others). This way, views outside will not get touch events when the user tries to touch-outside-cancel the dialog.
In the main Activity, create the touch interceptor in onCreate():
touchInterceptor = new FrameLayout(this);
touchInterceptor.setClickable(true); // otherwise clicks will fall through
In onPause() add it:
if (touchInterceptor.getParent() == null) {
rootViewGroup.addView(touchInterceptor);
}
(rootViewGroup may have to be either a FrameLayout or a RelativeLayout. LinearLayout may not work.)
In onResume(), remove it:
rootViewGroup.removeView(touchInterceptor);
Then, for the dialog-themed Activity, use the code Gregory offered (copied here for your convenience):
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make us non-modal, so that others can receive touch events.
getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);
// ...but notify us that it happened.
getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
// Note that flag changes must happen *before* the content view is set.
setContentView(R.layout.my_dialog_view);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// If we've received a touch notification that the user has touched
// outside the app, finish the activity.
if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
finish();
return true;
}
// Delegate everything else to Activity.
return super.onTouchEvent(event);
}
}
If using a dialog theme like android:theme="#style/Theme.AppCompat.Dialog" or any other dialog theme.
On API 11 and after we can use
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setFinishOnTouchOutside(false);
}
call this inside onCreate of the activity.
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect dialogBounds = new Rect();
getWindow().getDecorView().getHitRect(dialogBounds);
if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
return true;
}
return super.dispatchTouchEvent(ev);
}
This code is solved my problem.
I couldn't get the top answer here to work on a Samsung tab running 3.1, so I did this:
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
int xmargin = (ViewUtils.getScreenWidth() - Constants.PRODUCT_DIALOG_WIDTH) / 2;
int ymargin = (ViewUtils.getScreenHeight() - Constants.PRODUCT_DIALOG_HEIGHT) / 2;
if (
x < xmargin ||
x > ViewUtils.getScreenWidth() - xmargin ||
y < ymargin ||
y > ViewUtils.getScreenHeight() - ymargin
) {
finish();
return true;
}
return super.onTouchEvent(event);
}
You'll need to replace Constants.PRODUCT_DIALOG_WIDTH and Constants.PRODUCT_DIALOG_HEIGHT with the width/height of your dialog. Mine was used in a number of places so I made them constants.
You'll also need to implement your own method to get the the screen width and height, which you can find easily on this here site. Don't forget to account for the Android header in that!
It's kind of ugly, and I'm not proud, but it works.
You can reference the dialog.java code from android source:
public boolean onTouchEvent(MotionEvent event) {
if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
cancel();
return true;
}
return false;
}
private boolean isOutOfBounds(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) || (y > (decorView.getHeight()+slop));
}
Just modify it to :
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
finish();
return true;
}
return false;
}
private boolean isOutOfBounds(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(this).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > decorView.getHeight() + slop));
}
can solve your problem.
The only way I got this to work was
alert = new AlertDialog.Builder(this)....
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(final DialogInterface arg0) {
Log.i(APP_NAME, "in OnDismissListener");
// removeDialog(R.layout.dialog3);
alert.dismiss();
finish();
}
i.e. I had to put both dismiss and finish in explicitly, otherwise I ended up with a small white rectangle in the middle of the screen.
An Activity have dispatchTouchEvent use that
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
finish();
return super.dispatchTouchEvent(ev);
}
Just add this item to styles.xml:
<style name="alert_dialog" parent="android:Theme.Dialog">
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowBackground">#color/float_transparent</item>
<item name="android:windowAnimationStyle">#null</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:backgroundDimAmount">0.4</item>
</style>
And in onCreate() and before setContentView:
setTheme(R.style.alert_dialog);
Using method setFinishOnTouchOutside to enable/disable whether outside is touchable or not.
This is working for activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yoptions);
/* your code here */
// set outside touchable
this.setFinishOnTouchOutside(true);
}
For those who want to not close the Dialog Application if touch is outside the Dialog.
Add this line
this.setFinishOnTouchOutside(false);
It will not close the dialog box
Just use this theme. Activity will be dismissed on touch outside.
<style name="DialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog">
<item name="android:windowIsTranslucent">true</item>
</style>
Kotlin version worked for me
alert.setOnDismissListener(DialogInterface.OnDismissListener() {
it.dismiss()
})
If there's no API support, you should just use a FrameLayout to fill the screen, and manually build a pop-up. Then you can receive focus anywhere on the screen and show/hide views accordingly.

Categories

Resources