O Android 8.0 introduziu uma nova arquitetura de informações para o aplicativo Configurações para simplificar a maneira como as configurações são organizadas e tornar mais fácil para os usuários encontrarem rapidamente as configurações para personalizar seus dispositivos Android. O Android 9 introduziu algumas melhorias para fornecer mais funcionalidades de Configurações e implementação mais fácil.
Exemplos e fonte
A maioria das páginas em Configurações está atualmente implementada usando a nova estrutura. Um bom exemplo é DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java
Os caminhos de arquivos para componentes importantes estão listados abaixo:
- CategoryKey :
packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
- DashboardFragmentRegistry :
packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
- DashboardFragment :
packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
- AbstractPreferenceController :
frameworks/base/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
- BasePreferenceController (introduzido no Android 9):
packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java
Implementação
Os fabricantes de dispositivos são incentivados a adaptar a arquitetura de informações de configurações existente e inserir páginas de configurações adicionais conforme necessário para acomodar recursos específicos do parceiro. Mover as preferências da página herdada (implementada como SettingsPreferencePage
) para uma nova página (implementada usando DashboardFragment
) pode ser complicado. A preferência da página legada provavelmente não é implementada com um PreferenceController
.
Portanto, ao mover as preferências de uma página herdada para uma nova página, você precisa criar um PreferenceController
e mover o código para o controlador antes de instanciá-lo no novo DashboardFragment
. As APIs que PreferenceController
requer são descritas em seu nome e documentadas em Javadoc.
É altamente recomendável adicionar um teste de unidade para cada PreferenceController
. Se a alteração for submetida ao AOSP, será necessário um teste de unidade. Para obter mais informações sobre como escrever testes baseados em Robolectric, consulte o arquivo leia-me packages/apps/Settings/tests/robotests/README.md
.
Arquitetura de informações no estilo de plug-in
Cada item de configuração é implementado como uma preferência. Uma preferência pode ser facilmente movida de uma página para outra.
Para facilitar a movimentação de várias configurações, o Android 8.0 introduziu um fragmento de host no estilo de plug-in que contém itens de configuração. Os itens de configurações são modelados como controladores de estilo de plug-in. Portanto, uma página de configurações é construída por um único fragmento de host e vários controladores de configuração.
DashboardFragment
DashboardFragment
é o host de controladores de preferência de estilo de plug-in. O fragmento herda de PreferenceFragment
e tem ganchos para expandir e atualizar listas de preferências estáticas e listas de preferências dinâmicas.
Preferências estáticas
Uma lista de preferências estáticas é definida em XML usando a tag <Preference>
. Uma implementação de DashboardFragment
usa o método getPreferenceScreenResId()
para definir qual arquivo XML contém a lista estática de preferências a serem exibidas.
Preferências dinâmicas
Um item dinâmico representa um bloco com intenção, levando a uma atividade externa ou interna. Normalmente, a intenção leva a uma página de configuração diferente. Por exemplo, o item de configuração "Google" na página inicial de Configurações é um item dinâmico. Os itens dinâmicos são definidos no AndroidManifest
(discutido abaixo) e carregados por meio de um FeatureProvider
(definido como DashboardFeatureProvider
).
As configurações dinâmicas são mais pesadas do que as configurações configuradas estaticamente, portanto, normalmente os desenvolvedores devem implementar a configuração como estática. No entanto, a configuração dinâmica pode ser útil quando qualquer uma das seguintes condições for verdadeira:
- A configuração não é implementada diretamente no aplicativo Configurações (como injetar uma configuração implementada por aplicativos OEM/Carrier).
- A configuração deve aparecer na página inicial de Configurações.
- Você já tem uma Activity para a configuração e não deseja implementar a configuração estática extra.
Para definir uma atividade como uma configuração dinâmica, faça o seguinte:
- Marque a atividade como uma configuração dinâmica adicionando um filtro de intenção à atividade.
- Diga ao aplicativo Configurações a qual categoria ele pertence. A categoria é uma constante, definida em
CategoryKey
. - Opcional: adicione texto de resumo quando a configuração for exibida.
Aqui está um exemplo retirado do aplicativo Configurações para DisplaySettings
.
<activity android:name="Settings$DisplaySettingsActivity" android:label="@string/display_settings" android:icon="@drawable/ic_settings_display"> <!-- Mark the activity as a dynamic setting --> <intent-filter> <action android:name="com.android.settings.action.IA_SETTINGS" /> </intent-filter> <!-- Tell Settings app which category it belongs to --> <meta-data android:name="com.android.settings.category" android:value="com.android.settings.category.ia.homepage" /> <!-- Add a summary text when the setting is displayed --> <meta-data android:name="com.android.settings.summary" android:resource="@string/display_dashboard_summary"/> </activity>
No momento da renderização, o fragmento solicitará uma lista de preferências do XML estático e das configurações dinâmicas definidas em AndroidManifest
. Independentemente de os PreferenceController
serem definidos em código Java ou em XML, o DashboardFragment
gerencia a lógica de manipulação de cada configuração por meio do PreferenceController
(discutido abaixo). Em seguida, eles são exibidos na interface do usuário como uma lista mista.
Controlador de Preferências
Existem diferenças entre implementar PreferenceController
no Android 9 e Android 8.x, conforme descrito nesta seção.
PreferenceController na versão Android 9
Um PreferenceController
contém toda a lógica para interagir com a preferência, incluindo exibição, atualização, indexação de pesquisa, etc.
A interface de PreferenceController
é definida como BasePreferenceController
. Por exemplo, veja o código em packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java
Existem várias subclasses de BasePreferenceController
, cada uma mapeando para um estilo de interface do usuário específico que o aplicativo Configurações oferece suporte por padrão. Por exemplo, TogglePreferenceController
tem uma API que mapeia diretamente como o usuário deve interagir com uma IU de preferência baseada em alternância.
BasePreferenceController
tem APIs como getAvailabilityStatus()
, displayPreference()
, handlePreferenceTreeClicked(),
etc. A documentação detalhada de cada API está na classe de interface.
Uma restrição na implementação de BasePreferenceController
(e suas subclasses como TogglePreferenceController
) é que a assinatura do construtor deve corresponder a um dos seguintes:
-
public MyController(Context context, String key) {}
-
public MyController(Context context) {}
Ao instalar uma preferência no fragmento, o painel fornece um método para anexar um PreferenceController
antes do tempo de exibição. No momento da instalação, o controlador é conectado ao fragmento para que todos os eventos relevantes futuros sejam enviados ao controlador.
DashboardFragment
mantém uma lista de PreferenceController
na tela. No onCreate()
do fragmento, todos os controladores são invocados para o método getAvailabilityStatus()
e, se retornar true, displayPreference()
é invocado para processar a lógica de exibição. getAvailabilityStatus()
também é importante para informar à estrutura de Configurações quais itens estão disponíveis durante a pesquisa.PreferenceController em versões do Android 8.x
Um PreferenceController
contém toda a lógica para interagir com a preferência, incluindo exibição, atualização, indexação de pesquisa. etc.
Correspondendo às interações de preferência, a interface do PreferenceController
possui as APIs isAvailable()
, displayPreference()
, handlePreferenceTreeClicked()
etc. A documentação detalhada sobre cada API pode ser encontrada na classe de interface.
Ao instalar uma preferência no fragmento, o painel fornece um método para anexar um PreferenceController
antes do tempo de exibição. No momento da instalação, o controlador é conectado ao fragmento para que todos os eventos relevantes futuros sejam enviados ao controlador.
DashboardFragment
mantém uma lista de PreferenceControllers
na tela. No onCreate()
do fragmento, todos os controladores são invocados para o método isAvailable()
e, se retornar true, displayPreference()
é invocado para processar a lógica de exibição.
Usando o DashboardFragment
Movendo uma preferência da página A para B
Se a preferência estiver listada estaticamente no arquivo XML de preferência da página original, siga o procedimento de movimentação estática para sua versão do Android abaixo. Caso contrário, siga o procedimento de movimentação dinâmica para sua versão do Android.
Movimento estático no Android 9
- Encontre os arquivos XML de preferência para a página original e a página de destino. Você pode encontrar essas informações no método
getPreferenceScreenResId()
da página. - Remova a preferência do XML da página original.
- Adicione a preferência ao XML da página de destino.
- Remova o
PreferenceController
dessa preferência da implementação Java da página original. Geralmente está emcreatePreferenceControllers()
. O controlador pode ser declarado em XML diretamente.Nota : A preferência pode não ter um
PreferenceController
. - Instancie o
PreferenceController
nocreatePreferenceControllers()
da página de destino. Se oPreferenceController
estiver definido em XML na página antiga, defina-o também em XML para a nova página.
Movimento dinâmico no Android 9
- Encontre qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em
DashboardFragmentRegistry
. - Abra o arquivo
AndroidManifest.xml
que contém a configuração que você precisa mover e localize a entrada Atividade que representa essa configuração. - Defina o valor de metadados da atividade para
com.android.settings.category
para a chave de categoria da nova página.
Movimento estático nas versões do Android 8.x
- Encontre os arquivos XML de preferência para a página original e a página de destino. Você pode encontrar essas informações no método
- Remova a preferência no XML da página original.
- Adicione a preferência ao XML da página de destino.
- Remova o
PreferenceController
para esta preferência na implementação Java da página original. Normalmente está emgetPreferenceControllers()
. - Instancie o
PreferenceController
nogetPreferenceControllers()
da página de destino.
getPreferenceScreenResId()
da página. Nota : É possível que a preferência não tenha um PreferenceController
.
Mudança dinâmica nas versões do Android 8.x
- Encontre qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em
DashboardFragmentRegistry
. - Abra o arquivo
AndroidManifest.xml
que contém a configuração que você precisa mover e localize a entrada Atividade que representa essa configuração. - Altere o valor de metadados da atividade para
com.android.settings.category
, defina o ponto de valor para a chave de categoria da nova página.
Criando uma nova preferência em uma página
Se a preferência estiver listada estaticamente no arquivo XML de preferência da página original, siga o procedimento estático abaixo. Caso contrário, siga o procedimento dinâmico .
Criando uma preferência estática
- Encontre os arquivos XML de preferência para a página. Você pode encontrar essas informações no método getPreferenceScreenResId() da página.
- Adicione um novo item de preferência no XML. Certifique-se de que ele tenha um
android:key
exclusivo. - Defina um
PreferenceController
para esta preferência no métodogetPreferenceControllers()
da página.- No Android 8.xe opcionalmente no Android 9, instancie um
PreferenceController
para essa preferência no métodocreatePreferenceControllers()
da página.Se essa preferência já existia em outros lugares, é possível que já exista um
PreferenceController
para ela. Você pode reutilizar oPreferenceController
sem criar um novo. - A partir do Android 9, você pode optar por declarar o
PreferenceController
em XML ao lado da preferência. Por exemplo:<Preference android:key="reset_dashboard" android:title="@string/reset_dashboard_title" settings:controller="com.android.settings.system.ResetPreferenceController"/>
- No Android 8.xe opcionalmente no Android 9, instancie um
Criando uma preferência dinâmica
- Encontre qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em
DashboardFragmentRegistry
. - Crie uma nova atividade no
AndroidManifest
- Adicione os metadados necessários à nova atividade para definir a configuração. Defina o valor de metadados para
com.android.settings.category
com o mesmo valor definido na etapa 1.
Criar uma nova página
- Crie um novo fragmento, herdando de
DashboardFragment
. - Defina sua categoria em
DashboardFragmentRegistry
.Nota: Esta etapa é opcional. Se você não precisar de preferências dinâmicas nesta página, não precisará fornecer uma chave de categoria.
- Siga as etapas para adicionar as configurações necessárias para esta página. Para obter mais informações, consulte a seção Implementação .
Validação
- Execute os testes robolectric em Configurações. Todos os testes existentes e novos devem passar.
- Crie e instale as configurações e abra manualmente a página que está sendo modificada. A página deve ser atualizada imediatamente.