O PreferenceFragment foi intencionalmente excluído do pacote de compatibilidade?

153

Estou procurando escrever preferências que possam ser aplicadas aos dispositivos 3.0 e pré-3.0. Descobrindo que PreferenceActivitycontém métodos obsoletos (embora estes sejam usados ​​no código de exemplo que acompanha), observei PreferenceFragemente o pacote de compatibilidade para resolver meus problemas.

Parece, porém, que PreferenceFragmentnão está no pacote de compatibilidade. Alguém pode me dizer se isso foi intencional? Em caso afirmativo, posso segmentar facilmente uma variedade de dispositivos (por exemplo, <3.0 e> = 3.0) ou terei que pular os bastidores? Se não foi intencionalmente excluído, podemos esperar um novo lançamento do pacote de compatibilidade? Ou existe outra solução alternativa que é segura de usar?

Felicidades

James

James
fonte
1
Esta é a minha abordagem para solucionar o problema: stackoverflow.com/questions/14076073/…
ecv
Alguém fez um terceiro PreferenceFragmentque você esquecerá que está lá. Veja minha resposta .
theblang
Chris Banes aborda isso em um comentário em seu blog . Ele disse que o motivo é"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Veja minha resposta atualizada . PreferenceFragmentCompatfoi adicionado recentemente à biblioteca de suporte.
theblang

Respostas:

90

Descobrindo que PreferenceActivity contém métodos obsoletos (embora sejam usados ​​no código de amostra que o acompanha)

Os métodos obsoletos estão obsoletos no Android 3.0. Eles estão perfeitamente bem em todas as versões do Android, mas a direção é usar PreferenceFragmentno Android 3.0 e superior.

Alguém pode me dizer se isso foi intencional?

Meu palpite é que é uma questão de tempo de engenharia, mas isso é apenas um palpite.

Em caso afirmativo, posso segmentar facilmente uma variedade de dispositivos (por exemplo, <3.0 e> = 3.0) ou terei que pular os bastidores?

Considero que seja feito "facilmente". Tenha duas PreferenceActivityimplementações separadas , uma usando cabeçalhos de preferência e PreferenceFragmentsa outra usando a abordagem original. Escolha o caminho certo no momento em que você precisa (por exemplo, quando o usuário clica no item de menu Opções). Aqui está um projeto de amostra demonstrando isso. Ou escolha um PreferenceActivityque lide com os dois casos, como neste projeto de exemplo .

Se não foi intencionalmente excluído, podemos esperar um novo lançamento do pacote de compatibilidade?

Você descobrirá quando o resto de nós descobrir, ou seja, se e quando ele será enviado.

Ou existe outra solução alternativa que é segura de usar?

Veja acima.

CommonsWare
fonte
Cheers Mark. Eu tinha visto que você fez comentários sobre isso em alguns lugares (grupo android do google e seu blog), mas queria uma resposta definitiva (na medida do possível, dadas as circunstâncias).
James
@ James: Sim, o problema estará na definição XML de preferência, obtendo algo que funcionará bem como fragmentos e também concatenado juntos, já que não tenho certeza se <include>funciona com XML preferencial. BTW, se você é um assinante, a atualização do livro referente a este projeto foi anunciada minutos atrás.
CommonsWare
7
Sinto muito, mas não tenho muita certeza do que você está tentando dizer aqui. Você não responde nada, apenas comenta / adivinha / se refere a links externos irrelevantes que não têm nada a ver com o problema. A questão é se a omissão foi intencional ou não, sem a versão de compatibilidade do PreferenceFragment, não há como estender o PreferenceActivity da maneira que você descreveu porque, se o PreferenceFragment não existir, getSupportFragmentManager () ou qualquer outro método necessário usar fragmentos em primeiro lugar.
23812 Justin Buser
8
@JustinBuser: "A questão é se a omissão foi intencional" - as únicas pessoas que podem responder a essa pergunta para o Google. Você pode encontrar um emprego no Google para descobrir. "não há como estender o PreferenceActivity da maneira que você descreveu" - você pode fazer o download do código ao qual vinculei.
CommonsWare
9
@ JustinBuser Para constar, Mark respondeu à minha pergunta. Isso é evidente por eu aceitar sua resposta.
James
21

A implicação sutil da resposta do @CommonsWare é a seguinte: seu aplicativo deve escolher entre a API de compatibilidade ou a API de fragmento interna (desde o SDK 11). De fato, foi o que a recomendação "fácil" fez. Em outras palavras, se você quiser usar o PreferenceFragment, seu aplicativo precisará usar a API de fragmento interna e lidar com os métodos descontinuados no PreferenceActivity. Por outro lado, se é importante que seu aplicativo use o compat. API com a qual você não terá uma classe PreferenceFragment. Portanto, os dispositivos de segmentação não são um problema, mas o salto em arco acontece quando você precisa escolher uma ou outra API e, assim, enviar seu design para soluções imprevistas. Eu preciso do compat. API, então eu vou criar minha própria classe PreferenceFragment e ver como isso funciona. No pior cenário, eu

EDIT: Depois de tentar e ver o código em http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - criar meu próprio PreferenceFragment não acontecerá. Parece que o uso liberal de package-private no PreferenceManager em vez de 'protected' é o principal bloqueador. Realmente não parece que haja alguma segurança ou motivação muito boa para fazer isso e não é ótimo para testes de unidade, mas tudo bem ... menos digitação, eu acho ...

EDIT v2: Na verdade, aconteceu e funcionou. Definitivamente, foi uma dor de cabeça fazer o código funcionar com o JAR da API de compatibilidade. Tive que copiar cerca de 70% do pacote com.android.preference do SDK para o meu aplicativo e depois lutar com o código Java de qualidade medíocre no Android. Eu usei a v14 do SDK. Teria sido muito mais fácil para um engenheiro da Goog fazer o que eu fiz, ao contrário do que ouvi alguns engenheiros líderes do Android dizerem sobre esse tópico.

BTW - eu disse "segmentar dispositivos não é um problema"? É totalmente ... se você usar com.android.preference, não poderá trocar com a API de compatibilidade sem grandes refatorações. Log divertido!

Persistente
fonte
Deixe-me ser mais direto. Se tudo o que você gosta é atingir o Honeycomb e superior (que tem quanta participação de mercado?), Vote na resposta da @Commonsware! Se você se preocupa com a maioria dos dispositivos Android no mercado hoje, leia minha resposta.
Tenacious
4
você gostaria de compartilhar como fez isso? Estou enfrentando exatamente o mesmo problema, apenas minha PreferenceActivity precisa usar Loaders e, portanto, devo usar a biblioteca de compatibilidade.
30912 Karakuri
3
@ Tenacious Eu gosto da sua investigação - muito bem. No entanto, acho que alguém deve esclarecer seu primeiro comentário lá - o código do Commonsware funcionará em dispositivos pré e pós HC - tente primeiro antes de fazer comentários como esse. O que você precisa entender é a ligação tardia usada no tempo de execução para oferecer suporte a dispositivos anteriores. A verificação da versão em tempo de execução cuida do suporte a ambas as famílias de sistemas operacionais - esse é um padrão comum do Android (não um que eu goste - mas que é importante para os desenvolvedores do Android aprenderem e se familiarizarem) ... não descarte nenhuma das abordagens.
Richard Le Mesurier
@RichardLeMesurier, mas o método do Commonsware não é adequado se você precisar de preferências dentro do DrawerLayout
neworld
16

Com base na resposta do CommonsWare e nas observações do Tenacious, criei uma solução de classe descendente capaz de direcionar todas as versões atuais da API Android com um mínimo de barulho e sem duplicação de código ou recurso. Por favor, veja minha resposta para a pergunta relacionada aqui: PreferenceActivity Android 4.0 e versões anteriores

ou no meu blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Testado em dois tablets executando 4.0.3 e 4.0.4, bem como em um telefone executando 4.0.4 e 2.3.3 e também em um emulador executando 1.6.

Uncle Code Monkey
fonte
10

Em agosto de 2015, o Google lançou a nova Biblioteca de Suporte de Preferências v7 .

Agora você pode usar o PreferenceFragmentCompat com qualquer ActivityouAppCompatActivity

public static class PrefsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

Você precisa definir o preferenceThemeseu tema:

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  ...
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

Dessa forma, você pode personalizar o preferenceThemeestilo dos layouts usados ​​para cada tipo de preferência sem afetar outras partes da sua Atividade.

Gabriele Mariotti
fonte
1
Eu acho que você perde uma função: onCreatePreferences
desenvolvedor Android
Isso salvou meu dia! Gostaria de saber por que isso não é visível na Estrutura do projeto do Android Studio ... ?? BTW, você tem um erro de digitação no seu código. Deve ser "estende PreferenceFragmentCompat"
Grzegorz D.
7

A resposta de Tenacious está correta, mas aqui estão mais alguns detalhes.

O motivo pelo qual você não pode "criar um layout normal e vincular os componentes de exibição aos compartilhados manualmente" é que existem algumas omissões surpreendentes na API android.preferences. PreferenceActivity e PreferenceFragment têm acesso a métodos PreferenceManager não públicos críticos, sem os quais você não pode implementar uma interface de usuário de preferência.

Em particular, para construir uma hierarquia de preferências a partir de um arquivo XML, você precisa usar um PreferenceManager, mas todos os construtores do PreferenceManager são privados ou ocultos por pacotes. O método de anexar os ouvintes do Preference onClick à sua atividade também é privado do pacote.

E você não pode contornar isso colocando sua implementação sorrateiramente no pacote android.preferences, porque métodos não públicos nas APIs do Android são realmente omitidas no SDK. Com um pouco de criatividade envolvendo reflexão e proxies dinâmicos, você ainda pode alcançá-los. A única alternativa, como diz Tenacious, é dividir o pacote android.preference inteiro, incluindo pelo menos 15 classes, 5 layouts e um número semelhante de elementos style.xml e attrs.xml.

Portanto, para responder à pergunta original, o motivo pelo qual o Google não incluiu o PreferenceFragment no pacote de compatibilidade é que eles teriam exatamente a mesma dificuldade que o Tenacious e eu. Mesmo o Google não pode voltar no tempo e tornar esses métodos públicos nas plataformas antigas (embora eu espero que eles façam isso em versões futuras).

mhsmith
fonte
2

Meu destino de aplicativo é a API +14, mas devido ao uso da biblioteca de suporte para uma navegação sofisticada, eu não podia usar o android.app.Fragmente precisava usá-lo android.support.v4.app.Fragment, mas também precisava PreferenceFragmentimplementá-lo sem grandes alterações no código.

Portanto, minha solução fácil para ter os dois mundos da biblioteca de suporte e PreferenceFragment:

private android.support.v4.app.Fragment fragment;
private android.app.Fragment nativeFragment = null;

private void selectItem(int position) {
    fragment = null;
    boolean useNativeFragment = false;
    switch (position) {
    case 0:
        fragment = new SampleSupprtFragment1();
        break;
    case 1:
        fragment = new SampleSupprtFragment2();
        break;
    case 2:
        nativeFragment = new SettingsFragment();
        useNativeFragment = true;
        break;
    }
    if (useNativeFragment) {
        android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, nativeFragment).commit();
    } else {
        if (nativeFragment != null) {
            getFragmentManager().beginTransaction().remove(nativeFragment)
                .commit();
            nativeFragment = null;
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment).commit();
    }
}
Mohsen Afshin
fonte
2

Eu precisava integrar Preferências no design do aplicativo e manter o suporte para o Android 2.3. Então, eu ainda precisava do PreferencesFragment.

Após alguma pesquisa, encontrei a biblioteca android-support-v4-preferênciafragment . Essa lib economiza muito tempo para copiar e refatorar o PreferencesFragment original, como disse Tenacious. Funciona bem e os usuários desfrutam de preferências.

neworld
fonte