O que usar em vez de "addPreferencesFromResource" em uma PreferenceActivity?

360

Acabei de notar o fato de que o método addPreferencesFromResource(int preferencesResId)está marcado como obsoleto na documentação do Android ( entrada de referência ).

Infelizmente, nenhum método alternativo é fornecido na descrição do método.

Qual método deve ser usado para conectar um preferênciaScreen.xml à PreferenceActivity correspondente?

mweisz
fonte
2
Uma abordagem muito direta é fornecida por WannaGetHigh em stackoverflow.com/questions/23523806/…
Sabin
A solução ainda existe addPreferencesFromResource(int preferencesResId). Estou esquecendo de algo ?
Jammo
@ Jammo Sim, mas foi movido da Atividade para o Fragmento para refletir a nova maneira de fazê-lo -> Fragmento.
WannaGetHigh

Respostas:

332

Nenhum método alternativo é fornecido na descrição do método porque a abordagem preferida (no nível 11 da API) é instanciar objetos PreferenceFragment para carregar suas preferências em um arquivo de recurso. Veja o código de exemplo aqui: PreferenceActivity

glorifiedHacker
fonte
Muito obrigado pela sua resposta. Eu estava apenas seguindo as instruções obsoletas do "Video2Brain-Android Development", o que me levou a usar a versão do método PreferenceActivity. Btw: Eu adoraria classificar sua resposta como útil se eu pudesse.
Mweisz
33
Agora somente se o PreferenceFragment foi incluído no pacote de compatibilidade não faria sentido para usá-lo stackoverflow.com/questions/5501431/...
Christoff
11
Desde que eu uso o Action Bar Sherlock, segui o seguinte blog para resolver esse problema, consulte ... commonsware.com/blog/2012/10/16/…
Alguém em algum lugar
2
Ainda assim, você precisa chamar addPreferencesFromResource (int PreferencesID) se quiser que o aplicativo seja compatível com versões anteriores com nível de API anterior a 11 (Android 3.0). Mas acho que você também pode considerar esses dispositivos antigos obsoletos.
Einar Sundgren
5
@EinarSundgren Eu sou o proprietário do Nexus One e minha versão máxima disponível é 2.2: você não vai me parar! Eu nunca serei preterido! Pelo poder de Grayskull ... Eu tenho o poder!
TechNyquist
186

Para adicionar mais informações à resposta correta acima, depois de ler um exemplo do Android-er , descobri que você pode converter facilmente sua atividade preferencial em um fragmento de preferência. Se você tem a seguinte atividade:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.my_preference_screen);
    }
}

As únicas alterações que você precisa fazer é criar uma classe de fragmento interno, mover addPreferencesFromResources()o fragmento para o fragmento e chamar o fragmento da atividade, assim:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    }

    public static class MyPreferenceFragment extends PreferenceFragment
    {
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.my_preference_screen);
        }
    }
}

Pode haver outras sutilezas para fazer preferências mais complexas a partir de fragmentos; se assim for, espero que alguém as anote aqui.

Garret Wilson
fonte
6
Bom, obrigado por isso. Java e este tipo de pensamento é tão distante do meu mundo pouco agradável .net :)
Tom
44
Por que eles depreciariam o addPreferencesFromResources()comércio pelo PreferenceFragment? Parece desnecessário do ponto de vista de iniciantes.
Howdy_McGee
4
Chamada exige nível API 11
Mehmet
11
e se a API estiver no nível 9? @mehmet
gumuruh
2
Resposta incrível! existe uma maneira de conseguir isso sem alterar o android.R.id.content? parece deselegante para mim por alguma razão ... (me corrigir se estou errado)
tomer.z
37

@ Garret Wilson Muito obrigado! Como um noob para a codificação do Android, eu fiquei preso com o problema de incompatibilidade de preferências por tantas horas, e acho tão decepcionante que eles tenham depreciado o uso de alguns métodos / abordagens para novos que não são suportados pelas APIs mais antigas. ter que recorrer a todos os tipos de soluções alternativas para fazer seu aplicativo funcionar em uma ampla variedade de dispositivos. É realmente frustrante!

Sua classe é ótima, pois permite que você continue trabalhando em novas APIs com preferências do jeito que costumava ser, mas não é compatível com versões anteriores. Como estou tentando alcançar uma ampla variedade de dispositivos, trabalhei um pouco com ele para fazê-lo funcionar em dispositivos anteriores à API 11 e em APIs mais recentes:

import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;

public class MyPrefsActivity extends PreferenceActivity
{
    private static int prefs=R.xml.myprefs;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        try {
            getClass().getMethod("getFragmentManager");
            AddResourceApi11AndGreater();
        } catch (NoSuchMethodException e) { //Api < 11
            AddResourceApiLessThan11();
        }
    }

    @SuppressWarnings("deprecation")
    protected void AddResourceApiLessThan11()
    {
        addPreferencesFromResource(prefs);
    }

    @TargetApi(11)
    protected void AddResourceApi11AndGreater()
    {
        getFragmentManager().beginTransaction().replace(android.R.id.content,
                new PF()).commit();
    }

    @TargetApi(11)
    public static class PF extends PreferenceFragment
    {       
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
            // private members seem to be visible for inner class, and
            // making it static made things so much easier
        }
    }
}

Testado em dois emuladores (2.2 e 4.2) com sucesso.

Por que meu código parece tão ruim?

Sou iniciante em codificação Android e não sou o maior fã de java.

Para evitar o aviso preterido e forçar o Eclipse a me permitir compilar, tive que recorrer a anotações, mas elas parecem afetar apenas classes ou métodos; portanto, tive que mover o código para dois novos métodos para tirar vantagem disso.

Eu não gostaria de escrever meu ID de recurso xml duas vezes sempre que copiar e colar a classe para uma nova PreferenceActivity, então criei uma nova variável para armazenar esse valor.

Espero que isso seja útil para outra pessoa.

PS: Desculpe pelas minhas opiniões opinativas, mas quando você se torna novo e encontra essas desvantagens, não pode evitar ficar frustrado!

ecv
fonte
Oh, bem ... Acabei de notar que precisaria alterar o nome da classe externa duas vezes sempre que copiar e colar. Na verdade, há uma maneira de evitar isso, passando prefs para a classe interna. Você não pode criar um construtor de classe interno que aceite prefs como parâmetro, pois aparentemente não é recomendado para classes derivadas de PreferenceFragment. Além disso, você não pode criar um método na classe interna para obter prefs e chamar addPreferencesFromResource imediatamente, pois addPreferencesFromResource precisa ser chamado depois que super.onCreate foi chamado e onCreate não é chamado logo após a classe derivada PreferenceFragment ...
ECV
... foi instanciado. Portanto, você precisa criar uma nova variável na classe interna, criar um método de conjunto público para ela na classe interna, deixar o addPreferencesFromResource onde está após a chamada para super.onCreate, onCreate, instanciar a classe interna, definir os prefs e use-o na chamada para getFragmentManager () ... como antes.
ECV
2
Seu código foi um salva-vidas !! Eu estava tentando segmentar o telefone antigo e o novo e perdi 3 dias .. E a resposta é muito simples. Obrigado
Devdatta Tengshe
22

Minha abordagem está muito próxima da de Garret Wilson (obrigado, votei em você;)

Além disso, fornece compatibilidade com o Android <3.

Acabei de reconhecer que minha solução é ainda mais próxima da de Kevin Remo . É apenas um pouquinho mais limpo (pois não depende do antipadrão de "expecção" ).

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            onCreatePreferenceActivity();
        } else {
            onCreatePreferenceFragment();
        }
    }

    /**
     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
     * < 11).
     */
    @SuppressWarnings("deprecation")
    private void onCreatePreferenceActivity() {
        addPreferencesFromResource(R.xml.preferences);
    }

    /**
     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
     * 11).
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void onCreatePreferenceFragment() {
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new MyPreferenceFragment ())
                .commit();
    }
}

Para um exemplo "real" (mas mais complexo), consulte NusicPreferencesActivity e NusicPreferencesFragment .

schnatterer
fonte
@SuppressLint("NewApi")- evite - seja mais preciso. Você o executou para APIs baixas? Ele vai jogarVerifyError
Mr_and_Mrs_D
Testo o acima no emulador executando API nível 10, o APK foi criado usando o SDK versão 19. Qual versão do SDK você usou para criar? Em que nível da API do dispositivo você o executou? Você o executou no emulador ou em um dispositivo físico? Quando exatamente ocorreu o erro? Se Ur construindo com SDK <= 16, consulte esta resposta .
Schnatterer
Você está perdendo o objetivo - e adicione @Mr_and_Mrs_D se quiser que eu seja notificado. Para o VE, leia aqui: stackoverflow.com/questions/20271593/… . O @SuppressLint("NewApi")estilo é apenas ruim
Mr_and_Mrs_D
@M Bem, então, que tal uma proposta sobre como evitar @SuppressLint("NewApi")nesta situação específica?
Schnatterer
@TargetApi(Build.VERSION_CODES.HONEYCOMB)- nem todos os avisos para qualquer api :)
Mr_and_Mrs_D
6

Em vez de exceções, basta usar:

if (Build.VERSION.SDK_INT >= 11)

E use

@SuppressLint("NewApi")

suprimir os avisos.

Pedro
fonte
11
porque ele não faz nada além de falha: D
Ishtiaq 17/04
11
se estiver
travando
0

Em vez de usar um PreferenceActivitypara carregar diretamente as preferências, use um AppCompatActivityou equivalente que carregue umPreferenceFragmentCompat que carrega suas preferências. Faz parte da biblioteca de suporte (agora Android Jetpack) e fornece compatibilidade com a API 14.

No seu build.gradle, adicione uma dependência para a biblioteca de suporte de preferências:

dependencies {
    // ...
    implementation "androidx.preference:preference:1.0.0-alpha1"
}

Nota: Vamos assumir que você já tem suas preferências XML criadas.

Para sua atividade, crie uma nova classe de atividade. Se você estiver usando temas materiais, estenda um AppCompatActivity, mas poderá ser flexível com isso:

public class MyPreferencesActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_preferences_activity)
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, MyPreferencesFragment())
                    .commitNow()
        }
    }
}

Agora, a parte importante: crie um fragmento que carregue suas preferências do XML:

public class MyPreferencesFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
    }
}

Para obter mais informações, leia os documentos para desenvolvedores do Android PreferenceFragmentCompat.

Willie Chalmers III
fonte