Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Providing Haptic Feedback

Published? true
FormatLanguage: WikiFormat

Problem:

Building user confidence that their actions had an effect is a requirement for any app on any platform. The canonical example is displaying a progress bar to let users know their action took effect and it's being processed. For touch interfaces this technique still applies, but the advantage of a touch interface is that developers have the opportunity to provide physical feedback, users are capable of actually feeling the device react to their actions.

Solution:

I've played with many apps on Android phones and tablets, and the thing I appreciate most is knowing that touching the screen had an effect. I like to know immediately that the app recognized and is reacting to my touch. This reaction comes in three forms, visual, audio, or physical. This recipe discusses how to increase use confidence in your app by providing instant physical feedback through the use of Android's haptic controls.

Discussion:

Android has some stock haptic controls, but if these don't satisfy your needs you can gain control of the device's vibrator for custom feedback.

Custom control of the device's vibrator requires permission, this is something you'll have to explicitly list in your AndroidManifest.xml (example below). If you're paranoid about asking for permission or if you already have a long list of permissions, you may want to use the stock Android haptic feedback options.

Please note, the Motorola Xoom doesn't have a vibrator, therefore the examples below will compile and run, but you will not receive haptic feedback.

I'll start by showing the more complicated example first, custom haptic feedback.

Custom haptic feedback using the device's vibrator

1.) First Step, request the permission. Add the following line to you AndroidManifest.xml.

<uses-permission android:name="android.permission.VIBRATE" />

2.) Now define a listener to respond to touch events. It's not shown here but the CustomHapticListener class is actually a private non-static inner class of my Activity. This is because it needs access to the Context.getSystemService(...) method.

private class CustomHapticListener implements OnTouchListener {

  // Duration in milliseconds to vibrate
  private final int durationMs;
  
  public CustomHapticListener( int ms ) { 
    durationMs = ms;
  }   

  @Override
  public boolean onTouch( View v, MotionEvent event ) {
    if( event.getAction() == MotionEvent.ACTION_DOWN ){
      Vibrator vibe = ( Vibrator ) getSystemService( VIBRATOR_SERVICE );
      vibe.vibrate( durationMs );
    }
    return true;
  }
}

Lines 13 and 14 are the important ones. Line 13 gets a reference to the Vibrator service and line 14 vibrates the device. If you have requested the vibrate permission, line 14 will throw an exception.

3.) Lastly, register the listener. In your Activity's onCreate(...) method. You'll need to get a reference to the GUI element you want to attach haptic feedback to and then register the OnTouchListener we defined above.

@Override
public void onCreate( Bundle savedInstance ) {
  Button customBtn = ( Button ) findViewById( R.id.btn_custom );
  customBtn.setOnTouchListener( new CustomHapticListener( 100 ) );
}

That's it you're now in control of the haptic feedback, now onto using stock Android haptic feedback.

Stock Haptic Feedback Events

First things first: to use stock Android haptic feedback events you must enable this on View-by-View basis. That is, you must explicitly enable haptic feedback for each View. Enabling haptic feedback can be done declaratively in your layout file or programmatically in Java. To enable haptic feedback in your layout, simply add the android:hapticFeedbackEnabled="true" attribute to your View(s). Here's an abbreviated example:

<button android:hapticFeedbackEnabled="true"> 
</button>

Here's how you do the same thing in code:

Button keyboardTapBtn = ( Button ) findViewById( btnId );
keyboardTapBtn.setHapticFeedbackEnabled( true );

Now that haptic feedback has been enabled, the next step is to register an OnTouchListener and then perform the actual feedback. Below is an example of registering an OnTouchListener and performing haptic feedback when a user touches the View.

// Initialize some buttons with the stock Android haptic feedback values
private void initializeButtons() {
    // intialize the buttons with the standard Haptic feedback options
    initializeButton( R.id.btn_keyboardTap, HapticFeedbackConstants.KEYBOARD_TAP );
    initializeButton( R.id.btn_longPress,   HapticFeedbackConstants.LONG_PRESS );
    initializeButton( R.id.btn_virtualKey,  HapticFeedbackConstants.VIRTUAL_KEY );
}

// helper method to initialize single buttons and register an OnTouchListener
// to perform the haptic feedback
private void initializeButton( int btnId, int hapticId ) {
    Button btn = ( Button ) findViewById( btnId );
    btn.setOnTouchListener( new HapticTouchListener( hapticId ) );     
}    

// Class to handle touch events and respond with haptic feedback
private class HapticTouchListener implements OnTouchListener {

    private final int feedbackType;
     
    public HapticTouchListener( int type ) { feedbackType = type; }
     
    public int feedbackType() { return feedbackType; }
     
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // only perform feedback when the user touches the view, as opposed
        // to lifting a finger off the view
        if( event.getAction() == MotionEvent.ACTION_DOWN ){
            // perform the feedback
            v.performHapticFeedback( feedbackType() );
        }
        return true;
    }
}

You'll notice on lines 3 - 5 I'm initializing 3 different buttons with three different haptic feedback constants. These are Android's stock values, 2 of the 3 seem to provide exactly the same feedback. The example code above is part of a test app I wrote to demonstrate haptic feedback and I could not tell the difference between HapticFeedbackConstants.LONG_PRESS and HapticFeedbackConstants.KeyboardTap. Also, HapticFeedbackConstants.VIRTUAL_KEY do not appear to provide any feedback when tested.

Line 31 is where the haptic feedback is performed. All in all, providing haptic feedback is pretty simple, if you want control of the device's vibrator make sure you request permission in your AndroidManifest.xml. If you choose to use the stock Android haptic feedback options, make sure you enable haptic feedback for your Views either in the layout or programmatically.

See Also:

http://mytensions.blogspot.com/2011/03/androids-haptic-feedback.html

Download:

The source code for this project can be downloaded from https://docs.google.com/leaf?id=0BwH86cQEzwiZZjZiMThmM2EtZDk3Zi00NTViLTk0NjYtNDU2YzI5MjVmMzYw&hl=en&authkey=CJu58JcL.