Navigation.java
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.navigation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewParent;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import java.lang.ref.WeakReference;
/**
* Entry point for navigation operations.
*
* <p>This class provides utilities for finding a relevant {@link NavController} instance from
* various common places in your application, or for performing navigation in response to
* UI events.</p>
*/
public final class Navigation {
// No instances. Static utilities only.
private Navigation() {
}
/**
* Find a {@link NavController} given the id of a View and its containing
* {@link Activity}. This is a convenience wrapper around {@link #findNavController(View)}.
*
* <p>This method will locate the {@link NavController} associated with this view.
* This is automatically populated for the id of a {@link NavHost} and its children.</p>
*
* @param activity The Activity hosting the view
* @param viewId The id of the view to search from
* @return the {@link NavController} associated with the view referenced by id
* @throws IllegalStateException if the given viewId does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull Activity activity, @IdRes int viewId) {
View view = ActivityCompat.requireViewById(activity, viewId);
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("Activity " + activity
+ " does not have a NavController set on " + viewId);
}
return navController;
}
/**
* Find a {@link NavController} given a local {@link View}.
*
* <p>This method will locate the {@link NavController} associated with this view.
* This is automatically populated for views that are managed by a {@link NavHost}
* and is intended for use by various {@link android.view.View.OnClickListener listener}
* interfaces.</p>
*
* @param view the view to search from
* @return the locally scoped {@link NavController} to the given view
* @throws IllegalStateException if the given view does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull View view) {
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("View " + view + " does not have a NavController set");
}
return navController;
}
/**
* Create an {@link android.view.View.OnClickListener} for navigating
* to a destination. This supports both navigating via an
* {@link NavDestination#getAction(int) action} and directly navigating to a destination.
*
* @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
* navigate to when the view is clicked
* @return a new click listener for setting on an arbitrary view
*/
@NonNull
public static View.OnClickListener createNavigateOnClickListener(@IdRes final int resId) {
return createNavigateOnClickListener(resId, null);
}
/**
* Create an {@link android.view.View.OnClickListener} for navigating
* to a destination. This supports both navigating via an
* {@link NavDestination#getAction(int) action} and directly navigating to a destination.
*
* @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
* navigate to when the view is clicked
* @param args arguments to pass to the final destination
* @return a new click listener for setting on an arbitrary view
*/
@NonNull
public static View.OnClickListener createNavigateOnClickListener(@IdRes final int resId,
@Nullable final Bundle args) {
return new View.OnClickListener() {
@Override
public void onClick(View view) {
findNavController(view).navigate(resId, args);
}
};
}
/**
* Create an {@link android.view.View.OnClickListener} for navigating
* to a destination via a generated {@link NavDirections}.
*
* @param directions directions that describe this navigation operation
* @return a new click listener for setting on an arbitrary view
*/
@NonNull
public static View.OnClickListener createNavigateOnClickListener(
@NonNull final NavDirections directions) {
return new View.OnClickListener() {
@Override
public void onClick(View view) {
findNavController(view).navigate(directions);
}
};
}
/**
* Associates a NavController with the given View, allowing developers to use
* {@link #findNavController(View)} and {@link #findNavController(Activity, int)} with that
* View or any of its children to retrieve the NavController.
* <p>
* This is generally called for you by the hosting {@link NavHost}.
* @param view View that should be associated with the given NavController
* @param controller The controller you wish to later retrieve via
* {@link #findNavController(View)}
*/
public static void setViewNavController(@NonNull View view,
@Nullable NavController controller) {
view.setTag(R.id.nav_controller_view_tag, controller);
}
/**
* Recurse up the view hierarchy, looking for the NavController
* @param view the view to search from
* @return the locally scoped {@link NavController} to the given view, if found
*/
@Nullable
private static NavController findViewNavController(@NonNull View view) {
while (view != null) {
NavController controller = getViewNavController(view);
if (controller != null) {
return controller;
}
ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
return null;
}
@SuppressWarnings("unchecked")
@Nullable
private static NavController getViewNavController(@NonNull View view) {
Object tag = view.getTag(R.id.nav_controller_view_tag);
NavController controller = null;
if (tag instanceof WeakReference) {
controller = ((WeakReference<NavController>) tag).get();
} else if (tag instanceof NavController) {
controller = (NavController) tag;
}
return controller;
}
}