Navegação e interface responsivas
Para oferecer a melhor experiência de navegação aos seus usuários, você precisa oferecer uma IU de navegação personalizada para a largura, altura e menor largura do dispositivo do usuário. Use uma barra de apps inferior, uma gaveta de navegação sempre presente ou recolhível, uma coluna ou talvez algo completamente novo com base no espaço de tela disponível e no estilo exclusivo do seu app.
O guia da arquitetura do produto do Material Design fornece contexto e considerações adicionais para criar uma IU responsiva, ou seja, uma IU que se adapta dinamicamente a alterações ambientais. Alguns exemplos de mudanças ambientais incluem ajustes na largura, altura, orientação e preferência de idioma do usuário. Essas propriedades ambientais são coletivamente denominadas configuração do dispositivo.
Quando uma ou mais dessas propriedades são alteradas no ambiente de execução, o SO Android responde destruindo e recriando as atividades e os fragmentos do app. Portanto, o melhor que você pode fazer para oferecer suporte a uma interface responsiva no Android é garantir que você esteja usando qualificadores de configuração de recursos quando apropriado e evitando o uso de tamanhos de layout codificados.
Como implementar a navegação global em uma interface responsiva
A implementação da navegação global como parte de uma IU responsiva começa com a
atividade que está hospedando o gráfico de navegação. Para um exemplo prático,
confira o
codelab de navegação.
O codelab usa um NavigationView
para exibir o menu de navegação, como mostrado na Figura 2. Quando executado em um dispositivo
que é renderizado com uma largura de pelo menos 960 dp, esse
NavigationView
está sempre na tela.
Outros tamanhos e orientações de dispositivo alternam dinamicamente entre
DrawerLayout
ou
BottomNavigationView
,
conforme necessário.
É possível implementar esse comportamento criando três layouts diferentes, em que cada um deles define os elementos de navegação e a hierarquia de visualização desejados com base na configuração atual do dispositivo.
A configuração a que cada layout se aplica é determinada pela estrutura de diretórios
em que o arquivo de layout é colocado. Por exemplo, o arquivo de layout NavigationView
é encontrado no diretório res/layout-w960dp
.
<!-- res/layout-w960dp/navigation_activity.xml -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
app:elevation="0dp"
app:headerLayout="@layout/nav_view_header"
app:menu="@menu/nav_drawer_menu" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_toEndOf="@id/nav_view"
android:background="?android:attr/listDivider" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/nav_view"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
android:layout_toEndOf="@id/nav_view"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</RelativeLayout>
A visualização de navegação inferior é encontrada no diretório res/layout-h470dp
:
<!-- res/layout-h470dp/navigation_activity.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu" />
</LinearLayout>
O layout de gaveta é encontrado no diretório res/layout
. Use esse diretório para
layouts padrão sem qualificadores específicos de configuração:
<!-- res/layout/navigation_activity.xml -->
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
O Android segue uma
ordem de precedência
ao determinar quais recursos serão aplicados. Especificamente para este exemplo, -w960dp
(ou largura disponível >= 960 dp) tem precedência sobre -h470dp
(ou
altura >= 470). Se a configuração do dispositivo não corresponder a nenhuma dessas
condições, o recurso de layout padrão
(res/layout/navigation_activity.xml
) será usado.
Ao processar eventos de navegação, você precisa conectar apenas os eventos que correspondem aos widgets que estão presentes, como mostrado no exemplo a seguir.
Kotlin
class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration : AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.navigation_activity) val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout) appBarConfiguration = AppBarConfiguration( setOf(R.id.home_dest, R.id.deeplink_dest), drawerLayout) ... // Initialize the app bar with the navigation drawer if present. // If the drawerLayout is not null here, a Navigation button will be added // to the app bar whenever the user is on a top-level destination. setupActionBarWithNavController(navController, appBarConfig) // Initialize the NavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. val sideNavView = findViewById<NavigationView>(R.id.nav_view) sideNavView?.setupWithNavController(navController) // Initialize the BottomNavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view) bottomNav?.setupWithNavController(navController) ... } ... }
Java
public class MainActivity extends AppCompatActivity { private AppBarConfiguration appBarConfiguration; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.navigation_activity); NavHostFragment host = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.my_nav_host_fragment); NavController navController = host.getNavController(); DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); appBarConfiguration = new AppBarConfiguration.Builder( R.id.home_dest, R.id.deeplink_dest) .setDrawerLayout(drawerLayout) .build(); // Initialize the app bar with the navigation drawer if present. // If the drawerLayout is not null here, a Navigation button will be added to // the app bar whenever the user is on a top-level destination. NavigationUI.setupActionBarWithNavController( this, navController, appBarConfiguration); // Initialize the NavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. NavigationView sideNavView = findViewById(R.id.nav_view); if(sideNavView != null) { NavigationUI.setupWithNavController(sideNavView, navController); } // Initialize the BottomNavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. BottomNavigationView bottomNav = findViewById(R.id.bottom_nav_view); if(bottomNav != null) { NavigationUI.setupWithNavController(bottomNav, navController); } } }
Se a configuração do dispositivo mudar, a menos que explicitamente
configurado de outra forma,
o Android destruirá a atividade da configuração anterior com as
visualizações associadas. Em seguida, a atividade é recriada com recursos projetados
para a nova configuração. A atividade que está sendo destruída e recriada, depois
conecta automaticamente os elementos de navegação globais adequados em onCreate()
.
Considere alternativas para layouts de visualização dividida
Layouts de visualização dividida, ou layouts mestres/detalhados, já foram uma maneira muito recomendada e recomendada de projetar para tablets e outros dispositivos de tela grande.
Desde o lançamento dos tablets Android, o ecossistema de dispositivos cresceu bastante. Um fator que influenciou consideravelmente o espaço de design para dispositivos de tela grande foi o lançamento dos modos de várias janelas, especialmente janelas de formato livre totalmente redimensionáveis, como as dos dispositivos ChromeOS. Isso dá uma ênfase significativamente maior para que cada tela do app seja responsiva, em vez de alterar a estrutura de navegação com base no tamanho da tela.
Embora seja possível implementar uma interface de layout de visualização dividida usando a Biblioteca de navegação, considere outras alternativas.
Nomes de destinos
Se você fornecer nomes de destino no gráfico usando o atributo android:label
,
sempre use valores de recurso para que seu conteúdo ainda
possa ser localizado.
<navigation ...>
<fragment
android:id="@+id/my_dest"
android:name="com.example.MyFragment"
android:label="@string/my_dest_label"
tools:layout="@layout/my_fragment" />
...
Com os valores de recursos, seus destinos têm automaticamente os recursos mais apropriados aplicados sempre que sua configuração é alterada.