Exit full-screen activities on Wear

A user can exit a Wear OS activity by swiping from left to right. However, if the app has horizontal scrolling, a user exits by navigating to the edge of the content and then swiping from left to right. Pressing the power button also returns the user to the watch face.

The swipe-to-dismiss gesture

Users swipe from left to right to close the current screen. Therefore, it is recommended that you use the following:

  • Vertical layouts
  • Content containers

It also is recommended that your app not contain horizontal swiping gestures.

Dismissal of an activity

An activity automatically supports swipe-to-dismiss. Swiping an activity from left to right results in dismissal of the activity, and the app navigates down the back stack.

Fragments

Fragments require that you wrap a fragment-containing view in the SwipeDismissFrameLayout class, to support swipe to dismiss (Wear OS's back action). Take this into consideration when choosing to use framgents. Use the SwipeDismissFrameLayout class as shown in the following example:

Kotlin

class SwipeDismissFragment : Fragment() {
    private val callback = object : SwipeDismissFrameLayout.Callback() {
        override fun onSwipeStarted(layout: SwipeDismissFrameLayout) {
            // optional
        }

        override fun onSwipeCanceled(layout: SwipeDismissFrameLayout) {
            // optional
        }

        override fun onDismissed(layout: SwipeDismissFrameLayout) {
            // Code here for custom behavior such as going up the
            // back stack and destroying the fragment but staying in the app.
        }
    }

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View =
            SwipeDismissFrameLayout(activity).apply {

                // If the fragment should fill the screen (optional), then in the layout file,
                // in the androidx.wear.widget.SwipeDismissFrameLayout element,
                // set the android:layout_width and android:layout_height attributes
                // to "match_parent".

                inflater.inflate(
                        R.layout.swipe_dismiss_frame_layout,
                        this,
                        false
                ).also { inflatedView ->
                    addView(inflatedView)
                }
                addCallback(callback)
            }
}

Java

public class SwipeDismissFragment extends Fragment {
  private final Callback callback =
    new Callback() {
      @Override
        public void onSwipeStart() {
          // optional
        }

        @Override
        public void onSwipeCancelled() {
          // optional
        }

        @Override
        public void onDismissed(SwipeDismissFrameLayout layout) {
          // Code here for custom behavior such as going up the
          // back stack and destroying the fragment but staying in the app.
        }
      };

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    SwipeDismissFrameLayout swipeLayout = new SwipeDismissFrameLayout(getActivity());

    // If the fragment should fill the screen (optional), then in the layout file,
    // in the androidx.wear.widget.SwipeDismissFrameLayout element,
    // set the android:layout_width and android:layout_height attributes
    // to "match_parent".

    View inflatedView = inflater.inflate(R.layout.swipe_dismiss_frame_layout, swipeLayout, false);
    swipeLayout.addView(inflatedView);
    swipeLayout.addCallback(callback);

    return swipeLayout;
    }
}

Horizontal scrollable views

In some cases, such as in a view containing a map that supports panning, the user interface would not prevent horizontal swiping. In this scenario, there are two choices:

  • If the back stack is short, the user can dismiss the app and return to the home screen (watch face) by pressing the power button.
  • If you want the user to go down the back stack, you can wrap the view in a SwipeDismissFrameLayout object, which supports edge swipe. Edge swipe is enabled when the view or its children returns true from a canScrollHorizontally() call. Edge swipe enables the user to dismiss the view by swiping from the leftmost side of the screen (currently set to 10% of the screen width) and not just anywhere in the view.

To wrap a view in a SwipeDismissFrameLayout object, review the following examples:

<androidx.wear.widget.SwipeDismissFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/swipe_dismiss_root" >

    <TextView
        android:id="@+id/test_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Swipe me to dismiss me." />
</androidx.wear.widget.SwipeDismissFrameLayout>

Kotlin

activity?.findViewById<SwipeDismissFrameLayout>(R.id.swipe_dismiss_root)?.apply {
    addCallback(object : SwipeDismissFrameLayout.Callback() {

        override fun onDismissed(layout: SwipeDismissFrameLayout) {
            layout.visibility = View.GONE
        }
    })
}

Java

SwipeDismissFrameLayout testLayout =
    (SwipeDismissFrameLayout) activity.findViewById(R.id.swipe_dismiss_root);
testLayout.addCallback(new SwipeDismissFrameLayout.Callback() {
    @Override
    public void onDismissed(SwipeDismissFrameLayout layout) {
        layout.setVisibility(View.GONE);
    }
  }
);

Generally not recommended: Disabling swipe-to-dismiss

Disabling swipe-to-dismiss isn't generally recommended because the user expects to dismiss any screen with a swipe. In an exceptional case, you can extend the default theme (in a style resource) and set the android:windowSwipeToDismiss attribute to false, as shown in the following code sample:

<resources>
  <style name="AppTheme" parent="@android:style/Theme.DeviceDefault">
    <item name="android:windowSwipeToDismiss">false</item>
  </style>
</resources>

You can then inform users (on their first use of your app) that they can exit the app by pressing the power button.

Dismissal with the power button

A press of the physical power button sends a power key event. Therefore, you can't use the power button as a back button or for navigation in general.

When pressed, the power button returns the user to the home screen (watch face). There are two exceptions:

  • If the user is in an IME (an Input Method Editor, for example, a handwriting recognition screen), pressing the button closes the IME and returns the user to the app.
  • If the user is at the watch face, pressing the hardware button opens the app launcher.

Note that when the power button is pressed, the isFinishing() method of the Activity class does not return true (nor can you intercept the key event).

For more information, see Swipe to close pattern.